crewly 1.0.11 → 1.1.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 (110) hide show
  1. package/config/constants.ts +6 -0
  2. package/config/roles/orchestrator/self-evolution.md +72 -0
  3. package/config/skills/orchestrator/read-session-logs/execute.sh +14 -0
  4. package/config/skills/orchestrator/read-session-logs/instructions.md +33 -0
  5. package/config/skills/orchestrator/read-session-logs/skill.json +20 -0
  6. package/config/skills/orchestrator/read-system-logs/execute.sh +18 -0
  7. package/config/skills/orchestrator/read-system-logs/instructions.md +30 -0
  8. package/config/skills/orchestrator/read-system-logs/skill.json +20 -0
  9. package/config/skills/orchestrator/reply-slack/execute.sh +12 -0
  10. package/config/skills/orchestrator/reply-slack/instructions.md +16 -1
  11. package/config/skills/orchestrator/report-bug/execute.sh +49 -0
  12. package/config/skills/orchestrator/report-bug/instructions.md +30 -0
  13. package/config/skills/orchestrator/report-bug/skill.json +20 -0
  14. package/dist/backend/backend/src/constants.d.ts +24 -1
  15. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  16. package/dist/backend/backend/src/constants.js +25 -2
  17. package/dist/backend/backend/src/constants.js.map +1 -1
  18. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts +14 -0
  19. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +74 -0
  21. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js +20 -34
  24. package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js.map +1 -1
  25. package/dist/backend/backend/src/index.d.ts.map +1 -1
  26. package/dist/backend/backend/src/index.js +16 -1
  27. package/dist/backend/backend/src/index.js.map +1 -1
  28. package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts.map +1 -1
  29. package/dist/backend/backend/src/routes/modules/terminal.routes.js +2 -0
  30. package/dist/backend/backend/src/routes/modules/terminal.routes.js.map +1 -1
  31. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +24 -2
  32. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/agent/agent-registration.service.js +191 -89
  34. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  35. package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts +5 -1
  36. package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/agent/codex-runtime.service.js +10 -17
  38. package/dist/backend/backend/src/services/agent/codex-runtime.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts +16 -0
  40. package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js +111 -10
  42. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js.map +1 -1
  43. package/dist/backend/backend/src/services/agent/gemini-runtime.service.d.ts +33 -2
  44. package/dist/backend/backend/src/services/agent/gemini-runtime.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/agent/gemini-runtime.service.js +388 -20
  46. package/dist/backend/backend/src/services/agent/gemini-runtime.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +1 -1
  48. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +1 -1
  50. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  51. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts +17 -2
  52. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +105 -12
  54. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
  55. package/dist/backend/backend/src/services/core/config.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/core/config.service.js +3 -2
  57. package/dist/backend/backend/src/services/core/config.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/messaging/message-queue.service.js +17 -0
  60. package/dist/backend/backend/src/services/messaging/message-queue.service.js.map +1 -1
  61. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.d.ts +5 -0
  62. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js +42 -2
  64. package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js.map +1 -1
  65. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts +39 -0
  66. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +121 -1
  68. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  69. package/dist/backend/backend/src/services/settings/settings.service.d.ts.map +1 -1
  70. package/dist/backend/backend/src/services/settings/settings.service.js +6 -0
  71. package/dist/backend/backend/src/services/settings/settings.service.js.map +1 -1
  72. package/dist/backend/backend/src/services/workflow/message-scheduler.service.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js +8 -1
  74. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js.map +1 -1
  75. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts +1 -0
  76. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  77. package/dist/backend/backend/src/services/workflow/scheduler.service.js +28 -1
  78. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  79. package/dist/backend/backend/src/types/settings.types.d.ts +4 -0
  80. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  81. package/dist/backend/backend/src/types/settings.types.js +9 -1
  82. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  83. package/dist/backend/config/constants.d.ts +6 -0
  84. package/dist/backend/config/constants.d.ts.map +1 -1
  85. package/dist/backend/config/constants.js +6 -0
  86. package/dist/backend/config/constants.js.map +1 -1
  87. package/dist/backend/config/index.d.ts +3 -0
  88. package/dist/backend/config/index.d.ts.map +1 -1
  89. package/dist/cli/backend/src/constants.d.ts +24 -1
  90. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  91. package/dist/cli/backend/src/constants.js +25 -2
  92. package/dist/cli/backend/src/constants.js.map +1 -1
  93. package/dist/cli/backend/src/services/core/config.service.d.ts.map +1 -1
  94. package/dist/cli/backend/src/services/core/config.service.js +3 -2
  95. package/dist/cli/backend/src/services/core/config.service.js.map +1 -1
  96. package/dist/cli/backend/src/types/settings.types.d.ts +4 -0
  97. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  98. package/dist/cli/backend/src/types/settings.types.js +9 -1
  99. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  100. package/dist/cli/cli/src/index.js +1 -0
  101. package/dist/cli/cli/src/index.js.map +1 -1
  102. package/dist/cli/config/constants.d.ts +6 -0
  103. package/dist/cli/config/constants.d.ts.map +1 -1
  104. package/dist/cli/config/constants.js +6 -0
  105. package/dist/cli/config/constants.js.map +1 -1
  106. package/dist/cli/config/index.d.ts +3 -0
  107. package/dist/cli/config/index.d.ts.map +1 -1
  108. package/frontend/dist/assets/{index-0a245b0d.js → index-45eeea99.js} +2 -2
  109. package/frontend/dist/index.html +1 -1
  110. package/package.json +1 -1
@@ -26,6 +26,7 @@ export declare class AgentRegistrationService {
26
26
  private stuckMessageDetectorTimer;
27
27
  private tuiSessionRegistry;
28
28
  private sentMessageTracker;
29
+ private sessionDeliveryMutex;
29
30
  private static get CLAUDE_PROMPT_INDICATORS();
30
31
  private static get CLAUDE_PROMPT_STREAM_PATTERN();
31
32
  private static get CLAUDE_PROCESSING_INDICATORS();
@@ -46,6 +47,17 @@ export declare class AgentRegistrationService {
46
47
  * Get session helper synchronously (may throw if not initialized)
47
48
  */
48
49
  private getSessionHelperSync;
50
+ /**
51
+ * Resolve the runtime type for a session from storage.
52
+ * Checks orchestrator status first, then team member data.
53
+ * Falls back to CLAUDE_CODE if nothing is found.
54
+ *
55
+ * IMPORTANT: Callers should prefer passing runtimeType explicitly.
56
+ * This method exists as a safety net so that Ctrl+C (Claude Code
57
+ * cleanup) is never sent to a Gemini CLI session, which would
58
+ * trigger /quit and kill the agent.
59
+ */
60
+ private resolveSessionRuntimeType;
49
61
  /**
50
62
  * Find the project root by looking for package.json
51
63
  */
@@ -85,7 +97,7 @@ export declare class AgentRegistrationService {
85
97
  * Initialize agent with optimized 2-step escalation process
86
98
  * Reduced from 4-step to 2-step with shorter timeouts for better concurrency
87
99
  */
88
- initializeAgentWithRegistration(sessionName: string, role: string, projectPath?: string, timeout?: number, memberId?: string, runtimeType?: RuntimeType, runtimeFlags?: string[]): Promise<{
100
+ initializeAgentWithRegistration(sessionName: string, role: string, projectPath?: string, timeout?: number, memberId?: string, runtimeType?: RuntimeType, runtimeFlags?: string[], additionalAllowlistPaths?: string[]): Promise<{
89
101
  success: boolean;
90
102
  message?: string;
91
103
  error?: string;
@@ -134,10 +146,13 @@ export declare class AgentRegistrationService {
134
146
  *
135
147
  * @param sessionName - Session name (used for filename)
136
148
  * @param prompt - The full prompt content
137
- * @param runtimeType - Runtime type (determines directory)
138
149
  * @returns The absolute path to the written file, or undefined on error
139
150
  */
140
151
  private writePromptFile;
152
+ /**
153
+ * Build the unified init prompt path used by all runtimes.
154
+ */
155
+ private getInitPromptFilePath;
141
156
  /**
142
157
  * Wait for agent registration to complete
143
158
  */
@@ -179,6 +194,13 @@ export declare class AgentRegistrationService {
179
194
  * @default false
180
195
  */
181
196
  forceRecreate?: boolean;
197
+ /**
198
+ * Additional paths to add to Gemini CLI's /directory allowlist during
199
+ * postInitialize, before the registration prompt is sent. Used by the
200
+ * orchestrator to include all existing project paths so they don't
201
+ * race with the "Read the file..." prompt.
202
+ */
203
+ additionalAllowlistPaths?: string[];
182
204
  }): Promise<{
183
205
  success: boolean;
184
206
  sessionName?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-registration.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/agent-registration.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-registration.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/agent-registration.service.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { readFile, readdir, stat, mkdir, writeFile } 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';
7
- import { CREWLY_CONSTANTS, ENV_CONSTANTS, AGENT_TIMEOUTS, ORCHESTRATOR_ROLE, RUNTIME_TYPES, SESSION_COMMAND_DELAYS, EVENT_DELIVERY_CONSTANTS, TERMINAL_PATTERNS, GEMINI_SHELL_MODE_CONSTANTS, } from '../../constants.js';
7
+ import { CREWLY_CONSTANTS, ENV_CONSTANTS, AGENT_TIMEOUTS, ORCHESTRATOR_SESSION_NAME, ORCHESTRATOR_ROLE, RUNTIME_TYPES, SESSION_COMMAND_DELAYS, EVENT_DELIVERY_CONSTANTS, TERMINAL_PATTERNS, GEMINI_SHELL_MODE_CONSTANTS, } from '../../constants.js';
8
8
  import { WEB_CONSTANTS } from '../../../../config/constants.js';
9
9
  import { delay } from '../../utils/async.utils.js';
10
10
  import { getSettingsService } from '../settings/settings.service.js';
@@ -41,6 +41,10 @@ export class AgentRegistrationService {
41
41
  // Track recently-sent messages for background stuck-detection (all runtimes).
42
42
  // Key: sessionName, Value: array of tracked message entries
43
43
  sentMessageTracker = new Map();
44
+ // Per-session delivery mutex to serialize message delivery.
45
+ // Prevents concurrent sendMessageWithRetry calls to the same session,
46
+ // which causes multiple Ctrl+C presses that can crash the runtime.
47
+ sessionDeliveryMutex = new Map();
44
48
  // Terminal patterns are now centralized in TERMINAL_PATTERNS constant
45
49
  // Keeping these as static getters for backwards compatibility within the class
46
50
  static get CLAUDE_PROMPT_INDICATORS() {
@@ -119,6 +123,39 @@ export class AgentRegistrationService {
119
123
  }
120
124
  return this._sessionHelper;
121
125
  }
126
+ /**
127
+ * Resolve the runtime type for a session from storage.
128
+ * Checks orchestrator status first, then team member data.
129
+ * Falls back to CLAUDE_CODE if nothing is found.
130
+ *
131
+ * IMPORTANT: Callers should prefer passing runtimeType explicitly.
132
+ * This method exists as a safety net so that Ctrl+C (Claude Code
133
+ * cleanup) is never sent to a Gemini CLI session, which would
134
+ * trigger /quit and kill the agent.
135
+ */
136
+ async resolveSessionRuntimeType(sessionName) {
137
+ try {
138
+ // Check if this is the orchestrator session
139
+ if (sessionName === ORCHESTRATOR_SESSION_NAME) {
140
+ const orchestratorStatus = await this.storageService.getOrchestratorStatus();
141
+ if (orchestratorStatus?.runtimeType) {
142
+ return orchestratorStatus.runtimeType;
143
+ }
144
+ }
145
+ // Check team member data
146
+ const memberInfo = await this.storageService.findMemberBySessionName(sessionName);
147
+ if (memberInfo?.member?.runtimeType) {
148
+ return memberInfo.member.runtimeType;
149
+ }
150
+ }
151
+ catch (error) {
152
+ this.logger.debug('Could not resolve runtime type from storage, using default', {
153
+ sessionName,
154
+ error: error instanceof Error ? error.message : String(error),
155
+ });
156
+ }
157
+ return RUNTIME_TYPES.CLAUDE_CODE;
158
+ }
122
159
  /**
123
160
  * Find the project root by looking for package.json
124
161
  */
@@ -224,7 +261,7 @@ export class AgentRegistrationService {
224
261
  * Initialize agent with optimized 2-step escalation process
225
262
  * Reduced from 4-step to 2-step with shorter timeouts for better concurrency
226
263
  */
227
- async initializeAgentWithRegistration(sessionName, role, projectPath, timeout = AGENT_TIMEOUTS.REGULAR_AGENT_INITIALIZATION, memberId, runtimeType = RUNTIME_TYPES.CLAUDE_CODE, runtimeFlags) {
264
+ async initializeAgentWithRegistration(sessionName, role, projectPath, timeout = AGENT_TIMEOUTS.REGULAR_AGENT_INITIALIZATION, memberId, runtimeType = RUNTIME_TYPES.CLAUDE_CODE, runtimeFlags, additionalAllowlistPaths) {
228
265
  const startTime = Date.now();
229
266
  this.logger.info('Starting optimized agent initialization with registration', {
230
267
  sessionName,
@@ -245,7 +282,7 @@ export class AgentRegistrationService {
245
282
  sessionName,
246
283
  });
247
284
  const step1Success = await this.tryCleanupAndReinit(sessionName, role, 70000, // 70 seconds for cleanup and reinit (allows 60s for runtime ready)
248
- projectPath, memberId, runtimeType, runtimeFlags);
285
+ projectPath, memberId, runtimeType, runtimeFlags, additionalAllowlistPaths);
249
286
  if (step1Success) {
250
287
  return {
251
288
  success: true,
@@ -325,7 +362,7 @@ export class AgentRegistrationService {
325
362
  * Step 2: Cleanup with Ctrl+C and reinitialize
326
363
  * Tries to reset the runtime session and start fresh
327
364
  */
328
- async tryCleanupAndReinit(sessionName, role, timeout, projectPath, memberId, runtimeType = RUNTIME_TYPES.CLAUDE_CODE, runtimeFlags) {
365
+ async tryCleanupAndReinit(sessionName, role, timeout, projectPath, memberId, runtimeType = RUNTIME_TYPES.CLAUDE_CODE, runtimeFlags, additionalAllowlistPaths) {
329
366
  // Clear Commandline
330
367
  await (await this.getSessionHelper()).clearCurrentCommandLine(sessionName);
331
368
  // Inject --resume flag if this was a previously running Claude Code session
@@ -360,7 +397,7 @@ export class AgentRegistrationService {
360
397
  if (runtimeType === RUNTIME_TYPES.CLAUDE_CODE) {
361
398
  try {
362
399
  const prompt = await this.loadRegistrationPrompt(role, sessionName, memberId);
363
- promptFilePath = await this.writePromptFile(sessionName, prompt, runtimeType);
400
+ promptFilePath = await this.writePromptFile(sessionName, prompt);
364
401
  }
365
402
  catch (promptError) {
366
403
  this.logger.warn('Failed to pre-write prompt file (non-fatal, will fall back to direct delivery)', {
@@ -387,7 +424,7 @@ export class AgentRegistrationService {
387
424
  RuntimeExitMonitorService.getInstance().startMonitoring(sessionName, runtimeType, role);
388
425
  // Run post-initialization hook (e.g., Gemini CLI directory allowlist)
389
426
  try {
390
- await runtimeService2.postInitialize(sessionName, projectPath);
427
+ await runtimeService2.postInitialize(sessionName, projectPath, additionalAllowlistPaths);
391
428
  // Drain stale terminal escape sequences (e.g. DA1 [?1;2c) that may have
392
429
  // arrived during postInitialize commands, so they don't leak into the prompt input
393
430
  await delay(500);
@@ -502,13 +539,22 @@ export class AgentRegistrationService {
502
539
  const controller = new AbortController();
503
540
  this.registrationAbortControllers.set(sessionName, controller);
504
541
  try {
542
+ this.logger.info('Loading registration prompt', { sessionName, role, runtimeType });
505
543
  if (controller.signal.aborted)
506
544
  return;
507
545
  const prompt = await this.loadRegistrationPrompt(role, sessionName, memberId);
546
+ this.logger.info('Registration prompt loaded, sending to agent', {
547
+ sessionName, role, runtimeType, promptLength: prompt.length,
548
+ });
508
549
  if (controller.signal.aborted)
509
550
  return;
510
- await this.sendPromptRobustly(sessionName, prompt, runtimeType, controller.signal);
511
- this.logger.debug('Registration prompt sent asynchronously', { sessionName, role });
551
+ const sent = await this.sendPromptRobustly(sessionName, prompt, runtimeType, controller.signal);
552
+ if (sent) {
553
+ this.logger.info('Registration prompt sent successfully', { sessionName, role });
554
+ }
555
+ else {
556
+ this.logger.warn('Registration prompt delivery returned false', { sessionName, role, runtimeType });
557
+ }
512
558
  }
513
559
  catch (error) {
514
560
  if (controller.signal.aborted) {
@@ -518,6 +564,7 @@ export class AgentRegistrationService {
518
564
  this.logger.warn('Failed to send registration prompt asynchronously', {
519
565
  sessionName,
520
566
  error: error instanceof Error ? error.message : String(error),
567
+ stack: error instanceof Error ? error.stack : undefined,
521
568
  });
522
569
  }
523
570
  finally {
@@ -565,7 +612,7 @@ export class AgentRegistrationService {
565
612
  if (runtimeType === RUNTIME_TYPES.CLAUDE_CODE) {
566
613
  try {
567
614
  const prompt = await this.loadRegistrationPrompt(role, sessionName, memberId);
568
- promptFilePath = await this.writePromptFile(sessionName, prompt, runtimeType);
615
+ promptFilePath = await this.writePromptFile(sessionName, prompt);
569
616
  }
570
617
  catch (promptError) {
571
618
  this.logger.warn('Failed to pre-write prompt file in full recreation (non-fatal)', {
@@ -598,16 +645,27 @@ export class AgentRegistrationService {
598
645
  // Wait a bit longer for runtime to fully load after showing welcome message
599
646
  this.logger.debug('Runtime ready detected for orchestrator, waiting for full startup before verification', { sessionName, runtimeType });
600
647
  await delay(5000);
601
- this.logger.debug('Verifying orchestrator runtime responsiveness', {
602
- sessionName,
603
- runtimeType,
604
- });
605
- // runtimeService4: Final verification instance for orchestrator responsiveness
606
- // Clean instance for post-initialization responsiveness testing
607
- const runtimeService4 = this.createRuntimeService(runtimeType);
608
- const runtimeResponding = await runtimeService4.detectRuntimeWithCommand(sessionName);
609
- if (!runtimeResponding) {
610
- throw new Error(`${runtimeType} not responding to commands after orchestrator recreation`);
648
+ // Codex CLI is sensitive to active key-probe verification (it may drop to
649
+ // the shell when probe keys are interpreted outside the TUI). We already
650
+ // passed waitForRuntimeReady above, so skip the extra command probe here.
651
+ if (runtimeType !== RUNTIME_TYPES.CODEX_CLI) {
652
+ this.logger.debug('Verifying orchestrator runtime responsiveness', {
653
+ sessionName,
654
+ runtimeType,
655
+ });
656
+ // runtimeService4: Final verification instance for orchestrator responsiveness
657
+ // Clean instance for post-initialization responsiveness testing
658
+ const runtimeService4 = this.createRuntimeService(runtimeType);
659
+ const runtimeResponding = await runtimeService4.detectRuntimeWithCommand(sessionName);
660
+ if (!runtimeResponding) {
661
+ throw new Error(`${runtimeType} not responding to commands after orchestrator recreation`);
662
+ }
663
+ }
664
+ else {
665
+ this.logger.debug('Skipping active runtime probe for Codex orchestrator recreation', {
666
+ sessionName,
667
+ runtimeType,
668
+ });
611
669
  }
612
670
  this.logger.debug('Runtime confirmed ready for orchestrator in Step 3', { sessionName, runtimeType });
613
671
  }
@@ -789,6 +847,24 @@ export class AgentRegistrationService {
789
847
  if (memberId) {
790
848
  prompt += `\n- **Member ID:** ${memberId}`;
791
849
  }
850
+ // Conditionally append Self Evolution instructions for the orchestrator
851
+ if (role === ORCHESTRATOR_ROLE) {
852
+ try {
853
+ const settings = await getSettingsService().getSettings();
854
+ if (settings.general.enableSelfEvolution) {
855
+ const selfEvoPath = path.join(this.projectRoot, 'config', 'roles', 'orchestrator', 'self-evolution.md');
856
+ const selfEvoPrompt = await readFile(selfEvoPath, 'utf8');
857
+ prompt += `\n\n---\n\n${selfEvoPrompt}`;
858
+ this.logger.info('Self Evolution prompt injected', { sessionName });
859
+ }
860
+ }
861
+ catch (selfEvoError) {
862
+ this.logger.warn('Failed to load self-evolution prompt (non-critical)', {
863
+ sessionName,
864
+ error: selfEvoError instanceof Error ? selfEvoError.message : String(selfEvoError),
865
+ });
866
+ }
867
+ }
792
868
  return prompt;
793
869
  }
794
870
  catch (error) {
@@ -825,15 +901,11 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
825
901
  *
826
902
  * @param sessionName - Session name (used for filename)
827
903
  * @param prompt - The full prompt content
828
- * @param runtimeType - Runtime type (determines directory)
829
904
  * @returns The absolute path to the written file, or undefined on error
830
905
  */
831
- async writePromptFile(sessionName, prompt, runtimeType) {
832
- const isClaudeCode = runtimeType === RUNTIME_TYPES.CLAUDE_CODE;
833
- const promptsDir = isClaudeCode
834
- ? path.join(os.homedir(), CREWLY_CONSTANTS.PATHS.CREWLY_HOME, 'prompts')
835
- : path.join(this.projectRoot, '.crewly', 'prompts');
836
- const promptFilePath = path.join(promptsDir, `${sessionName}-init.md`);
906
+ async writePromptFile(sessionName, prompt) {
907
+ const promptFilePath = this.getInitPromptFilePath(sessionName);
908
+ const promptsDir = path.dirname(promptFilePath);
837
909
  try {
838
910
  await mkdir(promptsDir, { recursive: true });
839
911
  await writeFile(promptFilePath, prompt, 'utf8');
@@ -853,6 +925,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
853
925
  return undefined;
854
926
  }
855
927
  }
928
+ /**
929
+ * Build the unified init prompt path used by all runtimes.
930
+ */
931
+ getInitPromptFilePath(sessionName) {
932
+ return path.join(os.homedir(), CREWLY_CONSTANTS.PATHS.CREWLY_HOME, 'prompts', `${sessionName}-init.md`);
933
+ }
856
934
  /**
857
935
  * Wait for agent registration to complete
858
936
  */
@@ -1291,7 +1369,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1291
1369
  }
1292
1370
  // Start context window monitoring for recovered non-orchestrator session
1293
1371
  if (role !== ORCHESTRATOR_ROLE && config.teamId && config.memberId) {
1294
- ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role);
1372
+ ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role, runtimeType);
1295
1373
  }
1296
1374
  return {
1297
1375
  success: true,
@@ -1366,7 +1444,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1366
1444
  const timeout = role === ORCHESTRATOR_ROLE
1367
1445
  ? AGENT_TIMEOUTS.ORCHESTRATOR_INITIALIZATION
1368
1446
  : AGENT_TIMEOUTS.REGULAR_AGENT_INITIALIZATION;
1369
- const initResult = await this.initializeAgentWithRegistration(sessionName, role, projectPath, timeout, memberId, runtimeType, runtimeFlags);
1447
+ const initResult = await this.initializeAgentWithRegistration(sessionName, role, projectPath, timeout, memberId, runtimeType, runtimeFlags, config.additionalAllowlistPaths);
1370
1448
  if (!initResult.success) {
1371
1449
  return {
1372
1450
  success: false,
@@ -1376,7 +1454,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1376
1454
  }
1377
1455
  // Start context window monitoring for newly created non-orchestrator session
1378
1456
  if (role !== ORCHESTRATOR_ROLE && config.teamId && config.memberId) {
1379
- ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role);
1457
+ ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role, runtimeType);
1380
1458
  }
1381
1459
  return {
1382
1460
  success: true,
@@ -1490,7 +1568,22 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1490
1568
  * }
1491
1569
  * ```
1492
1570
  */
1493
- async sendMessageToAgent(sessionName, message, runtimeType = RUNTIME_TYPES.CLAUDE_CODE) {
1571
+ async sendMessageToAgent(sessionName, message, runtimeType) {
1572
+ // Per-session delivery serialization: chain this delivery after any
1573
+ // in-flight delivery to the same session. This prevents concurrent
1574
+ // sendMessageWithRetry calls that each send Ctrl+C on retry attempts,
1575
+ // which can kill the runtime when 25+ scheduled checks fire at once.
1576
+ const previousDelivery = this.sessionDeliveryMutex.get(sessionName);
1577
+ let releaseMutex;
1578
+ const currentDelivery = new Promise((r) => { releaseMutex = r; });
1579
+ this.sessionDeliveryMutex.set(sessionName, currentDelivery);
1580
+ if (previousDelivery) {
1581
+ this.logger.info('Waiting for in-flight delivery to complete before sending', {
1582
+ sessionName,
1583
+ messageLength: message.length,
1584
+ });
1585
+ await previousDelivery.catch(() => { });
1586
+ }
1494
1587
  try {
1495
1588
  if (!message || typeof message !== 'string') {
1496
1589
  return {
@@ -1498,6 +1591,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1498
1591
  error: 'Message is required and must be a string',
1499
1592
  };
1500
1593
  }
1594
+ // Auto-resolve runtime type if not provided.
1595
+ // CRITICAL: defaulting to CLAUDE_CODE is dangerous because
1596
+ // Ctrl+C cleanup (Claude Code behavior) triggers /quit on Gemini CLI.
1597
+ if (!runtimeType) {
1598
+ runtimeType = await this.resolveSessionRuntimeType(sessionName);
1599
+ }
1501
1600
  // Get session helper once for this method
1502
1601
  const sessionHelper = await this.getSessionHelper();
1503
1602
  // Check if session exists
@@ -1549,6 +1648,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
1549
1648
  error: errorMessage,
1550
1649
  };
1551
1650
  }
1651
+ finally {
1652
+ releaseMutex();
1653
+ if (this.sessionDeliveryMutex.get(sessionName) === currentDelivery) {
1654
+ this.sessionDeliveryMutex.delete(sessionName);
1655
+ }
1656
+ }
1552
1657
  }
1553
1658
  /**
1554
1659
  * Wait for an agent session to be at a ready prompt before sending messages.
@@ -2275,8 +2380,14 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2275
2380
  const hasGeminiIndicators = newContent.length > 0
2276
2381
  && /reading|thinking|processing|analyzing|generating|searching/i.test(newContent);
2277
2382
  const significantLengthChange = Math.abs(lengthDiff) > 10;
2383
+ // For Gemini CLI, contentChanged alone is sufficient evidence of
2384
+ // delivery. The TUI redraws minimally (lengthDiff can be as low as
2385
+ // 1-2 chars) when processing starts, so requiring significantLengthChange
2386
+ // causes false "stuck" detection. False "stuck" + Ctrl+C kills the CLI.
2387
+ const isGemini = runtimeType === RUNTIME_TYPES.GEMINI_CLI;
2278
2388
  const delivered = (lengthDiff > 20)
2279
2389
  || (contentChanged && significantLengthChange)
2390
+ || (contentChanged && isGemini)
2280
2391
  || hasProcessingIndicators
2281
2392
  || hasGeminiIndicators;
2282
2393
  if (delivered) {
@@ -2311,31 +2422,18 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2311
2422
  await sessionHelper.clearCurrentCommandLine(sessionName);
2312
2423
  }
2313
2424
  else {
2314
- // Gemini CLI retry cleanup: be careful with cleanup keystrokes.
2315
- // If output changed (contentChanged=true), text likely reached the
2316
- // input box but wasn't submitted Ctrl+C safely clears it.
2317
- // If output did NOT change at all, the TUI input is likely defocused
2318
- // and the input box is EMPTY. Ctrl+C on an empty Gemini CLI prompt
2319
- // triggers /quit and exits the CLI entirely.
2320
- const noOutputChange = !isClaudeCode && beforeOutput === sessionHelper.capturePane(sessionName, 20);
2321
- if (noOutputChange) {
2322
- this.logger.warn('No output change detected — TUI input likely defocused, using Tab to cycle Ink focus', {
2323
- sessionName,
2324
- attempt,
2325
- });
2326
- // Tab triggers focusNext() in Ink's FocusContext, which should
2327
- // cycle focus back to the InputPrompt component even when it's
2328
- // defocused. This is more reliable than Enter (which is silently
2329
- // consumed when no component is focused).
2330
- await sessionHelper.sendKey(sessionName, 'Tab');
2331
- await delay(300);
2332
- await sessionHelper.sendEnter(sessionName);
2333
- await delay(300);
2334
- }
2335
- else {
2336
- await sessionHelper.sendCtrlC(sessionName);
2337
- await delay(200);
2338
- }
2425
+ // Gemini CLI retry cleanup: NEVER send Ctrl+C it triggers /quit
2426
+ // and kills the CLI entirely, regardless of whether text is in the
2427
+ // input box or not. Use Tab to cycle Ink focus back to the input
2428
+ // prompt, then Enter to submit any pending text.
2429
+ this.logger.warn('Gemini CLI message stuck using Tab focus recovery (no Ctrl+C)', {
2430
+ sessionName,
2431
+ attempt,
2432
+ });
2433
+ await sessionHelper.sendKey(sessionName, 'Tab');
2434
+ await delay(300);
2435
+ await sessionHelper.sendEnter(sessionName);
2436
+ await delay(300);
2339
2437
  }
2340
2438
  await delay(SESSION_COMMAND_DELAYS.CLEAR_COMMAND_DELAY);
2341
2439
  if (attempt < maxAttempts) {
@@ -2950,7 +3048,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2950
3048
  const isClaudeCode = runtimeType === RUNTIME_TYPES.CLAUDE_CODE;
2951
3049
  const sessionHelper = await this.getSessionHelper();
2952
3050
  // Step 1: Write prompt to a file (idempotent — may already exist from pre-launch write).
2953
- const promptFilePath = await this.writePromptFile(sessionName, prompt, runtimeType);
3051
+ const promptFilePath = await this.writePromptFile(sessionName, prompt);
2954
3052
  if (!promptFilePath) {
2955
3053
  // File write failure is fatal — all runtimes use the file-read approach.
2956
3054
  return false;
@@ -2964,12 +3062,16 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2964
3062
  const messageToSend = isClaudeCode
2965
3063
  ? 'Begin your work now. Follow the instructions you were given and start by doing an initial assessment of the project.'
2966
3064
  : `Read the file at ${promptFilePath} and follow all instructions in it.`;
2967
- // Claude Code: single attempt only. The full prompt is loaded via
2968
- // --append-system-prompt-file, so the kickoff is just a short trigger.
2969
- // Retrying causes duplicates because Claude returns to its prompt between
2970
- // tool calls, making retry checks think the first message wasn't received.
2971
- // Gemini CLI / other runtimes: up to 3 attempts (prompt loaded via file-read).
2972
- const maxAttempts = isClaudeCode ? 1 : 3;
3065
+ // Single attempt for all runtimes. Retrying causes duplicate messages because:
3066
+ // - Claude Code: returns to its prompt between tool calls, making retry
3067
+ // checks think the first message wasn't received.
3068
+ // - Gemini CLI: the echoed message "Read the file at..." starts with `> `
3069
+ // which isClaudeAtPrompt misdetects as an idle prompt, and the TUI's
3070
+ // input box shows `> ` even during processing. Both cause false-negative
3071
+ // delivery verification and unnecessary retries.
3072
+ // The sendMessage helper reliably writes to the PTY — if it succeeds,
3073
+ // the message was delivered.
3074
+ const maxAttempts = 1;
2973
3075
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2974
3076
  // Check abort before each attempt
2975
3077
  if (abortSignal?.aborted) {
@@ -2977,7 +3079,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2977
3079
  return false;
2978
3080
  }
2979
3081
  try {
2980
- this.logger.debug('Sending prompt to agent', {
3082
+ this.logger.info('Sending prompt to agent', {
2981
3083
  sessionName,
2982
3084
  attempt,
2983
3085
  runtimeType,
@@ -2990,13 +3092,23 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
2990
3092
  return false;
2991
3093
  }
2992
3094
  // Clear any pending input before sending the instruction (first attempt only).
2993
- // Claude Code: Escape closes slash menus + Ctrl+U clears line.
2994
- // Gemini CLI (Ink TUI): Do NOT send any cleanup keystrokes.
2995
- if (isClaudeCode && attempt === 1) {
2996
- await sessionHelper.sendEscape(sessionName);
2997
- await delay(200);
2998
- await sessionHelper.sendKey(sessionName, 'C-u');
2999
- await delay(300);
3095
+ if (attempt === 1) {
3096
+ if (isClaudeCode) {
3097
+ // Claude Code: Escape closes slash menus + Ctrl+U clears line.
3098
+ await sessionHelper.sendEscape(sessionName);
3099
+ await delay(200);
3100
+ await sessionHelper.sendKey(sessionName, 'C-u');
3101
+ await delay(300);
3102
+ }
3103
+ else {
3104
+ // Gemini CLI (Ink TUI): After /directory add processing, the TUI
3105
+ // input may lose focus or have stale invisible characters.
3106
+ // Send Enter to flush any residual input, then wait for the
3107
+ // prompt to settle before sending the real instruction.
3108
+ this.logger.debug('Gemini CLI pre-send: flushing input with Enter', { sessionName });
3109
+ await sessionHelper.sendEnter(sessionName);
3110
+ await delay(1000);
3111
+ }
3000
3112
  }
3001
3113
  // Check abort right before writing instruction to terminal
3002
3114
  if (abortSignal?.aborted) {
@@ -3039,27 +3151,17 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
3039
3151
  });
3040
3152
  return false;
3041
3153
  }
3042
- // Gemini CLI / other runtimes: original verification with length comparison
3043
- // Capture state before sending was skipped for Claude Code path above,
3044
- // so capture it here for non-Claude runtimes on retry.
3045
- const verifyDelay = 3000;
3046
- await delay(verifyDelay);
3047
- if (abortSignal?.aborted)
3048
- return false;
3049
- const afterOutput = sessionHelper.capturePane(sessionName, 20);
3050
- const hasProcessingIndicators = /thinking|processing|analyzing|registering|reading/i.test(afterOutput);
3051
- if (hasProcessingIndicators) {
3052
- this.logger.debug('Prompt instruction delivered successfully', {
3053
- sessionName, attempt, runtimeType,
3054
- });
3055
- return true;
3056
- }
3057
- this.logger.debug('Prompt instruction delivery unconfirmed - retrying', {
3154
+ // Gemini CLI / other runtimes: trust the PTY delivery.
3155
+ // sendMessage writes directly to the PTY if it didn't throw,
3156
+ // the message was delivered. Terminal-based verification is unreliable
3157
+ // because Gemini's TUI always shows `> ` (even during processing)
3158
+ // and our echoed message starts with `> `, both causing false
3159
+ // "at prompt" detection.
3160
+ this.logger.info('Prompt instruction sent to PTY (trusting delivery)', {
3058
3161
  sessionName, attempt, runtimeType,
3162
+ messageLength: messageToSend.length,
3059
3163
  });
3060
- if (attempt < maxAttempts) {
3061
- await delay(1000);
3062
- }
3164
+ return true;
3063
3165
  }
3064
3166
  catch (error) {
3065
3167
  this.logger.error('Error during prompt instruction delivery', {