crewly 1.0.12 → 1.1.1
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.
- package/config/constants.ts +6 -0
- package/config/roles/_common/memory-instructions.md +10 -1
- package/config/roles/architect/prompt.md +7 -0
- package/config/roles/backend-developer/prompt.md +7 -0
- package/config/roles/content-strategist/prompt.md +69 -0
- package/config/roles/designer/prompt.md +7 -0
- package/config/roles/developer/prompt.md +7 -0
- package/config/roles/frontend-developer/prompt.md +7 -0
- package/config/roles/fullstack-dev/prompt.md +7 -0
- package/config/roles/generalist/prompt.md +8 -1
- package/config/roles/orchestrator/prompt.md +16 -1
- package/config/roles/orchestrator/self-evolution.md +72 -0
- package/config/roles/product-manager/prompt.md +7 -0
- package/config/roles/qa/prompt.md +7 -0
- package/config/roles/qa-engineer/prompt.md +7 -0
- package/config/roles/sales/prompt.md +7 -0
- package/config/roles/support/prompt.md +7 -0
- package/config/roles/tpm/prompt.md +7 -0
- package/config/skills/agent/core/register-self/instructions.md +1 -1
- package/config/skills/agent/core/remember/instructions.md +3 -3
- package/config/skills/orchestrator/delegate-task/execute.sh +21 -1
- package/config/skills/orchestrator/delegate-task/instructions.md +1 -0
- package/config/skills/orchestrator/read-session-logs/execute.sh +14 -0
- package/config/skills/orchestrator/read-session-logs/instructions.md +33 -0
- package/config/skills/orchestrator/read-session-logs/skill.json +20 -0
- package/config/skills/orchestrator/read-system-logs/execute.sh +18 -0
- package/config/skills/orchestrator/read-system-logs/instructions.md +30 -0
- package/config/skills/orchestrator/read-system-logs/skill.json +20 -0
- package/config/skills/orchestrator/remember/instructions.md +1 -1
- package/config/skills/orchestrator/reply-slack/execute.sh +45 -2
- package/config/skills/orchestrator/reply-slack/instructions.md +30 -2
- package/config/skills/orchestrator/report-bug/execute.sh +49 -0
- package/config/skills/orchestrator/report-bug/instructions.md +30 -0
- package/config/skills/orchestrator/report-bug/skill.json +20 -0
- package/config/slack-app-manifest.json +37 -0
- package/dist/backend/backend/src/constants.d.ts +38 -2
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +44 -4
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/index.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/index.js +6 -0
- package/dist/backend/backend/src/controllers/index.js.map +1 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts +3 -0
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js +80 -0
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts +14 -0
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +74 -0
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/oauth/oauth.routes.d.ts +3 -0
- package/dist/backend/backend/src/controllers/oauth/oauth.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/oauth/oauth.routes.js +127 -0
- package/dist/backend/backend/src/controllers/oauth/oauth.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js +20 -34
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +29 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/system/scheduler.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/system/scheduler.controller.js +73 -6
- package/dist/backend/backend/src/controllers/system/scheduler.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-management/assignments.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-management/assignments.controller.js +9 -5
- package/dist/backend/backend/src/controllers/task-management/assignments.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/user/user.routes.d.ts +3 -0
- package/dist/backend/backend/src/controllers/user/user.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/user/user.routes.js +45 -0
- package/dist/backend/backend/src/controllers/user/user.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +1 -1
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/modules/terminal.routes.js +2 -0
- package/dist/backend/backend/src/routes/modules/terminal.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +24 -2
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +222 -101
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts +5 -1
- package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/codex-runtime.service.js +11 -17
- package/dist/backend/backend/src/services/agent/codex-runtime.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts +21 -0
- package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/context-window-monitor.service.js +157 -11
- package/dist/backend/backend/src/services/agent/context-window-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/gemini-runtime.service.d.ts +33 -2
- package/dist/backend/backend/src/services/agent/gemini-runtime.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/gemini-runtime.service.js +388 -20
- package/dist/backend/backend/src/services/agent/gemini-runtime.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +1 -1
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +25 -2
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts +17 -2
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +133 -16
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/core/config.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/config.service.js +3 -2
- package/dist/backend/backend/src/services/core/config.service.js.map +1 -1
- package/dist/backend/backend/src/services/core/logger.service.d.ts +1 -0
- package/dist/backend/backend/src/services/core/logger.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/logger.service.js +22 -8
- package/dist/backend/backend/src/services/core/logger.service.js.map +1 -1
- package/dist/backend/backend/src/services/mcp-client.d.ts.map +1 -1
- package/dist/backend/backend/src/services/mcp-client.js +9 -4
- package/dist/backend/backend/src/services/mcp-client.js.map +1 -1
- package/dist/backend/backend/src/services/mcp-server.d.ts +4 -2
- package/dist/backend/backend/src/services/mcp-server.d.ts.map +1 -1
- package/dist/backend/backend/src/services/mcp-server.js +77 -18
- package/dist/backend/backend/src/services/mcp-server.js.map +1 -1
- package/dist/backend/backend/src/services/memory/memory.service.d.ts +1 -1
- package/dist/backend/backend/src/services/memory/memory.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/memory/memory.service.js +10 -1
- package/dist/backend/backend/src/services/memory/memory.service.js.map +1 -1
- package/dist/backend/backend/src/services/memory/project-memory.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/memory/project-memory.service.js +11 -2
- package/dist/backend/backend/src/services/memory/project-memory.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.d.ts +13 -0
- package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.js +45 -0
- package/dist/backend/backend/src/services/messaging/adapters/discord-messenger.adapter.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.d.ts +13 -0
- package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.js +27 -0
- package/dist/backend/backend/src/services/messaging/adapters/slack-messenger.adapter.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts +13 -0
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js +43 -0
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/message-queue.service.js +17 -0
- package/dist/backend/backend/src/services/messaging/message-queue.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.d.ts +25 -0
- package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.js +2 -0
- package/dist/backend/backend/src/services/messaging/messenger-adapter.interface.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/messenger-registry.service.d.ts +14 -0
- package/dist/backend/backend/src/services/messaging/messenger-registry.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/messenger-registry.service.js +20 -0
- package/dist/backend/backend/src/services/messaging/messenger-registry.service.js.map +1 -0
- package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.d.ts +3 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.js +15 -2
- package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.d.ts +5 -0
- package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js +42 -2
- package/dist/backend/backend/src/services/orchestrator/orchestrator-restart.service.js.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts +39 -0
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +121 -1
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-terminal-buffer.d.ts +4 -0
- package/dist/backend/backend/src/services/session/pty/pty-terminal-buffer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-terminal-buffer.js +7 -3
- package/dist/backend/backend/src/services/session/pty/pty-terminal-buffer.js.map +1 -1
- package/dist/backend/backend/src/services/settings/settings.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/settings/settings.service.js +6 -0
- package/dist/backend/backend/src/services/settings/settings.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +2 -2
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +14 -0
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/services/user/user-identity.service.d.ts +42 -0
- package/dist/backend/backend/src/services/user/user-identity.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/user/user-identity.service.js +123 -0
- package/dist/backend/backend/src/services/user/user-identity.service.js.map +1 -0
- package/dist/backend/backend/src/services/workflow/message-scheduler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/message-scheduler.service.js +8 -1
- package/dist/backend/backend/src/services/workflow/message-scheduler.service.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts +21 -2
- package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/scheduler.service.js +109 -6
- package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
- package/dist/backend/backend/src/types/chat.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/chat.types.js +2 -3
- package/dist/backend/backend/src/types/chat.types.js.map +1 -1
- package/dist/backend/backend/src/types/index.d.ts +8 -0
- package/dist/backend/backend/src/types/index.d.ts.map +1 -1
- package/dist/backend/backend/src/types/index.js.map +1 -1
- package/dist/backend/backend/src/types/memory.types.d.ts +1 -1
- package/dist/backend/backend/src/types/memory.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/memory.types.js.map +1 -1
- package/dist/backend/backend/src/types/settings.types.d.ts +11 -0
- package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/settings.types.js +23 -1
- package/dist/backend/backend/src/types/settings.types.js.map +1 -1
- package/dist/backend/config/constants.d.ts +6 -0
- package/dist/backend/config/constants.d.ts.map +1 -1
- package/dist/backend/config/constants.js +6 -0
- package/dist/backend/config/constants.js.map +1 -1
- package/dist/backend/config/index.d.ts +3 -0
- package/dist/backend/config/index.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +38 -2
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +44 -4
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/core/config.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/config.service.js +3 -2
- package/dist/cli/backend/src/services/core/config.service.js.map +1 -1
- package/dist/cli/backend/src/services/core/logger.service.d.ts +1 -0
- package/dist/cli/backend/src/services/core/logger.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/logger.service.js +22 -8
- package/dist/cli/backend/src/services/core/logger.service.js.map +1 -1
- package/dist/cli/backend/src/services/mcp-server.d.ts +4 -2
- package/dist/cli/backend/src/services/mcp-server.d.ts.map +1 -1
- package/dist/cli/backend/src/services/mcp-server.js +77 -18
- package/dist/cli/backend/src/services/mcp-server.js.map +1 -1
- package/dist/cli/backend/src/services/memory/memory.service.d.ts +1 -1
- package/dist/cli/backend/src/services/memory/memory.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/memory/memory.service.js +10 -1
- package/dist/cli/backend/src/services/memory/memory.service.js.map +1 -1
- package/dist/cli/backend/src/services/memory/project-memory.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/memory/project-memory.service.js +11 -2
- package/dist/cli/backend/src/services/memory/project-memory.service.js.map +1 -1
- package/dist/cli/backend/src/types/chat.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/chat.types.js +2 -3
- package/dist/cli/backend/src/types/chat.types.js.map +1 -1
- package/dist/cli/backend/src/types/index.d.ts +8 -0
- package/dist/cli/backend/src/types/index.d.ts.map +1 -1
- package/dist/cli/backend/src/types/index.js.map +1 -1
- package/dist/cli/backend/src/types/memory.types.d.ts +1 -1
- package/dist/cli/backend/src/types/memory.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/memory.types.js.map +1 -1
- package/dist/cli/backend/src/types/settings.types.d.ts +11 -0
- package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/settings.types.js +23 -1
- package/dist/cli/backend/src/types/settings.types.js.map +1 -1
- package/dist/cli/cli/src/index.js +1 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/config/constants.d.ts +6 -0
- package/dist/cli/config/constants.d.ts.map +1 -1
- package/dist/cli/config/constants.js +6 -0
- package/dist/cli/config/constants.js.map +1 -1
- package/dist/cli/config/index.d.ts +3 -0
- package/dist/cli/config/index.d.ts.map +1 -1
- package/frontend/dist/assets/{index-0a245b0d.js → index-45eeea99.js} +2 -2
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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
|
|
832
|
-
const
|
|
833
|
-
const promptsDir =
|
|
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
|
*/
|
|
@@ -1245,7 +1323,13 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1245
1323
|
}
|
|
1246
1324
|
}
|
|
1247
1325
|
// Step 2: If runtime not detected or registration failed, try Ctrl+C restart
|
|
1326
|
+
// only for Claude. Gemini/Codex treat Ctrl+C as destructive at prompt
|
|
1327
|
+
// and can exit the runtime instead of recovering.
|
|
1248
1328
|
if (!recoverySuccess) {
|
|
1329
|
+
if (runtimeType !== RUNTIME_TYPES.CLAUDE_CODE) {
|
|
1330
|
+
this.logger.info('Skipping Ctrl+C recovery for non-Claude runtime; falling back to recreation', { sessionName, runtimeType });
|
|
1331
|
+
throw new Error(`${runtimeType} recovery requires full recreation`);
|
|
1332
|
+
}
|
|
1249
1333
|
this.logger.info('Runtime not detected or registration failed, attempting Ctrl+C restart', {
|
|
1250
1334
|
sessionName,
|
|
1251
1335
|
});
|
|
@@ -1291,7 +1375,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1291
1375
|
}
|
|
1292
1376
|
// Start context window monitoring for recovered non-orchestrator session
|
|
1293
1377
|
if (role !== ORCHESTRATOR_ROLE && config.teamId && config.memberId) {
|
|
1294
|
-
ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role);
|
|
1378
|
+
ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role, runtimeType);
|
|
1295
1379
|
}
|
|
1296
1380
|
return {
|
|
1297
1381
|
success: true,
|
|
@@ -1366,7 +1450,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1366
1450
|
const timeout = role === ORCHESTRATOR_ROLE
|
|
1367
1451
|
? AGENT_TIMEOUTS.ORCHESTRATOR_INITIALIZATION
|
|
1368
1452
|
: AGENT_TIMEOUTS.REGULAR_AGENT_INITIALIZATION;
|
|
1369
|
-
const initResult = await this.initializeAgentWithRegistration(sessionName, role, projectPath, timeout, memberId, runtimeType, runtimeFlags);
|
|
1453
|
+
const initResult = await this.initializeAgentWithRegistration(sessionName, role, projectPath, timeout, memberId, runtimeType, runtimeFlags, config.additionalAllowlistPaths);
|
|
1370
1454
|
if (!initResult.success) {
|
|
1371
1455
|
return {
|
|
1372
1456
|
success: false,
|
|
@@ -1376,7 +1460,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1376
1460
|
}
|
|
1377
1461
|
// Start context window monitoring for newly created non-orchestrator session
|
|
1378
1462
|
if (role !== ORCHESTRATOR_ROLE && config.teamId && config.memberId) {
|
|
1379
|
-
ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role);
|
|
1463
|
+
ContextWindowMonitorService.getInstance().startSessionMonitoring(sessionName, config.memberId, config.teamId, role, runtimeType);
|
|
1380
1464
|
}
|
|
1381
1465
|
return {
|
|
1382
1466
|
success: true,
|
|
@@ -1490,7 +1574,22 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1490
1574
|
* }
|
|
1491
1575
|
* ```
|
|
1492
1576
|
*/
|
|
1493
|
-
async sendMessageToAgent(sessionName, message, runtimeType
|
|
1577
|
+
async sendMessageToAgent(sessionName, message, runtimeType) {
|
|
1578
|
+
// Per-session delivery serialization: chain this delivery after any
|
|
1579
|
+
// in-flight delivery to the same session. This prevents concurrent
|
|
1580
|
+
// sendMessageWithRetry calls that each send Ctrl+C on retry attempts,
|
|
1581
|
+
// which can kill the runtime when 25+ scheduled checks fire at once.
|
|
1582
|
+
const previousDelivery = this.sessionDeliveryMutex.get(sessionName);
|
|
1583
|
+
let releaseMutex;
|
|
1584
|
+
const currentDelivery = new Promise((r) => { releaseMutex = r; });
|
|
1585
|
+
this.sessionDeliveryMutex.set(sessionName, currentDelivery);
|
|
1586
|
+
if (previousDelivery) {
|
|
1587
|
+
this.logger.info('Waiting for in-flight delivery to complete before sending', {
|
|
1588
|
+
sessionName,
|
|
1589
|
+
messageLength: message.length,
|
|
1590
|
+
});
|
|
1591
|
+
await previousDelivery.catch(() => { });
|
|
1592
|
+
}
|
|
1494
1593
|
try {
|
|
1495
1594
|
if (!message || typeof message !== 'string') {
|
|
1496
1595
|
return {
|
|
@@ -1498,6 +1597,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1498
1597
|
error: 'Message is required and must be a string',
|
|
1499
1598
|
};
|
|
1500
1599
|
}
|
|
1600
|
+
// Auto-resolve runtime type if not provided.
|
|
1601
|
+
// CRITICAL: defaulting to CLAUDE_CODE is dangerous because
|
|
1602
|
+
// Ctrl+C cleanup (Claude Code behavior) triggers /quit on Gemini CLI.
|
|
1603
|
+
if (!runtimeType) {
|
|
1604
|
+
runtimeType = await this.resolveSessionRuntimeType(sessionName);
|
|
1605
|
+
}
|
|
1501
1606
|
// Get session helper once for this method
|
|
1502
1607
|
const sessionHelper = await this.getSessionHelper();
|
|
1503
1608
|
// Check if session exists
|
|
@@ -1510,8 +1615,11 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1510
1615
|
// Guard: refuse to deliver if the runtime process has exited.
|
|
1511
1616
|
// When Claude Code exits (e.g. context exhaustion), the PTY shell
|
|
1512
1617
|
// stays alive. Writing to it would execute garbage as shell commands.
|
|
1513
|
-
const backend = sessionHelper.getBackend
|
|
1514
|
-
|
|
1618
|
+
const backend = typeof sessionHelper.getBackend === 'function'
|
|
1619
|
+
? sessionHelper.getBackend()
|
|
1620
|
+
: getSessionBackendSync();
|
|
1621
|
+
const childAlive = backend?.isChildProcessAlive?.(sessionName);
|
|
1622
|
+
if (childAlive === false) {
|
|
1515
1623
|
this.logger.error('Runtime process not alive, refusing to deliver message', {
|
|
1516
1624
|
sessionName,
|
|
1517
1625
|
messageLength: message.length,
|
|
@@ -1549,6 +1657,12 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
1549
1657
|
error: errorMessage,
|
|
1550
1658
|
};
|
|
1551
1659
|
}
|
|
1660
|
+
finally {
|
|
1661
|
+
releaseMutex();
|
|
1662
|
+
if (this.sessionDeliveryMutex.get(sessionName) === currentDelivery) {
|
|
1663
|
+
this.sessionDeliveryMutex.delete(sessionName);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1552
1666
|
}
|
|
1553
1667
|
/**
|
|
1554
1668
|
* Wait for an agent session to be at a ready prompt before sending messages.
|
|
@@ -2019,11 +2133,9 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2019
2133
|
await sessionHelper.sendEnter(sessionName);
|
|
2020
2134
|
await delay(500);
|
|
2021
2135
|
}
|
|
2022
|
-
// For Gemini CLI: detect and
|
|
2023
|
-
//
|
|
2024
|
-
//
|
|
2025
|
-
// interactive UI rather than the main input. Send Ctrl-C to clear these
|
|
2026
|
-
// states before writing the actual message.
|
|
2136
|
+
// For Gemini CLI: detect and gently escape interactive modes before
|
|
2137
|
+
// sending the message. Avoid Ctrl-C here — Gemini interprets it as
|
|
2138
|
+
// destructive cancellation/quit in some states.
|
|
2027
2139
|
if (!isClaudeCode) {
|
|
2028
2140
|
const preOutput = sessionHelper.capturePane(sessionName, 10);
|
|
2029
2141
|
const interactiveModePatterns = [
|
|
@@ -2034,12 +2146,14 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2034
2146
|
];
|
|
2035
2147
|
const inInteractiveMode = interactiveModePatterns.some(p => preOutput.includes(p));
|
|
2036
2148
|
if (inInteractiveMode) {
|
|
2037
|
-
this.logger.warn('Gemini CLI in interactive mode,
|
|
2149
|
+
this.logger.warn('Gemini CLI in interactive mode, using non-destructive recovery', {
|
|
2038
2150
|
sessionName,
|
|
2039
2151
|
detectedPattern: interactiveModePatterns.find(p => preOutput.includes(p)),
|
|
2040
2152
|
});
|
|
2041
|
-
await sessionHelper.sendKey(sessionName, '
|
|
2042
|
-
await
|
|
2153
|
+
await sessionHelper.sendKey(sessionName, 'Tab');
|
|
2154
|
+
await delay(250);
|
|
2155
|
+
await sessionHelper.sendEnter(sessionName);
|
|
2156
|
+
await delay(1000);
|
|
2043
2157
|
}
|
|
2044
2158
|
}
|
|
2045
2159
|
// Capture output BEFORE sending to detect changes for ALL runtimes.
|
|
@@ -2275,8 +2389,14 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2275
2389
|
const hasGeminiIndicators = newContent.length > 0
|
|
2276
2390
|
&& /reading|thinking|processing|analyzing|generating|searching/i.test(newContent);
|
|
2277
2391
|
const significantLengthChange = Math.abs(lengthDiff) > 10;
|
|
2392
|
+
// For Gemini CLI, contentChanged alone is sufficient evidence of
|
|
2393
|
+
// delivery. The TUI redraws minimally (lengthDiff can be as low as
|
|
2394
|
+
// 1-2 chars) when processing starts, so requiring significantLengthChange
|
|
2395
|
+
// causes false "stuck" detection. False "stuck" + Ctrl+C kills the CLI.
|
|
2396
|
+
const isGemini = runtimeType === RUNTIME_TYPES.GEMINI_CLI;
|
|
2278
2397
|
const delivered = (lengthDiff > 20)
|
|
2279
2398
|
|| (contentChanged && significantLengthChange)
|
|
2399
|
+
|| (contentChanged && isGemini)
|
|
2280
2400
|
|| hasProcessingIndicators
|
|
2281
2401
|
|| hasGeminiIndicators;
|
|
2282
2402
|
if (delivered) {
|
|
@@ -2311,31 +2431,18 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2311
2431
|
await sessionHelper.clearCurrentCommandLine(sessionName);
|
|
2312
2432
|
}
|
|
2313
2433
|
else {
|
|
2314
|
-
// Gemini CLI retry cleanup:
|
|
2315
|
-
//
|
|
2316
|
-
// input box
|
|
2317
|
-
//
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
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
|
-
}
|
|
2434
|
+
// Gemini CLI retry cleanup: NEVER send Ctrl+C — it triggers /quit
|
|
2435
|
+
// and kills the CLI entirely, regardless of whether text is in the
|
|
2436
|
+
// input box or not. Use Tab to cycle Ink focus back to the input
|
|
2437
|
+
// prompt, then Enter to submit any pending text.
|
|
2438
|
+
this.logger.warn('Gemini CLI message stuck — using Tab focus recovery (no Ctrl+C)', {
|
|
2439
|
+
sessionName,
|
|
2440
|
+
attempt,
|
|
2441
|
+
});
|
|
2442
|
+
await sessionHelper.sendKey(sessionName, 'Tab');
|
|
2443
|
+
await delay(300);
|
|
2444
|
+
await sessionHelper.sendEnter(sessionName);
|
|
2445
|
+
await delay(300);
|
|
2339
2446
|
}
|
|
2340
2447
|
await delay(SESSION_COMMAND_DELAYS.CLEAR_COMMAND_DELAY);
|
|
2341
2448
|
if (attempt < maxAttempts) {
|
|
@@ -2710,6 +2817,8 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2710
2817
|
return TERMINAL_PATTERNS.CLAUDE_CODE_PROMPT;
|
|
2711
2818
|
if (runtimeType === RUNTIME_TYPES.GEMINI_CLI)
|
|
2712
2819
|
return TERMINAL_PATTERNS.GEMINI_CLI_PROMPT;
|
|
2820
|
+
if (runtimeType === RUNTIME_TYPES.CODEX_CLI)
|
|
2821
|
+
return TERMINAL_PATTERNS.CODEX_CLI_PROMPT;
|
|
2713
2822
|
return TERMINAL_PATTERNS.PROMPT_STREAM;
|
|
2714
2823
|
}
|
|
2715
2824
|
/**
|
|
@@ -2730,6 +2839,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2730
2839
|
const tailSection = terminalOutput.slice(-2000);
|
|
2731
2840
|
const isGemini = runtimeType === RUNTIME_TYPES.GEMINI_CLI;
|
|
2732
2841
|
const isClaudeCode = runtimeType === RUNTIME_TYPES.CLAUDE_CODE;
|
|
2842
|
+
const isCodex = runtimeType === RUNTIME_TYPES.CODEX_CLI;
|
|
2733
2843
|
const streamPattern = this.getPromptPatternForRuntime(runtimeType);
|
|
2734
2844
|
// Check for prompt FIRST. Processing indicators like "thinking" or "analyzing"
|
|
2735
2845
|
// can appear in the agent's previous response text and persist in the terminal
|
|
@@ -2747,7 +2857,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2747
2857
|
// Strip TUI box-drawing borders (│, ┃, etc.) that Gemini CLI wraps around prompts
|
|
2748
2858
|
const stripped = trimmed.replace(/^[│┃|]+\s*/, '').replace(/\s*[│┃|]+$/, '');
|
|
2749
2859
|
// Claude Code prompts: ❯, ⏵, $ alone on a line
|
|
2750
|
-
if (!isGemini) {
|
|
2860
|
+
if (!isGemini && !isCodex) {
|
|
2751
2861
|
if (['❯', '⏵', '$'].some(ch => trimmed === ch || stripped === ch)) {
|
|
2752
2862
|
return true;
|
|
2753
2863
|
}
|
|
@@ -2760,7 +2870,14 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2760
2870
|
}
|
|
2761
2871
|
// Gemini CLI prompts: > or ! followed by space
|
|
2762
2872
|
if (!isClaudeCode) {
|
|
2763
|
-
if (
|
|
2873
|
+
if (isCodex) {
|
|
2874
|
+
// Codex prompt uses `›`; avoid plain `> ` to prevent false-positives
|
|
2875
|
+
// from markdown blockquotes in agent output.
|
|
2876
|
+
if (trimmed.startsWith('› ') || stripped.startsWith('› ')) {
|
|
2877
|
+
return true;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
else if (trimmed.startsWith('> ') || trimmed.startsWith('! ') ||
|
|
2764
2881
|
stripped.startsWith('> ') || stripped.startsWith('! ')) {
|
|
2765
2882
|
return true;
|
|
2766
2883
|
}
|
|
@@ -2950,7 +3067,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2950
3067
|
const isClaudeCode = runtimeType === RUNTIME_TYPES.CLAUDE_CODE;
|
|
2951
3068
|
const sessionHelper = await this.getSessionHelper();
|
|
2952
3069
|
// Step 1: Write prompt to a file (idempotent — may already exist from pre-launch write).
|
|
2953
|
-
const promptFilePath = await this.writePromptFile(sessionName, prompt
|
|
3070
|
+
const promptFilePath = await this.writePromptFile(sessionName, prompt);
|
|
2954
3071
|
if (!promptFilePath) {
|
|
2955
3072
|
// File write failure is fatal — all runtimes use the file-read approach.
|
|
2956
3073
|
return false;
|
|
@@ -2964,12 +3081,16 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2964
3081
|
const messageToSend = isClaudeCode
|
|
2965
3082
|
? 'Begin your work now. Follow the instructions you were given and start by doing an initial assessment of the project.'
|
|
2966
3083
|
: `Read the file at ${promptFilePath} and follow all instructions in it.`;
|
|
2967
|
-
//
|
|
2968
|
-
//
|
|
2969
|
-
//
|
|
2970
|
-
//
|
|
2971
|
-
//
|
|
2972
|
-
|
|
3084
|
+
// Single attempt for all runtimes. Retrying causes duplicate messages because:
|
|
3085
|
+
// - Claude Code: returns to its prompt between tool calls, making retry
|
|
3086
|
+
// checks think the first message wasn't received.
|
|
3087
|
+
// - Gemini CLI: the echoed message "Read the file at..." starts with `> `
|
|
3088
|
+
// which isClaudeAtPrompt misdetects as an idle prompt, and the TUI's
|
|
3089
|
+
// input box shows `> ` even during processing. Both cause false-negative
|
|
3090
|
+
// delivery verification and unnecessary retries.
|
|
3091
|
+
// The sendMessage helper reliably writes to the PTY — if it succeeds,
|
|
3092
|
+
// the message was delivered.
|
|
3093
|
+
const maxAttempts = 1;
|
|
2973
3094
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2974
3095
|
// Check abort before each attempt
|
|
2975
3096
|
if (abortSignal?.aborted) {
|
|
@@ -2977,7 +3098,7 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2977
3098
|
return false;
|
|
2978
3099
|
}
|
|
2979
3100
|
try {
|
|
2980
|
-
this.logger.
|
|
3101
|
+
this.logger.info('Sending prompt to agent', {
|
|
2981
3102
|
sessionName,
|
|
2982
3103
|
attempt,
|
|
2983
3104
|
runtimeType,
|
|
@@ -2990,13 +3111,23 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
2990
3111
|
return false;
|
|
2991
3112
|
}
|
|
2992
3113
|
// Clear any pending input before sending the instruction (first attempt only).
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3114
|
+
if (attempt === 1) {
|
|
3115
|
+
if (isClaudeCode) {
|
|
3116
|
+
// Claude Code: Escape closes slash menus + Ctrl+U clears line.
|
|
3117
|
+
await sessionHelper.sendEscape(sessionName);
|
|
3118
|
+
await delay(200);
|
|
3119
|
+
await sessionHelper.sendKey(sessionName, 'C-u');
|
|
3120
|
+
await delay(300);
|
|
3121
|
+
}
|
|
3122
|
+
else {
|
|
3123
|
+
// Gemini CLI (Ink TUI): After /directory add processing, the TUI
|
|
3124
|
+
// input may lose focus or have stale invisible characters.
|
|
3125
|
+
// Send Enter to flush any residual input, then wait for the
|
|
3126
|
+
// prompt to settle before sending the real instruction.
|
|
3127
|
+
this.logger.debug('Gemini CLI pre-send: flushing input with Enter', { sessionName });
|
|
3128
|
+
await sessionHelper.sendEnter(sessionName);
|
|
3129
|
+
await delay(1000);
|
|
3130
|
+
}
|
|
3000
3131
|
}
|
|
3001
3132
|
// Check abort right before writing instruction to terminal
|
|
3002
3133
|
if (abortSignal?.aborted) {
|
|
@@ -3039,27 +3170,17 @@ After checking in, just say "Ready for tasks" and wait for me to send you work.`
|
|
|
3039
3170
|
});
|
|
3040
3171
|
return false;
|
|
3041
3172
|
}
|
|
3042
|
-
// Gemini CLI / other runtimes:
|
|
3043
|
-
//
|
|
3044
|
-
//
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
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', {
|
|
3173
|
+
// Gemini CLI / other runtimes: trust the PTY delivery.
|
|
3174
|
+
// sendMessage writes directly to the PTY — if it didn't throw,
|
|
3175
|
+
// the message was delivered. Terminal-based verification is unreliable
|
|
3176
|
+
// because Gemini's TUI always shows `> ` (even during processing)
|
|
3177
|
+
// and our echoed message starts with `> `, both causing false
|
|
3178
|
+
// "at prompt" detection.
|
|
3179
|
+
this.logger.info('Prompt instruction sent to PTY (trusting delivery)', {
|
|
3058
3180
|
sessionName, attempt, runtimeType,
|
|
3181
|
+
messageLength: messageToSend.length,
|
|
3059
3182
|
});
|
|
3060
|
-
|
|
3061
|
-
await delay(1000);
|
|
3062
|
-
}
|
|
3183
|
+
return true;
|
|
3063
3184
|
}
|
|
3064
3185
|
catch (error) {
|
|
3065
3186
|
this.logger.error('Error during prompt instruction delivery', {
|