cowork-os 0.3.71 → 0.3.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +62 -2
  3. package/dist/daemon/daemon/control-plane-methods.js +8 -8
  4. package/dist/daemon/electron/agent/browser/browser-service.js +43 -1
  5. package/dist/daemon/electron/agent/custom-skill-loader.js +78 -5
  6. package/dist/daemon/electron/agent/daemon.js +52 -2
  7. package/dist/daemon/electron/agent/executor.js +891 -126
  8. package/dist/daemon/electron/agent/llm/pricing.js +4 -0
  9. package/dist/daemon/electron/agent/llm/types.js +4 -4
  10. package/dist/daemon/electron/agent/search/provider-factory.js +65 -35
  11. package/dist/daemon/electron/agent/skills/organizer.js +1 -0
  12. package/dist/daemon/electron/agent/tools/browser-tools.js +6 -4
  13. package/dist/daemon/electron/agent/tools/cron-tools.js +1 -1
  14. package/dist/daemon/electron/agent/tools/file-tools.js +35 -8
  15. package/dist/daemon/electron/agent/tools/glob-tools.js +45 -7
  16. package/dist/daemon/electron/agent/tools/grep-tools.js +1 -1
  17. package/dist/daemon/electron/agent/tools/registry.js +77 -6
  18. package/dist/daemon/electron/agent/tools/search-tools.js +14 -3
  19. package/dist/daemon/electron/agent/tools/x-browser-scripts.js +426 -0
  20. package/dist/daemon/electron/agent/tools/x-tools.js +995 -15
  21. package/dist/daemon/electron/agents/agent-dispatch.js +8 -34
  22. package/dist/daemon/electron/agents/role-persona.js +221 -0
  23. package/dist/daemon/electron/control-plane/llm-configure.js +198 -0
  24. package/dist/daemon/electron/control-plane/protocol.js +1 -0
  25. package/dist/daemon/electron/control-plane/web-ui.js +174 -3
  26. package/dist/daemon/electron/database/repositories.js +4 -2
  27. package/dist/daemon/electron/database/schema.js +8 -7
  28. package/dist/daemon/electron/gateway/index.js +3 -0
  29. package/dist/daemon/electron/gateway/router.js +119 -71
  30. package/dist/daemon/electron/utils/pdf-parser.js +51 -0
  31. package/dist/daemon/electron/utils/pptx-extractor.js +461 -0
  32. package/dist/daemon/electron/utils/temp-workspace.js +211 -0
  33. package/dist/daemon/shared/types.js +9 -1
  34. package/dist/electron/electron/agent/browser/browser-service.js +43 -1
  35. package/dist/electron/electron/agent/custom-skill-loader.js +78 -5
  36. package/dist/electron/electron/agent/daemon.js +52 -2
  37. package/dist/electron/electron/agent/executor.js +891 -126
  38. package/dist/electron/electron/agent/llm/pricing.js +4 -0
  39. package/dist/electron/electron/agent/llm/types.js +4 -4
  40. package/dist/electron/electron/agent/search/provider-factory.js +65 -35
  41. package/dist/electron/electron/agent/skills/organizer.js +1 -0
  42. package/dist/electron/electron/agent/tools/browser-tools.js +6 -4
  43. package/dist/electron/electron/agent/tools/cron-tools.js +1 -1
  44. package/dist/electron/electron/agent/tools/file-tools.js +35 -8
  45. package/dist/electron/electron/agent/tools/glob-tools.js +45 -7
  46. package/dist/electron/electron/agent/tools/grep-tools.js +1 -1
  47. package/dist/electron/electron/agent/tools/registry.js +77 -6
  48. package/dist/electron/electron/agent/tools/search-tools.js +14 -3
  49. package/dist/electron/electron/agent/tools/x-browser-scripts.js +426 -0
  50. package/dist/electron/electron/agent/tools/x-tools.js +995 -15
  51. package/dist/electron/electron/agents/HeartbeatService.js +285 -19
  52. package/dist/electron/electron/agents/agent-dispatch.js +8 -34
  53. package/dist/electron/electron/agents/role-persona.js +221 -0
  54. package/dist/electron/electron/control-plane/handlers.js +18 -12
  55. package/dist/electron/electron/control-plane/llm-configure.js +198 -0
  56. package/dist/electron/electron/control-plane/protocol.js +1 -0
  57. package/dist/electron/electron/control-plane/web-ui.js +174 -3
  58. package/dist/electron/electron/database/repositories.js +4 -2
  59. package/dist/electron/electron/database/schema.js +8 -7
  60. package/dist/electron/electron/gateway/index.js +3 -0
  61. package/dist/electron/electron/gateway/router.js +119 -71
  62. package/dist/electron/electron/ipc/handlers.js +198 -75
  63. package/dist/electron/electron/ipc/image-viewer-ocr.js +93 -0
  64. package/dist/electron/electron/main.js +81 -18
  65. package/dist/electron/electron/preload.js +2 -2
  66. package/dist/electron/electron/tray/TrayManager.js +62 -47
  67. package/dist/electron/electron/utils/pdf-parser.js +51 -0
  68. package/dist/electron/electron/utils/pptx-extractor.js +461 -0
  69. package/dist/electron/electron/utils/temp-workspace.js +211 -0
  70. package/dist/electron/electron/utils/validation.js +32 -17
  71. package/dist/electron/shared/types.js +9 -1
  72. package/dist/renderer/assets/{index-Xvm2AXZV.js → index-DGA4L_Zx.js} +88 -75
  73. package/dist/renderer/index.html +1 -1
  74. package/docs/ARCHITECTURE.md +3 -1
  75. package/docs/node-daemon.md +1 -1
  76. package/docs/self-hosting.md +1 -1
  77. package/docs/vps-linux.md +30 -1
  78. package/node_modules/libsignal/.eslintrc.json +31 -0
  79. package/node_modules/libsignal/.npmignore +3 -0
  80. package/node_modules/libsignal/generate-proto.sh +1 -0
  81. package/node_modules/libsignal/protos/WhisperTextProtocol.proto +28 -0
  82. package/package.json +6 -5
  83. package/resources/skills/1password.json +10 -2
  84. package/resources/skills/add-documentation.json +13 -2
  85. package/resources/skills/agentic-image-loop.json +9 -1
  86. package/resources/skills/analyze-csv.json +9 -1
  87. package/resources/skills/apple-notes.json +10 -2
  88. package/resources/skills/apple-reminders.json +10 -2
  89. package/resources/skills/auto-commenter.json +9 -1
  90. package/resources/skills/bear-notes.json +10 -2
  91. package/resources/skills/bird.json +23 -5
  92. package/resources/skills/blogwatcher.json +10 -2
  93. package/resources/skills/blucli.json +10 -2
  94. package/resources/skills/bluebubbles.json +10 -2
  95. package/resources/skills/camsnap.json +10 -2
  96. package/resources/skills/clean-imports.json +9 -1
  97. package/resources/skills/code-review.json +13 -2
  98. package/resources/skills/code-reviewer.json +6 -0
  99. package/resources/skills/coding-agent.json +10 -2
  100. package/resources/skills/compare-files.json +9 -1
  101. package/resources/skills/convert-code.json +9 -1
  102. package/resources/skills/create-changelog.json +9 -1
  103. package/resources/skills/debug-error.json +13 -2
  104. package/resources/skills/dependency-check.json +9 -1
  105. package/resources/skills/developer-growth-analysis.json +7 -1
  106. package/resources/skills/discord.json +10 -2
  107. package/resources/skills/eightctl.json +10 -2
  108. package/resources/skills/explain-code.json +9 -1
  109. package/resources/skills/extract-todos.json +9 -1
  110. package/resources/skills/food-order.json +10 -2
  111. package/resources/skills/frontend-design.json +7 -1
  112. package/resources/skills/gemini.json +10 -2
  113. package/resources/skills/generate-readme.json +13 -2
  114. package/resources/skills/gifgrep.json +10 -2
  115. package/resources/skills/git-commit.json +9 -1
  116. package/resources/skills/github.json +10 -2
  117. package/resources/skills/gog.json +11 -1
  118. package/resources/skills/goplaces.json +10 -2
  119. package/resources/skills/himalaya.json +11 -1
  120. package/resources/skills/imsg.json +10 -2
  121. package/resources/skills/karpathy-guidelines.json +9 -1
  122. package/resources/skills/last30days.json +19 -2
  123. package/resources/skills/local-places.json +10 -2
  124. package/resources/skills/local-websearch.json +7 -1
  125. package/resources/skills/mcporter.json +10 -2
  126. package/resources/skills/memory-kit.json +9 -1
  127. package/resources/skills/model-usage.json +10 -2
  128. package/resources/skills/multi-pr-review.json +7 -1
  129. package/resources/skills/nano-pdf.json +10 -2
  130. package/resources/skills/notion.json +10 -2
  131. package/resources/skills/obsidian.json +10 -2
  132. package/resources/skills/openai-image-gen.json +11 -3
  133. package/resources/skills/openai-whisper-api.json +10 -2
  134. package/resources/skills/openai-whisper.json +10 -2
  135. package/resources/skills/openhue.json +10 -2
  136. package/resources/skills/oracle.json +10 -2
  137. package/resources/skills/ordercli.json +10 -2
  138. package/resources/skills/peekaboo.json +10 -2
  139. package/resources/skills/prd.json +7 -1
  140. package/resources/skills/project-structure.json +9 -1
  141. package/resources/skills/proofread.json +9 -1
  142. package/resources/skills/react-native-skills.json +7 -1
  143. package/resources/skills/refactor-code.json +9 -1
  144. package/resources/skills/rename-symbol.json +9 -1
  145. package/resources/skills/sag.json +10 -2
  146. package/resources/skills/security-audit.json +9 -1
  147. package/resources/skills/session-logs.json +9 -1
  148. package/resources/skills/sherpa-onnx-tts.json +10 -2
  149. package/resources/skills/skill-creator.json +12 -2
  150. package/resources/skills/skill-hub.json +22 -4
  151. package/resources/skills/slack.json +10 -2
  152. package/resources/skills/songsee.json +10 -2
  153. package/resources/skills/sonoscli.json +10 -2
  154. package/resources/skills/spotify-player.json +10 -2
  155. package/resources/skills/startup-cfo.json +9 -1
  156. package/resources/skills/summarize-folder.json +13 -2
  157. package/resources/skills/summarize.json +18 -2
  158. package/resources/skills/supabase-sdk-patterns.json +7 -1
  159. package/resources/skills/things-mac.json +10 -2
  160. package/resources/skills/tmux.json +10 -2
  161. package/resources/skills/translate.json +13 -2
  162. package/resources/skills/trello.json +10 -2
  163. package/resources/skills/usecase-booking-options.json +9 -1
  164. package/resources/skills/usecase-draft-reply.json +21 -3
  165. package/resources/skills/usecase-family-digest.json +19 -3
  166. package/resources/skills/usecase-household-capture.json +9 -1
  167. package/resources/skills/usecase-newsletter-digest.json +17 -3
  168. package/resources/skills/usecase-transaction-scan.json +14 -2
  169. package/resources/skills/video-frames.json +10 -2
  170. package/resources/skills/voice-call.json +9 -1
  171. package/resources/skills/wacli.json +10 -2
  172. package/resources/skills/weather.json +10 -2
  173. package/resources/skills/write-tests.json +13 -2
  174. package/scripts/qa/validate-skills-routing.mjs +128 -0
  175. package/src/daemon/control-plane-methods.ts +9 -8
  176. package/src/electron/agent/__tests__/auto-commenter-skill.test.ts +1 -0
  177. package/src/electron/agent/__tests__/custom-skill-loader.test.ts +38 -0
  178. package/src/electron/agent/__tests__/executor-completion-contract.test.ts +201 -0
  179. package/src/electron/agent/__tests__/executor-step-failures.test.ts +355 -2
  180. package/src/electron/agent/__tests__/executor-workspace-classification.test.ts +42 -0
  181. package/src/electron/agent/__tests__/executor-workspace-preflight-ack.test.ts +95 -14
  182. package/src/electron/agent/__tests__/memory-kit-skill.test.ts +1 -0
  183. package/src/electron/agent/browser/browser-service.ts +56 -2
  184. package/src/electron/agent/custom-skill-loader.ts +97 -5
  185. package/src/electron/agent/daemon.ts +58 -3
  186. package/src/electron/agent/executor.ts +1022 -133
  187. package/src/electron/agent/llm/pricing.ts +4 -0
  188. package/src/electron/agent/llm/types.ts +4 -4
  189. package/src/electron/agent/search/__tests__/provider-factory.test.ts +150 -0
  190. package/src/electron/agent/search/provider-factory.ts +79 -38
  191. package/src/electron/agent/search/types.ts +4 -0
  192. package/src/electron/agent/skills/organizer.ts +1 -0
  193. package/src/electron/agent/tools/__tests__/search-tools.test.ts +5 -0
  194. package/src/electron/agent/tools/__tests__/use-skill.test.ts +19 -0
  195. package/src/electron/agent/tools/browser-tools.ts +8 -5
  196. package/src/electron/agent/tools/cron-tools.ts +2 -2
  197. package/src/electron/agent/tools/file-tools.ts +37 -12
  198. package/src/electron/agent/tools/glob-tools.ts +63 -9
  199. package/src/electron/agent/tools/grep-tools.ts +1 -1
  200. package/src/electron/agent/tools/registry.ts +84 -6
  201. package/src/electron/agent/tools/search-tools.ts +14 -3
  202. package/src/electron/agent/tools/x-browser-scripts.ts +445 -0
  203. package/src/electron/agent/tools/x-tools.ts +1120 -16
  204. package/src/electron/agents/HeartbeatService.ts +364 -19
  205. package/src/electron/agents/__tests__/HeartbeatService.test.ts +102 -1
  206. package/src/electron/agents/__tests__/agent-dispatch.test.ts +48 -0
  207. package/src/electron/agents/__tests__/role-persona.test.ts +137 -0
  208. package/src/electron/agents/agent-dispatch.ts +13 -29
  209. package/src/electron/agents/role-persona.ts +225 -0
  210. package/src/electron/control-plane/__tests__/protocol-new-methods.test.ts +1 -0
  211. package/src/electron/control-plane/handlers.ts +21 -13
  212. package/src/electron/control-plane/llm-configure.ts +234 -0
  213. package/src/electron/control-plane/protocol.ts +1 -0
  214. package/src/electron/control-plane/web-ui.ts +174 -3
  215. package/src/electron/database/repositories.ts +4 -2
  216. package/src/electron/database/schema.ts +8 -7
  217. package/src/electron/gateway/index.ts +3 -0
  218. package/src/electron/gateway/router.ts +132 -80
  219. package/src/electron/ipc/handlers.ts +284 -96
  220. package/src/electron/ipc/image-viewer-ocr.ts +113 -0
  221. package/src/electron/main.ts +83 -19
  222. package/src/electron/preload.ts +30 -5
  223. package/src/electron/tray/TrayManager.ts +73 -55
  224. package/src/electron/utils/__tests__/temp-workspace.test.ts +334 -0
  225. package/src/electron/utils/__tests__/validation.test.ts +9 -0
  226. package/src/electron/utils/pdf-parser.ts +88 -0
  227. package/src/electron/utils/pptx-extractor.ts +486 -0
  228. package/src/electron/utils/temp-workspace.ts +248 -0
  229. package/src/electron/utils/validation.ts +37 -17
  230. package/src/renderer/App.tsx +82 -19
  231. package/src/renderer/components/ActivityFeed.tsx +2 -2
  232. package/src/renderer/components/MainContent.tsx +173 -113
  233. package/src/renderer/components/MissionControlPanel.tsx +16 -13
  234. package/src/renderer/components/NotificationPanel.tsx +1 -8
  235. package/src/renderer/components/RightPanel.tsx +2 -2
  236. package/src/renderer/components/Sidebar.tsx +7 -1
  237. package/src/renderer/components/TaskTimeline.tsx +12 -6
  238. package/src/renderer/components/TaskView.tsx +15 -13
  239. package/src/renderer/components/XSettings.tsx +2 -0
  240. package/src/renderer/components/utils/attachment-content.ts +97 -0
  241. package/src/renderer/hooks/useOnboardingFlow.ts +0 -1
  242. package/src/renderer/utils/agentMessages.ts +1 -1
  243. package/src/shared/types.ts +36 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.73] - 2026-02-14
11
+
12
+ ### Fixed
13
+ - **Release pipeline fix**: included daemon TypeScript sources in shared ESLint targets so `npm run lint` runs instead of failing with parse errors before build/publish steps.
14
+ - **Workspace/task validation fix**: enforced `PersonalityId` validation for task agent configs to prevent runtime/inference mismatches during task creation.
15
+ - **CLI and release install tests alignment**: updated control-plane and skill validation tests to match current runtime behaviors and skill metadata output.
16
+ - **Workspace preflight reliability**: stabilized ambiguous temp-task auto-switch behavior when project-signals are present and tests now validate that behavior.
17
+
18
+ ## [0.3.72] - 2026-02-14
19
+
20
+ ### Added
21
+ - **Session workspace isolation and cleanup**: temp tasks now get session-scoped workspace IDs, dedicated temp directories, and automatic pruning by age + usage caps.
22
+ - **Autonomous task mode** in execution flow and control-plane/web-UI paths, with optional bypass of interactive approval prompts where explicitly enabled.
23
+ - **Companion-mode handling for short conversational prompts** to return concise check-in responses without running task pipeline when appropriate.
24
+ - **Search execution ordering** now prefers Brave when available and can safely fallback through configured providers automatically.
25
+ - **PDF parsing compatibility wrapper** with runtime-safe handling for both legacy and v2 parser module shapes.
26
+
27
+ ### Changed
28
+ - **Task completion validation tightened** with final-response contracts (required direct answers, artifact checks, verification evidence).
29
+ - **Stricter tool failure handling** for hard/unavailable/disallowed outcomes to prevent false completion without real progress.
30
+ - **Temporary workspace handling** now uses explicit session-aware IDs and filters temp workspaces from user-visible lists consistently.
31
+ - **Search and file tools** now enforce more bounded scanning behavior and clearer fallback behavior under high-load conditions.
32
+
33
+ ### Fixed
34
+ - **Watch/skip recommendation tasks** now block artifact tools and require direct recommendation output.
35
+ - **Intermittent approval/partial-task updates** reduced by normalizing auto-approved events in UI and task-stream handling.
36
+ - **Temp workspace lifecycle reliability** improved through scheduled pruning and safer restore/create paths.
37
+
10
38
  ## [0.3.69] - 2026-02-11
11
39
 
12
40
  ### Fixed
@@ -481,6 +509,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
481
509
 
482
510
  | Version | Date | Highlights |
483
511
  |---------|------|------------|
512
+ | 0.3.73 | 2026-02-14 | Release automation hardening and task/workspace validation fixes |
513
+ | 0.3.72 | 2026-02-14 | Session-based temp workspaces, autonomous execution mode, safer completion validation |
484
514
  | 0.3.29 | 2025-02-08 | Multi-provider image generation, visual annotation, local embeddings, verification UX |
485
515
  | 0.3.25 | 2025-02-05 | Google Workspace integration, gateway enhancements, agent retry logic |
486
516
  | 0.1.6 | 2025-01-25 | Discord bot integration with slash commands |
@@ -490,7 +520,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
490
520
  | 0.1.0 | 2025-01-24 | First public release with core features |
491
521
  | 0.0.1 | 2025-01-20 | Initial development setup |
492
522
 
493
- [Unreleased]: https://github.com/CoWork-OS/CoWork-OS/compare/v0.3.29...HEAD
523
+ [Unreleased]: https://github.com/CoWork-OS/CoWork-OS/compare/v0.3.73...HEAD
524
+ [0.3.73]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.3.73
525
+ [0.3.72]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.3.72
526
+ [0.3.71]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.3.71
494
527
  [0.3.29]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.3.29
495
528
  [0.3.25]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.3.25
496
529
  [0.1.6]: https://github.com/CoWork-OS/CoWork-OS/releases/tag/v0.1.6
package/README.md CHANGED
@@ -36,6 +36,13 @@ Your AI needs a secure home. CoWork OS provides the runtime, security layers, an
36
36
  | **Security-First** | 2800+ unit tests, configurable guardrails, approval workflows, gateway hardening |
37
37
  | **Local-First** | Your data stays on your machine. BYOK (Bring Your Own Key) |
38
38
 
39
+ ### What’s new in 0.3.73
40
+
41
+ - **Release and install reliability**: fixed publish-blocking type/lint regressions so npm desktop/client releases can be published reliably.
42
+ - **Task config validation**: stricter `personalityId` validation for task `agentConfig` avoids malformed IDs reaching task execution.
43
+ - **Control-plane/LLM error handling alignment**: tests and runtime error codes now match shared validation shapes for predictable client behavior.
44
+ - **Workspace auto-switch confidence**: improved ambiguous temp-task preflight handling when preferred workspace signals are available.
45
+
39
46
  > **Status**: macOS desktop app + headless/server mode (Linux/VPS). Cross-platform desktop support planned.
40
47
 
41
48
  ---
@@ -70,6 +77,16 @@ pkill -f '/cowork-os' || true
70
77
  npx cowork-os
71
78
  ```
72
79
 
80
+ #### Install reliability notes (macOS / low-memory environments)
81
+
82
+ - If install fails with `SIGKILL` during `node_modules/electron/install.js`, use a two-step install:
83
+ - `npm install --ignore-scripts cowork-os@latest --no-audit --no-fund`
84
+ - `npm run setup` (from the install directory) before launching the CLI
85
+ - For local package testing, use the same `--ignore-scripts` flow with the tarball:
86
+ - `npm init -y`
87
+ - `npm install --ignore-scripts /path/to/cowork-os-<version>.tgz`
88
+ - If you already have a global install, verify with `coworkd-node --version` and avoid launching without dependency setup on first run.
89
+
73
90
  You can also install globally and launch directly:
74
91
 
75
92
  ```bash
@@ -193,6 +210,7 @@ ssh -N -L 28789:127.0.0.1:18789 user@your-vps
193
210
 
194
211
  - `http://127.0.0.1:18789/` (or `http://127.0.0.1:28789/` if you used 28789)
195
212
  - Paste the token from step 1
213
+ - If you skipped `OPENAI_API_KEY`/`ANTHROPIC_API_KEY`, use the **LLM Setup** panel in this UI to configure provider credentials.
196
214
 
197
215
  For production/persistent setup and Docker/systemd options, use:
198
216
 
@@ -407,7 +425,6 @@ Configure in **Settings** > **Appearance**.
407
425
  ### Agent Capabilities
408
426
 
409
427
  - **Task-Based Workflow**: Multi-step execution with plan-execute-observe loops
410
- - **Goal Mode**: Define success criteria and auto-retry until verification passes
411
428
  - **Dynamic Re-Planning**: Agent can revise its plan mid-execution
412
429
  - **85+ Built-in Skills**: GitHub, Slack, Notion, Spotify, Apple Notes, and more
413
430
  - **Document Creation**: Excel, Word, PDF, PowerPoint with professional formatting
@@ -417,10 +434,13 @@ Configure in **Settings** > **Appearance**.
417
434
  - **Performance Reviews**: Score and review agent-role outcomes, with autonomy-level recommendations
418
435
  - **Voice Calls**: Outbound phone calls via ElevenLabs Agents (list agents, list numbers, initiate calls)
419
436
  - **Vision**: Analyze workspace images (screenshots, photos, diagrams) via `analyze_image` tool (OpenAI, Anthropic, or Gemini)
437
+ - **X Browser Fallback**: `x_action` automatically falls back to browser-mode read/write flows when Bird CLI is blocked (rate limits, auth challenges, access issues)
438
+ - **Attachment OCR (Optional)**: Extract image text in local file previews with Tesseract (`tesseract` binary must be installed and on `PATH`)
420
439
  - **Image Generation**: Create images via `generate_image` with multi-provider support (Gemini, OpenAI gpt-image-1/1.5/DALL-E, Azure OpenAI) and automatic provider selection
421
440
  - **Visual Annotation**: Iterative image refinement with the Visual Annotator — generate, annotate, refine, repeat until approved
422
441
  - **Email IMAP Access**: Direct IMAP mailbox access via `email_imap_unread` — check unread emails without needing Google Workspace
423
442
  - **Workspace Recency**: Workspaces ordered by last used time for quick access
443
+ - **Hook-Triggered Heartbeat Wakeups**: Agents can act on hook-based wake events as explicit check-in prompts
424
444
 
425
445
  ### Voice Mode
426
446
 
@@ -489,6 +509,40 @@ Notes:
489
509
 
490
510
  Configure in **Settings** > **Memory Hub**.
491
511
 
512
+ ### Role Profile Files (`.cowork/agents/`)
513
+
514
+ You can define per-role personality and operating guidelines directly in workspace files.
515
+ When a role runs, CoWork loads these first; if missing, it falls back to legacy role metadata.
516
+
517
+ Expected structure:
518
+
519
+ | File | Purpose |
520
+ |---|---|
521
+ | `.cowork/agents/<role-id>/SOUL.md` | Role personality, behavior style, and execution philosophy |
522
+ | `.cowork/agents/<role-id>/IDENTITY.md` | Role-specific identity and constraints |
523
+ | `.cowork/agents/<role-id>/RULES.md` | Operational rules, safety boundaries, communication defaults |
524
+
525
+ Role resolution checks these folder candidates in order:
526
+
527
+ - `<normalized role.name>` (keeps folder-safe letters, numbers, `_`, `-`, `.`)
528
+ - slugified variant of role name (ASCII-safe fallback)
529
+ - `<normalized role.displayName>` (same normalization)
530
+ - slugified variant of displayName
531
+ - `<normalized role.id>`
532
+ - slugified variant of id
533
+ - `default`
534
+
535
+ Only non-empty `.cowork/agents/...` files are used. If no files are found, CoWork uses existing DB role `soul` notes.
536
+
537
+ Tips:
538
+
539
+ - Start by cloning a profile from an existing role: copy one `*.md` file to a new role folder.
540
+ - Keep templates simple; the first 4k chars per file are injected into prompts.
541
+ - Keep sensitive values out of these files; `.cowork` content is sanitized before use.
542
+ - Quick scaffold:
543
+ - `mkdir -p .cowork/agents/<role-id> && printf '%s\n' "# SOUL.md" "..." > .cowork/agents/<role-id>/SOUL.md`
544
+ - If you are unsure about the folder name, place a `default` profile at `.cowork/agents/default/` and copy it to `.../<role-id>/`.
545
+
492
546
  ### Agent Teams
493
547
 
494
548
  Coordinate multiple agents working together on complex tasks with shared state.
@@ -547,6 +601,10 @@ Full Playwright integration:
547
601
  - Click, fill forms, type text, press keys
548
602
  - Extract page content, links, and form data
549
603
  - Scroll pages, wait for elements, execute JavaScript
604
+ - Browser automation supports `browser_channel`:
605
+ - `chromium` (default bundled Chromium)
606
+ - `chrome` (system Google Chrome)
607
+ - `brave` (system Brave or a path set in `BRAVE_PATH`)
550
608
 
551
609
  ### System Tools
552
610
 
@@ -1680,7 +1738,7 @@ Claude Code-style tools for developers.
1680
1738
  → grep pattern="TODO:" glob="*.ts"
1681
1739
  ```
1682
1740
 
1683
- **Smart Document Detection**: Automatically detects document-heavy workspaces (PDF/DOCX) and provides helpful guidance to use `read_file` instead, since grep only searches text files.
1741
+ **Smart Document Detection**: Automatically detects document-heavy workspaces (PDF/DOCX/PPTX) and provides helpful guidance to use `read_file` instead, since grep only searches text files.
1684
1742
 
1685
1743
  ### edit_file - Surgical Editing
1686
1744
 
@@ -2207,6 +2265,7 @@ Users must comply with their model provider's terms:
2207
2265
  - [x] Chat commands: `/schedule`, `/digest`, `/followups`, `/brief` across all gateway channels
2208
2266
  - [x] Inbound attachment persistence (channel messages save files to `.cowork/inbox/attachments/`)
2209
2267
  - [x] Cron template variables (`{{today}}`, `{{chat_messages}}`, etc.) and conditional delivery
2268
+ - [x] Brave browser channel support via `browser_navigate` (`browser_channel: "chrome" | "chromium" | "brave"`)
2210
2269
  - [x] Image generation via `generate_image` with multi-provider support (Gemini, OpenAI, Azure OpenAI)
2211
2270
  - [x] Visual annotation tools and Agentic Image Loop skill for iterative image refinement
2212
2271
  - [x] Inline image preview in task event timeline
@@ -2219,6 +2278,7 @@ Users must comply with their model provider's terms:
2219
2278
  - [x] Control plane web dashboard, approval API, and channel management API
2220
2279
  - [x] Docker and VPS deployment support (Dockerfiles, docker-compose, systemd units)
2221
2280
  - [x] Dedicated workspaces for scheduled cron jobs (auto-created from temp workspace)
2281
+ - [x] Hook wake requests for heartbeat processing, including queueing, coalescing, and immediate scheduling
2222
2282
 
2223
2283
  ### Planned
2224
2284
 
@@ -46,8 +46,8 @@ const protocol_1 = require("../electron/control-plane/protocol");
46
46
  const settings_1 = require("../electron/control-plane/settings");
47
47
  const types_1 = require("../shared/types");
48
48
  const repositories_1 = require("../electron/database/repositories");
49
- const llm_1 = require("../electron/agent/llm");
50
49
  const search_1 = require("../electron/agent/search");
50
+ const llm_configure_1 = require("../electron/control-plane/llm-configure");
51
51
  const runtime_mode_1 = require("../electron/utils/runtime-mode");
52
52
  const user_data_dir_1 = require("../electron/utils/user-data-dir");
53
53
  function requireScope(client, scope) {
@@ -844,6 +844,11 @@ function registerControlPlaneMethods(server, deps) {
844
844
  await channelGateway.removeChannel(channelId);
845
845
  return { ok: true };
846
846
  });
847
+ // LLM setup (headless-friendly credential/provider configuration).
848
+ server.registerMethod(protocol_1.Methods.LLM_CONFIGURE, async (client, params) => {
849
+ requireScope(client, 'admin');
850
+ return (0, llm_configure_1.configureLlmFromControlPlaneParams)(params);
851
+ });
847
852
  // Config/health (sanitized; no secrets).
848
853
  server.registerMethod(protocol_1.Methods.CONFIG_GET, async (client) => {
849
854
  requireScope(client, 'read');
@@ -863,12 +868,7 @@ function registerControlPlaneMethods(server, deps) {
863
868
  tasksByStatus[status] = safeCount;
864
869
  taskTotal += safeCount;
865
870
  }
866
- const llmStatus = llm_1.LLMProviderFactory.getConfigStatus();
867
- const llm = {
868
- currentProvider: llmStatus.currentProvider,
869
- currentModel: llmStatus.currentModel,
870
- providers: llmStatus.providers,
871
- };
871
+ const llm = (0, llm_configure_1.getControlPlaneLlmStatus)();
872
872
  const anyLlmConfigured = llm.providers.some((p) => p.configured);
873
873
  const currentProviderConfigured = llm.providers.find((p) => p.type === llm.currentProvider)?.configured || false;
874
874
  const searchStatus = search_1.SearchProviderFactory.getConfigStatus();
@@ -896,7 +896,7 @@ function registerControlPlaneMethods(server, deps) {
896
896
  warnings.push('No workspaces configured. Set COWORK_BOOTSTRAP_WORKSPACE_PATH on startup or create one via workspace.create.');
897
897
  }
898
898
  if (!anyLlmConfigured) {
899
- warnings.push('No LLM provider credentials configured. Set COWORK_IMPORT_ENV_SETTINGS=1 (or run with --import-env-settings) plus an API key (e.g. OPENAI_API_KEY), then restart.');
899
+ warnings.push('No LLM provider credentials configured. Configure one via Control Plane (LLM Setup / llm.configure), or use COWORK_IMPORT_ENV_SETTINGS=1 with provider env vars and restart.');
900
900
  }
901
901
  else if (!currentProviderConfigured) {
902
902
  warnings.push(`Selected LLM provider "${llm.currentProvider}" is not configured. Either switch provider or configure its credentials.`);
@@ -50,9 +50,42 @@ class BrowserService {
50
50
  this.options = {
51
51
  headless: options.headless ?? true,
52
52
  timeout: options.timeout ?? 30000,
53
- viewport: options.viewport ?? { width: 1280, height: 720 }
53
+ viewport: options.viewport ?? { width: 1280, height: 720 },
54
+ userDataDir: options.userDataDir,
55
+ channel: options.channel,
54
56
  };
55
57
  }
58
+ async resolveBraveExecutablePath() {
59
+ const envPath = process.env.BRAVE_PATH?.trim();
60
+ const candidates = [
61
+ envPath,
62
+ process.platform === 'darwin'
63
+ ? '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'
64
+ : undefined,
65
+ process.platform === 'linux' ? '/usr/bin/brave-browser' : undefined,
66
+ process.platform === 'linux' ? '/usr/bin/brave-browser-stable' : undefined,
67
+ process.platform === 'linux' ? '/snap/bin/brave' : undefined,
68
+ process.platform === 'win32' && process.env.LOCALAPPDATA
69
+ ? path.join(process.env.LOCALAPPDATA, 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
70
+ : undefined,
71
+ process.platform === 'win32'
72
+ ? 'C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe'
73
+ : undefined,
74
+ process.platform === 'win32'
75
+ ? 'C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe'
76
+ : undefined,
77
+ ].filter((value) => Boolean(value));
78
+ for (const candidate of candidates) {
79
+ try {
80
+ await fs.access(candidate);
81
+ return candidate;
82
+ }
83
+ catch {
84
+ // Keep scanning candidates.
85
+ }
86
+ }
87
+ return undefined;
88
+ }
56
89
  /**
57
90
  * Initialize the browser
58
91
  * Uses try-finally to ensure cleanup on errors
@@ -64,11 +97,19 @@ class BrowserService {
64
97
  let context = null;
65
98
  try {
66
99
  const channel = this.options.channel === 'chrome' ? 'chrome' : undefined;
100
+ const executablePath = this.options.channel === 'brave'
101
+ ? await this.resolveBraveExecutablePath()
102
+ : undefined;
103
+ if (this.options.channel === 'brave' && !executablePath) {
104
+ throw new Error('Brave browser was requested but no Brave executable was found. ' +
105
+ 'Install Brave or set BRAVE_PATH to the Brave binary path.');
106
+ }
67
107
  if (this.options.userDataDir) {
68
108
  await fs.mkdir(this.options.userDataDir, { recursive: true });
69
109
  context = await playwright_1.chromium.launchPersistentContext(this.options.userDataDir, {
70
110
  headless: this.options.headless,
71
111
  ...(channel ? { channel } : {}),
112
+ ...(executablePath ? { executablePath } : {}),
72
113
  viewport: this.options.viewport,
73
114
  });
74
115
  browser = context.browser();
@@ -77,6 +118,7 @@ class BrowserService {
77
118
  browser = await playwright_1.chromium.launch({
78
119
  headless: this.options.headless,
79
120
  ...(channel ? { channel } : {}),
121
+ ...(executablePath ? { executablePath } : {}),
80
122
  });
81
123
  context = await browser.newContext({
82
124
  viewport: this.options.viewport,
@@ -304,7 +304,8 @@ class CustomSkillLoader {
304
304
  * List skills that can be automatically invoked by the model
305
305
  * Excludes guidelines and skills with disableModelInvocation set
306
306
  */
307
- listModelInvocableSkills() {
307
+ listModelInvocableSkills(options = {}) {
308
+ const availableToolNames = options.availableToolNames;
308
309
  return this.listSkills().filter((skill) => {
309
310
  // Exclude guideline skills
310
311
  if (skill.type === 'guideline')
@@ -315,6 +316,22 @@ class CustomSkillLoader {
315
316
  // Exclude skills that explicitly disable model invocation
316
317
  if (skill.invocation?.disableModelInvocation === true)
317
318
  return false;
319
+ // If tool availability is provided, filter out skills that cannot run in this context.
320
+ if (availableToolNames) {
321
+ const skillRequires = (skill.requires || {});
322
+ const requiredTools = Array.isArray(skillRequires.tools)
323
+ ? skillRequires.tools.filter((tool) => typeof tool === 'string' && tool.trim().length > 0)
324
+ : [];
325
+ if (requiredTools.some((tool) => !availableToolNames.has(tool))) {
326
+ return false;
327
+ }
328
+ // Skills requiring external binaries generally need run_command access.
329
+ const hasBinaryRequirements = (Array.isArray(skill.requires?.bins) && skill.requires.bins.length > 0) ||
330
+ (Array.isArray(skill.requires?.anyBins) && skill.requires.anyBins.length > 0);
331
+ if (hasBinaryRequirements && !availableToolNames.has('run_command')) {
332
+ return false;
333
+ }
334
+ }
318
335
  return true;
319
336
  });
320
337
  }
@@ -322,8 +339,8 @@ class CustomSkillLoader {
322
339
  * Get formatted skill descriptions for the model's system prompt
323
340
  * Groups skills by category and includes parameter info
324
341
  */
325
- getSkillDescriptionsForModel() {
326
- const skills = this.listModelInvocableSkills();
342
+ getSkillDescriptionsForModel(options = {}) {
343
+ const skills = this.listModelInvocableSkills(options);
327
344
  if (skills.length === 0) {
328
345
  return '';
329
346
  }
@@ -345,10 +362,47 @@ class CustomSkillLoader {
345
362
  ? ` (params: ${skill.parameters.map(p => p.name + (p.required ? '*' : '')).join(', ')})`
346
363
  : '';
347
364
  lines.push(`- ${skill.id}: ${skill.description}${paramInfo}`);
365
+ const routingHints = this.getSkillRoutingHints(skill);
366
+ for (const hint of routingHints) {
367
+ lines.push(` ${hint}`);
368
+ }
348
369
  }
349
370
  }
350
371
  return lines.join('\n');
351
372
  }
373
+ /**
374
+ * Build compact routing and success hints for model prompt listing.
375
+ * These are intentionally short and should act like decision boundaries.
376
+ */
377
+ getSkillRoutingHints(skill) {
378
+ const routing = skill.metadata?.routing;
379
+ if (!routing) {
380
+ return [];
381
+ }
382
+ const hints = [];
383
+ if (routing.useWhen) {
384
+ hints.push(`Use when: ${routing.useWhen}`);
385
+ }
386
+ if (routing.dontUseWhen && !this.isLowSignalRoutingHint('dontUseWhen', routing.dontUseWhen)) {
387
+ hints.push(`Don't use when: ${routing.dontUseWhen}`);
388
+ }
389
+ if (routing.outputs && !this.isLowSignalRoutingHint('outputs', routing.outputs)) {
390
+ hints.push(`Outputs: ${routing.outputs}`);
391
+ }
392
+ if (routing.successCriteria && !this.isLowSignalRoutingHint('successCriteria', routing.successCriteria)) {
393
+ hints.push(`Success criteria: ${routing.successCriteria}`);
394
+ }
395
+ if (routing.expectedArtifacts?.length) {
396
+ hints.push(`Artifacts: ${routing.expectedArtifacts.join(', ')}`);
397
+ }
398
+ return hints;
399
+ }
400
+ isLowSignalRoutingHint(kind, value) {
401
+ const normalized = value.trim();
402
+ if (!normalized)
403
+ return true;
404
+ return CustomSkillLoader.LOW_SIGNAL_ROUTING_HINT_PATTERNS[kind].some((pattern) => pattern.test(normalized));
405
+ }
352
406
  /**
353
407
  * Get a specific skill by ID
354
408
  */
@@ -358,8 +412,8 @@ class CustomSkillLoader {
358
412
  /**
359
413
  * Expand a skill's prompt template with parameter values
360
414
  */
361
- expandPrompt(skill, parameterValues) {
362
- let prompt = this.expandBaseDir(skill.prompt, skill);
415
+ expandPrompt(skill, parameterValues, context = {}) {
416
+ let prompt = this.expandSkillPromptPlaceholders(skill.prompt, skill, context);
363
417
  // Replace {{param}} placeholders with values
364
418
  if (skill.parameters) {
365
419
  for (const param of skill.parameters) {
@@ -382,6 +436,13 @@ class CustomSkillLoader {
382
436
  const baseDir = this.resolveBaseDir(skill);
383
437
  return prompt.replace(/\{baseDir\}/g, baseDir);
384
438
  }
439
+ expandSkillPromptPlaceholders(prompt, skill, context) {
440
+ let output = this.expandBaseDir(prompt, skill);
441
+ if (context.artifactDir) {
442
+ output = output.replace(/\{artifactDir\}/g, context.artifactDir);
443
+ }
444
+ return output;
445
+ }
385
446
  resolveBaseDir(skill) {
386
447
  const fileDir = skill.filePath ? path.dirname(skill.filePath) : this.bundledSkillsDir;
387
448
  const candidates = [
@@ -580,6 +641,18 @@ class CustomSkillLoader {
580
641
  }
581
642
  }
582
643
  exports.CustomSkillLoader = CustomSkillLoader;
644
+ CustomSkillLoader.LOW_SIGNAL_ROUTING_HINT_PATTERNS = {
645
+ dontUseWhen: [
646
+ /planning documents,\s*high-level strategy,\s*or non-executable discussion/i,
647
+ ],
648
+ outputs: [
649
+ /^Outcome from .*task-specific result plus concrete action notes\.?$/i,
650
+ ],
651
+ successCriteria: [
652
+ /returns concrete actions and decisions matching the requested task/i,
653
+ /no fabricated tool-side behavior/i,
654
+ ],
655
+ };
583
656
  // Singleton instance
584
657
  let instance = null;
585
658
  function getCustomSkillLoader(config) {
@@ -381,6 +381,11 @@ class AgentDaemon extends events_1.EventEmitter {
381
381
  const parent = this.taskRepo.findById(params.parentTaskId);
382
382
  const parentGatewayContext = parent?.agentConfig?.gatewayContext;
383
383
  const childGatewayContext = params.agentConfig?.gatewayContext;
384
+ const parentAutonomousMode = parent?.agentConfig?.autonomousMode === true;
385
+ const mergedAutonomousMode = parentAutonomousMode || params.agentConfig?.autonomousMode === true;
386
+ const mergedAllowUserInput = mergedAutonomousMode
387
+ ? false
388
+ : params.agentConfig?.allowUserInput ?? parent?.agentConfig?.allowUserInput;
384
389
  // Prevent privilege escalation: a child task may not become "more private" than its parent.
385
390
  const mergedGatewayContext = (() => {
386
391
  const rank = {
@@ -418,6 +423,12 @@ class AgentDaemon extends events_1.EventEmitter {
418
423
  if (mergedToolRestrictions) {
419
424
  next.toolRestrictions = mergedToolRestrictions;
420
425
  }
426
+ if (mergedAutonomousMode !== undefined) {
427
+ next.autonomousMode = mergedAutonomousMode;
428
+ }
429
+ if (mergedAllowUserInput !== undefined) {
430
+ next.allowUserInput = mergedAllowUserInput;
431
+ }
421
432
  return Object.keys(next).length > 0 ? next : undefined;
422
433
  })();
423
434
  const task = this.taskRepo.create({
@@ -520,7 +531,15 @@ class AgentDaemon extends events_1.EventEmitter {
520
531
  return;
521
532
  const planSummary = this.buildPlanSummary(plan);
522
533
  for (const role of rolesToDispatch) {
523
- const childPrompt = (0, agent_dispatch_1.buildAgentDispatchPrompt)(role, { title: task.title, prompt: task.prompt }, { ...(planSummary ? { planSummary } : {}), includeRoleDetails: false });
534
+ const workspacePath = task.workspaceId
535
+ ? this.workspaceRepo.findById(task.workspaceId)?.path
536
+ : undefined;
537
+ const childPrompt = (0, agent_dispatch_1.buildAgentDispatchPrompt)(role, { title: task.title, prompt: task.prompt }, {
538
+ ...(planSummary ? { planSummary } : {}),
539
+ includeRoleDetails: false,
540
+ includeRoleProfile: true,
541
+ workspacePath,
542
+ });
524
543
  const childTask = await this.createChildTask({
525
544
  title: `@${role.displayName}: ${task.title}`,
526
545
  prompt: childPrompt,
@@ -714,6 +733,27 @@ class AgentDaemon extends events_1.EventEmitter {
714
733
  * Request approval from user for an action
715
734
  */
716
735
  async requestApproval(taskId, type, description, details) {
736
+ const task = this.taskRepo.findById(taskId);
737
+ if (task?.agentConfig?.autonomousMode) {
738
+ const approval = this.approvalRepo.create({
739
+ taskId,
740
+ type: type,
741
+ description,
742
+ details,
743
+ status: 'approved',
744
+ requestedAt: Date.now(),
745
+ });
746
+ this.approvalRepo.update(approval.id, 'approved');
747
+ this.logEvent(taskId, 'approval_requested', {
748
+ approval,
749
+ autoApproved: true,
750
+ });
751
+ this.logEvent(taskId, 'approval_granted', {
752
+ approvalId: approval.id,
753
+ autoApproved: true,
754
+ });
755
+ return true;
756
+ }
717
757
  const approval = this.approvalRepo.create({
718
758
  taskId,
719
759
  type: type,
@@ -1643,6 +1683,16 @@ class AgentDaemon extends events_1.EventEmitter {
1643
1683
  getWorkspaceByPath(path) {
1644
1684
  return this.workspaceRepo.findByPath(path);
1645
1685
  }
1686
+ /**
1687
+ * Get the most recently used non-temporary workspace, if any.
1688
+ */
1689
+ getMostRecentNonTempWorkspace() {
1690
+ const workspaces = this.workspaceRepo.findAll();
1691
+ return workspaces.find((workspace) => !(0, types_1.isTempWorkspaceId)(workspace.id) &&
1692
+ !workspace.isTemp &&
1693
+ typeof workspace.path === 'string' &&
1694
+ workspace.path.trim().length > 0);
1695
+ }
1646
1696
  /**
1647
1697
  * Create a new workspace with default permissions
1648
1698
  */
@@ -1657,7 +1707,7 @@ class AgentDaemon extends events_1.EventEmitter {
1657
1707
  return this.workspaceRepo.create(name, path, defaultPermissions);
1658
1708
  }
1659
1709
  /**
1660
- * Update task fields (for Goal Mode attempt tracking, etc.)
1710
+ * Update task fields (for retry/verification attempt tracking, etc.)
1661
1711
  */
1662
1712
  updateTask(taskId, updates) {
1663
1713
  const existing = this.taskRepo.findById(taskId);