crewly 1.1.2 → 1.2.0

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 (216) hide show
  1. package/README.md +6 -6
  2. package/config/roles/ops/prompt.md +140 -0
  3. package/config/roles/ops/role.json +13 -0
  4. package/config/skills/agent/browse-stealth/execute.sh +84 -0
  5. package/config/skills/agent/browse-stealth/instructions.md +108 -0
  6. package/config/skills/agent/browse-stealth/launch-chrome-cdp.sh +141 -0
  7. package/config/skills/agent/browse-stealth/skill.json +20 -0
  8. package/config/skills/agent/browse-stealth/stealth-browse.py +330 -0
  9. package/config/skills/agent/competitor-content-tracker/execute.sh +232 -0
  10. package/config/skills/agent/competitor-content-tracker/instructions.md +210 -0
  11. package/config/skills/agent/competitor-content-tracker/skill.json +22 -0
  12. package/config/skills/agent/content-calendar/execute.sh +294 -0
  13. package/config/skills/agent/content-calendar/instructions.md +122 -0
  14. package/config/skills/agent/content-calendar/skill.json +22 -0
  15. package/config/skills/agent/content-repurposer/execute.sh +194 -0
  16. package/config/skills/agent/content-repurposer/instructions.md +69 -0
  17. package/config/skills/agent/content-repurposer/skill.json +22 -0
  18. package/config/skills/agent/content-writer/execute.sh +311 -0
  19. package/config/skills/agent/content-writer/instructions.md +124 -0
  20. package/config/skills/agent/content-writer/skill.json +22 -0
  21. package/config/skills/agent/core/generate-pdf/execute.sh +88 -0
  22. package/config/skills/agent/core/generate-pdf/instructions.md +46 -0
  23. package/config/skills/agent/core/generate-pdf/skill.json +20 -0
  24. package/config/skills/agent/core/report-status/execute.sh +6 -0
  25. package/config/skills/agent/trend-monitor/execute.sh +211 -0
  26. package/config/skills/agent/trend-monitor/instructions.md +207 -0
  27. package/config/skills/agent/trend-monitor/skill.json +22 -0
  28. package/config/skills/agent/vnc-browser/execute.sh +261 -0
  29. package/config/skills/agent/vnc-browser/instructions.md +102 -0
  30. package/config/skills/agent/vnc-browser/skill.json +20 -0
  31. package/config/skills/orchestrator/delegate-task/execute.sh +63 -4
  32. package/config/skills/orchestrator/delegate-task/instructions.md +60 -0
  33. package/config/skills/orchestrator/delegate-task/skill.json +4 -4
  34. package/config/skills/orchestrator/reply-slack/execute.sh +2 -0
  35. package/config/skills/orchestrator/send-key/execute.sh +19 -6
  36. package/config/skills/orchestrator/send-key/instructions.md +44 -0
  37. package/config/skills/orchestrator/send-key/skill.json +20 -0
  38. package/config/skills/orchestrator/send-message/execute.sh +9 -1
  39. package/config/skills/registry.json +256 -0
  40. package/config/templates/code-review-team/README.md +176 -0
  41. package/config/templates/code-review-team/team-config.json +16 -0
  42. package/config/templates/code-review-team.json +62 -0
  43. package/config/templates/content-generation-team/README.md +128 -0
  44. package/config/templates/content-generation-team/team-config.json +21 -0
  45. package/config/templates/content-generation-team.json +67 -0
  46. package/config/templates/demo-team.json +22 -0
  47. package/config/templates/social-media-ops-team/README.md +145 -0
  48. package/config/templates/social-media-ops-team/team-config.json +21 -0
  49. package/config/templates/social-media-ops-team.json +67 -0
  50. package/dist/backend/backend/src/constants.d.ts +69 -6
  51. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  52. package/dist/backend/backend/src/constants.js +75 -6
  53. package/dist/backend/backend/src/constants.js.map +1 -1
  54. package/dist/backend/backend/src/controllers/index.d.ts.map +1 -1
  55. package/dist/backend/backend/src/controllers/index.js +2 -0
  56. package/dist/backend/backend/src/controllers/index.js.map +1 -1
  57. package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts +8 -0
  58. package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts.map +1 -1
  59. package/dist/backend/backend/src/controllers/messaging/messenger.routes.js +110 -63
  60. package/dist/backend/backend/src/controllers/messaging/messenger.routes.js.map +1 -1
  61. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  62. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +31 -4
  63. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  64. package/dist/backend/backend/src/controllers/oauth/oauth.routes.d.ts +8 -0
  65. package/dist/backend/backend/src/controllers/oauth/oauth.routes.d.ts.map +1 -1
  66. package/dist/backend/backend/src/controllers/oauth/oauth.routes.js +127 -111
  67. package/dist/backend/backend/src/controllers/oauth/oauth.routes.js.map +1 -1
  68. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts +34 -0
  69. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts.map +1 -1
  70. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js +219 -2
  71. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js.map +1 -1
  72. package/dist/backend/backend/src/controllers/user/user.routes.d.ts +7 -0
  73. package/dist/backend/backend/src/controllers/user/user.routes.d.ts.map +1 -1
  74. package/dist/backend/backend/src/controllers/user/user.routes.js +45 -38
  75. package/dist/backend/backend/src/controllers/user/user.routes.js.map +1 -1
  76. package/dist/backend/backend/src/controllers/whatsapp/index.d.ts +17 -0
  77. package/dist/backend/backend/src/controllers/whatsapp/index.d.ts.map +1 -0
  78. package/dist/backend/backend/src/controllers/whatsapp/index.js +18 -0
  79. package/dist/backend/backend/src/controllers/whatsapp/index.js.map +1 -0
  80. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.d.ts +12 -0
  81. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.d.ts.map +1 -0
  82. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js +185 -0
  83. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js.map +1 -0
  84. package/dist/backend/backend/src/index.d.ts +5 -0
  85. package/dist/backend/backend/src/index.d.ts.map +1 -1
  86. package/dist/backend/backend/src/index.js +35 -0
  87. package/dist/backend/backend/src/index.js.map +1 -1
  88. package/dist/backend/backend/src/routes/modules/task-management.routes.d.ts.map +1 -1
  89. package/dist/backend/backend/src/routes/modules/task-management.routes.js +4 -0
  90. package/dist/backend/backend/src/routes/modules/task-management.routes.js.map +1 -1
  91. package/dist/backend/backend/src/services/agent/agent-heartbeat.service.js +1 -1
  92. package/dist/backend/backend/src/services/agent/agent-heartbeat.service.js.map +1 -1
  93. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +14 -3
  94. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/agent/agent-registration.service.js +160 -29
  96. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  97. package/dist/backend/backend/src/services/agent/claude-runtime.service.d.ts +4 -3
  98. package/dist/backend/backend/src/services/agent/claude-runtime.service.d.ts.map +1 -1
  99. package/dist/backend/backend/src/services/agent/claude-runtime.service.js +29 -4
  100. package/dist/backend/backend/src/services/agent/claude-runtime.service.js.map +1 -1
  101. package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js +11 -0
  103. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js.map +1 -1
  104. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +32 -2
  105. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  106. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +69 -8
  107. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  108. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.d.ts.map +1 -1
  109. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js +14 -2
  110. package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js.map +1 -1
  111. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.js +11 -2
  113. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.js.map +1 -1
  114. package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.d.ts +18 -0
  115. package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.d.ts.map +1 -1
  116. package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.js +28 -4
  117. package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.js.map +1 -1
  118. package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.js +2 -2
  119. package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.js.map +1 -1
  120. package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts +18 -0
  121. package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts.map +1 -1
  122. package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js +26 -4
  123. package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js.map +1 -1
  124. package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.d.ts +28 -2
  125. package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.d.ts.map +1 -1
  126. package/dist/backend/backend/src/services/messaging/messenger-registry.service.d.ts +33 -2
  127. package/dist/backend/backend/src/services/messaging/messenger-registry.service.d.ts.map +1 -1
  128. package/dist/backend/backend/src/services/messaging/messenger-registry.service.js +33 -0
  129. package/dist/backend/backend/src/services/messaging/messenger-registry.service.js.map +1 -1
  130. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.d.ts.map +1 -1
  131. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.js +4 -2
  132. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.js.map +1 -1
  133. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.d.ts.map +1 -1
  134. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js +4 -3
  135. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js.map +1 -1
  136. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts +27 -0
  137. package/dist/backend/backend/src/services/project/task-tracking.service.d.ts.map +1 -1
  138. package/dist/backend/backend/src/services/project/task-tracking.service.js +54 -0
  139. package/dist/backend/backend/src/services/project/task-tracking.service.js.map +1 -1
  140. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +36 -6
  141. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  142. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +238 -36
  143. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  144. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  145. package/dist/backend/backend/src/services/slack/slack.service.js +6 -4
  146. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  147. package/dist/backend/backend/src/services/user/user-identity.service.d.ts +44 -0
  148. package/dist/backend/backend/src/services/user/user-identity.service.d.ts.map +1 -1
  149. package/dist/backend/backend/src/services/user/user-identity.service.js +75 -8
  150. package/dist/backend/backend/src/services/user/user-identity.service.js.map +1 -1
  151. package/dist/backend/backend/src/services/whatsapp/index.d.ts +11 -0
  152. package/dist/backend/backend/src/services/whatsapp/index.d.ts.map +1 -0
  153. package/dist/backend/backend/src/services/whatsapp/index.js +11 -0
  154. package/dist/backend/backend/src/services/whatsapp/index.js.map +1 -0
  155. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts +66 -0
  156. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts.map +1 -0
  157. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js +96 -0
  158. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js.map +1 -0
  159. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts +109 -0
  160. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -0
  161. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +234 -0
  162. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -0
  163. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts +127 -0
  164. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts.map +1 -0
  165. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js +347 -0
  166. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js.map +1 -0
  167. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  168. package/dist/backend/backend/src/services/workflow/scheduler.service.js +4 -0
  169. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  170. package/dist/backend/backend/src/types/index.d.ts +1 -0
  171. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  172. package/dist/backend/backend/src/types/index.js.map +1 -1
  173. package/dist/backend/backend/src/types/slack.types.d.ts +24 -0
  174. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  175. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  176. package/dist/backend/backend/src/types/task-tracking.types.d.ts +4 -0
  177. package/dist/backend/backend/src/types/task-tracking.types.d.ts.map +1 -1
  178. package/dist/backend/backend/src/types/task-tracking.types.js.map +1 -1
  179. package/dist/backend/backend/src/types/whatsapp.types.d.ts +84 -0
  180. package/dist/backend/backend/src/types/whatsapp.types.d.ts.map +1 -0
  181. package/dist/backend/backend/src/types/whatsapp.types.js +33 -0
  182. package/dist/backend/backend/src/types/whatsapp.types.js.map +1 -0
  183. package/dist/backend/backend/src/websocket/terminal.gateway.d.ts +11 -0
  184. package/dist/backend/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
  185. package/dist/backend/backend/src/websocket/terminal.gateway.js +35 -1
  186. package/dist/backend/backend/src/websocket/terminal.gateway.js.map +1 -1
  187. package/dist/cli/backend/src/constants.d.ts +69 -6
  188. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  189. package/dist/cli/backend/src/constants.js +75 -6
  190. package/dist/cli/backend/src/constants.js.map +1 -1
  191. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts.map +1 -1
  192. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js +14 -2
  193. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js.map +1 -1
  194. package/dist/cli/backend/src/types/index.d.ts +1 -0
  195. package/dist/cli/backend/src/types/index.d.ts.map +1 -1
  196. package/dist/cli/backend/src/types/index.js.map +1 -1
  197. package/dist/cli/cli/src/commands/publish.d.ts.map +1 -1
  198. package/dist/cli/cli/src/commands/publish.js +17 -15
  199. package/dist/cli/cli/src/commands/publish.js.map +1 -1
  200. package/dist/cli/cli/src/index.js +2 -2
  201. package/dist/cli/cli/src/index.js.map +1 -1
  202. package/dist/cli/cli/src/utils/gh-submit.d.ts +46 -0
  203. package/dist/cli/cli/src/utils/gh-submit.d.ts.map +1 -0
  204. package/dist/cli/cli/src/utils/gh-submit.js +167 -0
  205. package/dist/cli/cli/src/utils/gh-submit.js.map +1 -0
  206. package/dist/cli/cli/src/utils/marketplace.d.ts.map +1 -1
  207. package/dist/cli/cli/src/utils/marketplace.js +13 -5
  208. package/dist/cli/cli/src/utils/marketplace.js.map +1 -1
  209. package/dist/cli/cli/src/utils/templates.d.ts +3 -2
  210. package/dist/cli/cli/src/utils/templates.d.ts.map +1 -1
  211. package/dist/cli/cli/src/utils/templates.js +5 -4
  212. package/dist/cli/cli/src/utils/templates.js.map +1 -1
  213. package/frontend/dist/assets/{index-45eeea99.js → index-a23214ae.js} +241 -241
  214. package/frontend/dist/assets/{index-6972eeee.css → index-c407fe13.css} +1 -1
  215. package/frontend/dist/index.html +2 -2
  216. package/package.json +3 -1
@@ -1,6 +1,6 @@
1
1
  import * as path from 'path';
2
2
  import * as os from 'os';
3
- import { readFile, readdir, stat, mkdir, writeFile } from 'fs/promises';
3
+ import { readFile, readdir, stat, mkdir, writeFile, access } from 'fs/promises';
4
4
  import { LoggerService } from '../core/logger.service.js';
5
5
  import { createSessionCommandHelper, getSessionBackendSync, createSessionBackend, getSessionStatePersistence, } from '../session/index.js';
6
6
  import { RuntimeServiceFactory } from './runtime-service.factory.js';
@@ -13,6 +13,7 @@ import { RuntimeExitMonitorService } from './runtime-exit-monitor.service.js';
13
13
  import { ContextWindowMonitorService } from './context-window-monitor.service.js';
14
14
  import { SubAgentMessageQueue } from '../messaging/sub-agent-message-queue.service.js';
15
15
  import { AgentSuspendService } from './agent-suspend.service.js';
16
+ import { stripAnsiCodes } from '../../utils/terminal-output.utils.js';
16
17
  /**
17
18
  * Service responsible for the complex, multi-step process of agent initialization and registration.
18
19
  * Isolates the complex state management of agent startup with progressive escalation.
@@ -187,11 +188,17 @@ export class AgentRegistrationService {
187
188
  for (const excluded of (excludedRoleSkills || [])) {
188
189
  effectiveSkills.delete(excluded);
189
190
  }
190
- // 3. For each skill, read skill.json and collect flags matching runtime
191
+ // 3. For each skill, read skill.json and collect flags matching runtime.
192
+ // Skills live under agent/core/ or agent/marketplace/ subdirectories,
193
+ // so search both paths when looking up skill.json.
191
194
  for (const skillId of effectiveSkills) {
192
195
  try {
193
- const skillPath = path.join(this.projectRoot, 'config', 'skills', skillId, 'skill.json');
194
- const skillContent = await readFile(skillPath, 'utf8');
196
+ const skillJsonPath = await this.findSkillJsonPath(skillId);
197
+ if (!skillJsonPath) {
198
+ this.logger.warn('Skill config not found in any known directory', { skillId });
199
+ continue;
200
+ }
201
+ const skillContent = await readFile(skillJsonPath, 'utf8');
195
202
  const skillConfig = JSON.parse(skillContent);
196
203
  if (skillConfig.runtime?.runtime === runtimeType && Array.isArray(skillConfig.runtime?.flags)) {
197
204
  for (const flag of skillConfig.runtime.flags) {
@@ -199,8 +206,11 @@ export class AgentRegistrationService {
199
206
  }
200
207
  }
201
208
  }
202
- catch {
203
- // Skill config not found — skip silently
209
+ catch (skillErr) {
210
+ this.logger.warn('Failed to read skill config', {
211
+ skillId,
212
+ error: skillErr instanceof Error ? skillErr.message : String(skillErr),
213
+ });
204
214
  }
205
215
  }
206
216
  if (flags.size > 0) {
@@ -216,6 +226,29 @@ export class AgentRegistrationService {
216
226
  }
217
227
  return Array.from(flags);
218
228
  }
229
+ /**
230
+ * Search known skill directories for a skill's skill.json file.
231
+ * Skills live under `config/skills/agent/core/` or `config/skills/agent/marketplace/`.
232
+ *
233
+ * @param skillId - The skill identifier (directory name)
234
+ * @returns Absolute path to skill.json, or null if not found
235
+ */
236
+ async findSkillJsonPath(skillId) {
237
+ const searchDirs = [
238
+ path.join(this.projectRoot, 'config', 'skills', 'agent', 'core', skillId, 'skill.json'),
239
+ path.join(this.projectRoot, 'config', 'skills', 'agent', 'marketplace', skillId, 'skill.json'),
240
+ ];
241
+ for (const candidate of searchDirs) {
242
+ try {
243
+ await access(candidate);
244
+ return candidate;
245
+ }
246
+ catch {
247
+ // Not found in this directory, try next
248
+ }
249
+ }
250
+ return null;
251
+ }
219
252
  /**
220
253
  * Create a runtime service instance for the given runtime type.
221
254
  * Centralizes RuntimeServiceFactory creation to reduce code duplication.
@@ -422,9 +455,26 @@ export class AgentRegistrationService {
422
455
  // Must be before postInitialize and sendRegistrationPromptAsync so exits
423
456
  // during those phases are detected and the abort signal fires in time.
424
457
  RuntimeExitMonitorService.getInstance().startMonitoring(sessionName, runtimeType, role);
458
+ // Look up per-agent browser automation override from member config
459
+ let browserAutomationOverride;
460
+ if (memberId) {
461
+ try {
462
+ const teams = await this.storageService.getTeams();
463
+ for (const team of teams) {
464
+ const member = team.members?.find((m) => m.id === memberId || m.sessionName === sessionName);
465
+ if (member?.enableBrowserAutomation !== undefined) {
466
+ browserAutomationOverride = member.enableBrowserAutomation;
467
+ break;
468
+ }
469
+ }
470
+ }
471
+ catch {
472
+ // Non-fatal — will use global setting
473
+ }
474
+ }
425
475
  // Run post-initialization hook (e.g., Gemini CLI directory allowlist)
426
476
  try {
427
- await runtimeService2.postInitialize(sessionName, projectPath, additionalAllowlistPaths);
477
+ await runtimeService2.postInitialize(sessionName, projectPath, additionalAllowlistPaths, browserAutomationOverride);
428
478
  // Drain stale terminal escape sequences (e.g. DA1 [?1;2c) that may have
429
479
  // arrived during postInitialize commands, so they don't leak into the prompt input
430
480
  await delay(500);
@@ -684,10 +734,27 @@ export class AgentRegistrationService {
684
734
  // Start runtime exit monitoring immediately after runtime is ready
685
735
  RuntimeExitMonitorService.getInstance().startMonitoring(sessionName, runtimeType, role);
686
736
  }
737
+ // Look up per-agent browser automation override from member config
738
+ let browserOverrideForRecreation;
739
+ if (memberId) {
740
+ try {
741
+ const teams = await this.storageService.getTeams();
742
+ for (const team of teams) {
743
+ const member = team.members?.find((m) => m.id === memberId || m.sessionName === sessionName);
744
+ if (member?.enableBrowserAutomation !== undefined) {
745
+ browserOverrideForRecreation = member.enableBrowserAutomation;
746
+ break;
747
+ }
748
+ }
749
+ }
750
+ catch {
751
+ // Non-fatal — will use global setting
752
+ }
753
+ }
687
754
  // Run post-initialization hook (e.g., Gemini CLI directory allowlist)
688
755
  try {
689
756
  const postInitService = this.createRuntimeService(runtimeType);
690
- await postInitService.postInitialize(sessionName, projectPath);
757
+ await postInitService.postInitialize(sessionName, projectPath, undefined, browserOverrideForRecreation);
691
758
  // Drain stale terminal escape sequences (e.g. DA1 [?1;2c) that may have
692
759
  // arrived during postInitialize commands, so they don't leak into the prompt input
693
760
  await delay(500);
@@ -1742,7 +1809,10 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1742
1809
  const unsubscribe = session.onData((data) => {
1743
1810
  if (resolved)
1744
1811
  return;
1745
- if (streamPattern.test(data)) {
1812
+ // Strip ANSI escape sequences before testing — raw PTY data contains
1813
+ // cursor positioning, color codes, etc. that break regex matching (#106)
1814
+ const cleanData = stripAnsiCodes(data);
1815
+ if (streamPattern.test(cleanData)) {
1746
1816
  // Double-check with capturePane to avoid false positives from partial data
1747
1817
  const output = sessionHelper.capturePane(sessionName);
1748
1818
  if (this.isClaudeAtPrompt(output, runtimeType)) {
@@ -2035,11 +2105,25 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2035
2105
  // Verify agent is at prompt before sending
2036
2106
  const output = sessionHelper.capturePane(sessionName);
2037
2107
  if (!this.isClaudeAtPrompt(output, runtimeType)) {
2038
- // On the final attempt, fall back to direct delivery rather than
2039
- // returning a 502 error. The agent may be at a prompt that doesn't
2040
- // match our detection patterns, or the terminal buffer may be stale.
2041
- // This mirrors what the /write endpoint does successfully.
2042
2108
  if (attempt === maxAttempts) {
2109
+ // On the final attempt, check if the agent is DEFINITELY busy
2110
+ // before force-delivering. If we see "esc to interrupt" or
2111
+ // processing indicators, the agent is actively working and
2112
+ // force-delivery risks corrupting its current task.
2113
+ const tailForBusyCheck = output.slice(-2000);
2114
+ const isBusy = TERMINAL_PATTERNS.BUSY_STATUS_BAR.test(tailForBusyCheck) ||
2115
+ TERMINAL_PATTERNS.PROCESSING_WITH_TEXT.test(tailForBusyCheck);
2116
+ if (isBusy) {
2117
+ this.logger.warn('Agent is busy (processing indicators detected), skipping force delivery', {
2118
+ sessionName,
2119
+ attempt,
2120
+ runtimeType,
2121
+ });
2122
+ return false;
2123
+ }
2124
+ // Not clearly busy — fall back to direct delivery. The agent
2125
+ // may be at a prompt that doesn't match our detection patterns,
2126
+ // or the terminal buffer may be stale.
2043
2127
  this.logger.warn('Prompt detection failed on final attempt, delivering message directly', {
2044
2128
  sessionName,
2045
2129
  attempt,
@@ -2096,9 +2180,18 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2096
2180
  }
2097
2181
  }
2098
2182
  else {
2099
- // On retry attempts (2+), force a PTY resize to trigger SIGWINCH.
2100
- // This makes Ink re-render the TUI, potentially restoring focus state.
2101
- if (attempt > 1) {
2183
+ // Detect recent /compress Ink TUI loses internal focus after
2184
+ // /compress re-renders, causing subsequent messages to be silently
2185
+ // dropped even though the prompt `>` is visible (#114).
2186
+ // Always force PTY resize on attempt 1 if /compress detected.
2187
+ const recentOutput = sessionHelper.capturePane(sessionName, 40);
2188
+ const compressDetected = recentOutput.includes('/compress') ||
2189
+ recentOutput.includes('Context compressed') ||
2190
+ recentOutput.includes('Compressing context');
2191
+ const needsResize = attempt > 1 || compressDetected;
2192
+ // Force a PTY resize to trigger SIGWINCH, making Ink
2193
+ // re-render the TUI and potentially restore focus state.
2194
+ if (needsResize) {
2102
2195
  try {
2103
2196
  const session = sessionHelper.getSession(sessionName);
2104
2197
  if (session) {
@@ -2110,6 +2203,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2110
2203
  this.logger.debug('PTY resize sent to trigger TUI re-render', {
2111
2204
  sessionName,
2112
2205
  attempt,
2206
+ compressDetected,
2113
2207
  });
2114
2208
  }
2115
2209
  }
@@ -2131,7 +2225,8 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2131
2225
  // the input is engaged. Enter on an empty `> ` prompt is a
2132
2226
  // safe no-op (just shows a new blank prompt line).
2133
2227
  await sessionHelper.sendEnter(sessionName);
2134
- await delay(500);
2228
+ // Extra settling time after /compress to let Ink TUI stabilize
2229
+ await delay(compressDetected ? 1000 : 500);
2135
2230
  }
2136
2231
  // For Gemini CLI: detect and gently escape interactive modes before
2137
2232
  // sending the message. Avoid Ctrl-C here — Gemini interprets it as
@@ -2400,7 +2495,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2400
2495
  || hasProcessingIndicators
2401
2496
  || hasGeminiIndicators;
2402
2497
  if (delivered) {
2403
- this.logger.debug('Message delivered successfully (TUI output changed)', {
2498
+ // Log at warn level when verification passed on weak signals
2499
+ // (output length change only, no explicit processing indicators)
2500
+ // so future false positives are traceable in logs.
2501
+ const hasStrongSignal = hasProcessingIndicators || hasGeminiIndicators;
2502
+ const logLevel = hasStrongSignal ? 'debug' : 'warn';
2503
+ this.logger[logLevel](`Message delivery verified (${hasStrongSignal ? 'strong' : 'weak'} signal)`, {
2404
2504
  sessionName,
2405
2505
  attempt,
2406
2506
  lengthDiff,
@@ -2408,6 +2508,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2408
2508
  hasProcessingIndicators,
2409
2509
  hasGeminiIndicators,
2410
2510
  newContentLength: newContent.length,
2511
+ signal: hasStrongSignal ? 'processing-indicators' : 'output-length-change-only',
2411
2512
  });
2412
2513
  return true;
2413
2514
  }
@@ -2460,6 +2561,19 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2460
2561
  }
2461
2562
  }
2462
2563
  }
2564
+ // Verification failed, but the message was physically written to the PTY.
2565
+ // If the session is still alive, the agent will likely process it — return
2566
+ // true to avoid false "Failed to deliver" errors shown to users (#99).
2567
+ const backend = getSessionBackendSync();
2568
+ const childAlive = backend?.isChildProcessAlive?.(sessionName);
2569
+ if (childAlive !== false) {
2570
+ this.logger.warn('Message delivery verification inconclusive but session alive — assuming success', {
2571
+ sessionName,
2572
+ maxAttempts,
2573
+ messageLength: message.length,
2574
+ });
2575
+ return true;
2576
+ }
2463
2577
  this.logger.error('Message delivery failed after all retry attempts', {
2464
2578
  sessionName,
2465
2579
  maxAttempts,
@@ -2822,11 +2936,14 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2822
2936
  return TERMINAL_PATTERNS.PROMPT_STREAM;
2823
2937
  }
2824
2938
  /**
2825
- * Check if Claude Code appears to be at an input prompt.
2826
- * Looks for common prompt indicators in terminal output.
2939
+ * Check if the agent appears to be at an input prompt.
2940
+ * Looks for prompt indicators (❯, ⏵, $, ❯❯, ⏵⏵) in terminal output.
2941
+ * Also checks for busy indicators (esc to interrupt, spinners, ⏺)
2942
+ * to avoid false negatives when the agent is processing.
2827
2943
  *
2828
2944
  * @param terminalOutput - The terminal output to check
2829
- * @returns true if Claude Code appears to be at a prompt
2945
+ * @param runtimeType - The runtime type for pattern selection
2946
+ * @returns true if the agent appears to be at a prompt
2830
2947
  */
2831
2948
  isClaudeAtPrompt(terminalOutput, runtimeType) {
2832
2949
  // Handle null/undefined/empty input — return false since an empty buffer
@@ -2835,8 +2952,10 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2835
2952
  this.logger.debug('Terminal output is empty or invalid, cannot detect prompt', { runtimeType });
2836
2953
  return false;
2837
2954
  }
2838
- // Only analyze the tail of the buffer to avoid matching historical prompts
2839
- const tailSection = terminalOutput.slice(-2000);
2955
+ // Only analyze the tail of the buffer to avoid matching historical prompts.
2956
+ // Use 5000 chars to accommodate large tool outputs that push the prompt
2957
+ // further back in the buffer (#106).
2958
+ const tailSection = terminalOutput.slice(-5000);
2840
2959
  const isGemini = runtimeType === RUNTIME_TYPES.GEMINI_CLI;
2841
2960
  const isClaudeCode = runtimeType === RUNTIME_TYPES.CLAUDE_CODE;
2842
2961
  const isCodex = runtimeType === RUNTIME_TYPES.CODEX_CLI;
@@ -2854,8 +2973,11 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2854
2973
  const linesToCheck = lines.slice(-10);
2855
2974
  const hasPrompt = linesToCheck.some((line) => {
2856
2975
  const trimmed = line.trim();
2857
- // Strip TUI box-drawing borders (│, ┃, etc.) that Gemini CLI wraps around prompts
2858
- const stripped = trimmed.replace(/^[│┃|]+\s*/, '').replace(/\s*[│┃|]+$/, '');
2976
+ // Strip TUI box-drawing borders that Gemini CLI and other TUI frameworks
2977
+ // wrap around prompts. Covers full Unicode box-drawing range (#106).
2978
+ const stripped = trimmed
2979
+ .replace(/^[\u2500-\u257F|+\-═║╭╮╰╯]+\s*/, '')
2980
+ .replace(/\s*[\u2500-\u257F|+\-═║╭╮╰╯]+$/, '');
2859
2981
  // Claude Code prompts: ❯, ⏵, $ alone on a line
2860
2982
  if (!isGemini && !isCodex) {
2861
2983
  if (['❯', '⏵', '$'].some(ch => trimmed === ch || stripped === ch)) {
@@ -2863,7 +2985,8 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2863
2985
  }
2864
2986
  // ❯❯ = bypass permissions prompt (idle).
2865
2987
  // Matches "❯❯", "❯❯ ", and "❯❯ bypass permissions on (shift+tab to cycle)".
2866
- // This line disappears during processing and reappears when idle.
2988
+ // Note: ⏵⏵ appears in the status bar but is visible both when idle AND
2989
+ // busy, so it cannot be used as a reliable prompt indicator.
2867
2990
  if (trimmed.startsWith('❯❯')) {
2868
2991
  return true;
2869
2992
  }
@@ -2887,13 +3010,21 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2887
3010
  if (hasPrompt) {
2888
3011
  return true;
2889
3012
  }
2890
- // No prompt found — check if still processing. Only check the last few
2891
- // lines to avoid matching words like "thinking" in historical response text.
2892
- const recentLines = linesToCheck.slice(-5).join('\n');
3013
+ // No prompt found — check if still processing.
3014
+ // Check last 10 lines (not just 5) because tool output can push processing
3015
+ // indicators further up while the status bar stays at the bottom.
3016
+ const recentLines = linesToCheck.join('\n');
2893
3017
  if (TERMINAL_PATTERNS.PROCESSING_WITH_TEXT.test(recentLines)) {
2894
3018
  this.logger.debug('Processing indicators present near bottom of output');
2895
3019
  return false;
2896
3020
  }
3021
+ // Check for "esc to interrupt" in the status bar — this is a definitive
3022
+ // busy signal. Claude Code only shows this text while actively processing.
3023
+ // It disappears when the agent returns to idle at the prompt.
3024
+ if (TERMINAL_PATTERNS.BUSY_STATUS_BAR.test(recentLines)) {
3025
+ this.logger.debug('Busy status bar detected (esc to interrupt)');
3026
+ return false;
3027
+ }
2897
3028
  this.logger.debug('No prompt detected in terminal output', {
2898
3029
  tailLength: tailSection.length,
2899
3030
  lastLines: linesToCheck.slice(-3).map(l => l.substring(0, 80)),