crewly 1.20.199 → 1.20.201

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 (114) hide show
  1. package/config/constants.ts +2 -0
  2. package/config/skills/agent/screenshot-compare/execute.sh +8 -2
  3. package/config/skills/agent/screenshot-compare/execute.test.sh +77 -0
  4. package/config/skills/agent/transcribe-audio/SKILL.md +1 -1
  5. package/config/skills/agent/transcribe-audio/execute.sh +13 -4
  6. package/config/skills/agent/transcribe-audio/execute.test.sh +39 -0
  7. package/config/skills/agent/transcribe-audio/instructions.md +1 -1
  8. package/config/skills/registry.json +3 -3
  9. package/dist/backend/backend/src/constants.d.ts +28 -4
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +27 -4
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +32 -10
  14. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +47 -16
  16. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +20 -7
  19. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/session/session.controller.d.ts +3 -1
  21. package/dist/backend/backend/src/controllers/session/session.controller.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/session/session.controller.js +7 -2
  23. package/dist/backend/backend/src/controllers/session/session.controller.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/session/session.routes.d.ts.map +1 -1
  25. package/dist/backend/backend/src/controllers/session/session.routes.js +15 -4
  26. package/dist/backend/backend/src/controllers/session/session.routes.js.map +1 -1
  27. package/dist/backend/backend/src/controllers/settings/settings.controller.d.ts.map +1 -1
  28. package/dist/backend/backend/src/controllers/settings/settings.controller.js +91 -16
  29. package/dist/backend/backend/src/controllers/settings/settings.controller.js.map +1 -1
  30. package/dist/backend/backend/src/index.d.ts.map +1 -1
  31. package/dist/backend/backend/src/index.js +4 -0
  32. package/dist/backend/backend/src/index.js.map +1 -1
  33. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +52 -0
  34. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  35. package/dist/backend/backend/src/services/agent/agent-registration.service.js +125 -11
  36. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  37. package/dist/backend/backend/src/services/agent/antigravity-runtime.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/agent/antigravity-runtime.service.js +3 -2
  39. package/dist/backend/backend/src/services/agent/antigravity-runtime.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts +17 -0
  41. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts.map +1 -1
  42. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js +23 -0
  43. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js.map +1 -1
  44. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.d.ts +14 -1
  45. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.d.ts.map +1 -1
  46. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.js +23 -1
  47. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.js.map +1 -1
  48. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +1 -0
  50. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  51. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts +10 -0
  52. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/session/pty/pty-session.js +22 -0
  54. package/dist/backend/backend/src/services/session/pty/pty-session.js.map +1 -1
  55. package/dist/backend/backend/src/services/session/session-command-helper.d.ts +109 -15
  56. package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/session/session-command-helper.js +244 -41
  58. package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
  59. package/dist/backend/backend/src/services/session/tui-input-guard.d.ts +57 -0
  60. package/dist/backend/backend/src/services/session/tui-input-guard.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/session/tui-input-guard.js +107 -5
  62. package/dist/backend/backend/src/services/session/tui-input-guard.js.map +1 -1
  63. package/dist/backend/backend/src/types/settings.types.d.ts +24 -0
  64. package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
  65. package/dist/backend/backend/src/types/settings.types.js +65 -3
  66. package/dist/backend/backend/src/types/settings.types.js.map +1 -1
  67. package/dist/backend/build-info.json +2 -2
  68. package/dist/backend/config/constants.d.ts +2 -0
  69. package/dist/backend/config/constants.d.ts.map +1 -1
  70. package/dist/backend/config/constants.js +2 -0
  71. package/dist/backend/config/constants.js.map +1 -1
  72. package/dist/cli/backend/src/constants.d.ts +28 -4
  73. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  74. package/dist/cli/backend/src/constants.js +27 -4
  75. package/dist/cli/backend/src/constants.js.map +1 -1
  76. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +52 -0
  77. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  78. package/dist/cli/backend/src/services/agent/agent-registration.service.js +125 -11
  79. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  80. package/dist/cli/backend/src/services/agent/antigravity-runtime.service.d.ts.map +1 -1
  81. package/dist/cli/backend/src/services/agent/antigravity-runtime.service.js +3 -2
  82. package/dist/cli/backend/src/services/agent/antigravity-runtime.service.js.map +1 -1
  83. package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.d.ts +17 -0
  84. package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.d.ts.map +1 -1
  85. package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.js +23 -0
  86. package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.js.map +1 -1
  87. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.d.ts +14 -1
  88. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.d.ts.map +1 -1
  89. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.js +23 -1
  90. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.js.map +1 -1
  91. package/dist/cli/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
  92. package/dist/cli/backend/src/services/session/pty/pty-session-backend.js +1 -0
  93. package/dist/cli/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  94. package/dist/cli/backend/src/services/session/pty/pty-session.d.ts +10 -0
  95. package/dist/cli/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
  96. package/dist/cli/backend/src/services/session/pty/pty-session.js +22 -0
  97. package/dist/cli/backend/src/services/session/pty/pty-session.js.map +1 -1
  98. package/dist/cli/backend/src/services/session/session-command-helper.d.ts +109 -15
  99. package/dist/cli/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  100. package/dist/cli/backend/src/services/session/session-command-helper.js +244 -41
  101. package/dist/cli/backend/src/services/session/session-command-helper.js.map +1 -1
  102. package/dist/cli/backend/src/services/session/tui-input-guard.d.ts +57 -0
  103. package/dist/cli/backend/src/services/session/tui-input-guard.d.ts.map +1 -1
  104. package/dist/cli/backend/src/services/session/tui-input-guard.js +107 -5
  105. package/dist/cli/backend/src/services/session/tui-input-guard.js.map +1 -1
  106. package/dist/cli/backend/src/types/settings.types.d.ts +24 -0
  107. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
  108. package/dist/cli/backend/src/types/settings.types.js +65 -3
  109. package/dist/cli/backend/src/types/settings.types.js.map +1 -1
  110. package/dist/cli/config/constants.d.ts +2 -0
  111. package/dist/cli/config/constants.d.ts.map +1 -1
  112. package/dist/cli/config/constants.js +2 -0
  113. package/dist/cli/config/constants.js.map +1 -1
  114. package/package.json +1 -1
@@ -11,8 +11,8 @@ import { RuntimeServiceFactory } from './runtime-service.factory.js';
11
11
  import { OrcReplyRouteService } from '../orc/orc-reply-route.service.js';
12
12
  import { registerInProcessRuntime, unregisterInProcessRuntime, } from './crewly-agent/in-process-runtime-registry.js';
13
13
  import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
14
- import { CREWLY_CONSTANTS, ENV_CONSTANTS, OWNER_AUTH_CONSTANTS, AGENT_TIMEOUTS, ORCHESTRATOR_SESSION_NAME, ORCHESTRATOR_ROLE, RUNTIME_TYPES, SESSION_COMMAND_DELAYS, SESSION_RECREATION_CONSTANTS, EVENT_DELIVERY_CONSTANTS, TERMINAL_PATTERNS, GEMINI_SHELL_MODE_CONSTANTS, GEMINI_STUCK_CONNECTIVITY_PATTERN, GEMINI_ERROR_STATE_CONSTANTS, REGISTRATION_DELIVERY_CONSTANTS, ORC_CONVERSATION_CONSTANTS, SLACK_THREAD_KEY_CONSTANTS, OPEN_ITEMS_CONSTANTS, SPEND_CAP_CONSTANTS, RUNTIME_INPUT_SAFETY, TUI_INPUT_GUARD, } from '../../constants.js';
15
- import { TuiInputGuardError } from '../session/tui-input-guard.js';
14
+ import { CREWLY_CONSTANTS, ENV_CONSTANTS, OWNER_AUTH_CONSTANTS, AGENT_TIMEOUTS, ORCHESTRATOR_SESSION_NAME, ORCHESTRATOR_ROLE, RUNTIME_TYPES, SESSION_COMMAND_DELAYS, SUB_AGENT_QUEUE_CONSTANTS, SESSION_RECREATION_CONSTANTS, EVENT_DELIVERY_CONSTANTS, TERMINAL_PATTERNS, GEMINI_SHELL_MODE_CONSTANTS, GEMINI_STUCK_CONNECTIVITY_PATTERN, GEMINI_ERROR_STATE_CONSTANTS, REGISTRATION_DELIVERY_CONSTANTS, ORC_CONVERSATION_CONSTANTS, SLACK_THREAD_KEY_CONSTANTS, OPEN_ITEMS_CONSTANTS, SPEND_CAP_CONSTANTS, RUNTIME_INPUT_SAFETY, TUI_INPUT_GUARD, } from '../../constants.js';
15
+ import { TuiInputGuardError, screenShowsTurnInProgress } from '../session/tui-input-guard.js';
16
16
  import { InputBlockedRetryService } from '../messaging/input-blocked-retry.service.js';
17
17
  import { extractSlackThreadKeys, formatSlackThreadKey } from '../slack/slack-thread-key.js';
18
18
  import { delay } from '../../utils/async.utils.js';
@@ -162,6 +162,17 @@ export class AgentRegistrationService {
162
162
  // box was unreadable or held text we did not write. Such a message is
163
163
  // kept queued and retried, never dropped (2026-10-03 review).
164
164
  lastGuardRefusal = new Map();
165
+ /**
166
+ * Sessions whose last delivery attempt found a busy Claude Code (its
167
+ * "esc to interrupt" bar on screen) and did not paste: the message is
168
+ * queued and delivered when the agent goes idle (2026-10-03 Ella: a paste
169
+ * into a busy box rendered late, got no Enter and blocked the box).
170
+ */
171
+ busyHold = new Set();
172
+ /** When messages to a session started being held for a busy agent (first hold of the episode) */
173
+ busyHoldSince = new Map();
174
+ /** Re-check timers for messages held for a busy agent */
175
+ busyHoldTimers = new Map();
165
176
  // Consecutive guard refusals per session, for escalation.
166
177
  guardRefusalCount = new Map();
167
178
  // In-process Crewly Agent runtimes (sessionName → runtime instance).
@@ -1674,7 +1685,7 @@ export class AgentRegistrationService {
1674
1685
  await delay(pollMs);
1675
1686
  continue;
1676
1687
  }
1677
- if (runtimeService.isReadyForInput(screen)) {
1688
+ if (runtimeService.isReadyForInput(screen) || this.claudeIdlePromptFromBox(sessionHelper, sessionName, screen, runtimeType) === true) {
1678
1689
  this.logger.info('Runtime idle at input prompt — delivering registration instruction', {
1679
1690
  sessionName,
1680
1691
  runtimeType,
@@ -4087,14 +4098,28 @@ Loop until done, blocked, or explicitly reassigned:
4087
4098
  const turnTracker = InFlightTurnTracker.getInstance();
4088
4099
  turnTracker.settle(sessionName);
4089
4100
  this.lastGuardRefusal.delete(sessionName);
4101
+ this.busyHold.delete(sessionName);
4090
4102
  const delivered = await this.sendMessageWithRetry(sessionName, message, maxDeliveryAttempts, runtimeType);
4091
4103
  const refusal = this.lastGuardRefusal.get(sessionName);
4104
+ if (!delivered && this.busyHold.delete(sessionName)) {
4105
+ SubAgentMessageQueue.getInstance().enqueue(sessionName, message);
4106
+ this.scheduleBusyHoldRecheck(sessionName);
4107
+ const since = this.busyHoldSince.get(sessionName) ?? { at: Date.now(), message };
4108
+ this.busyHoldSince.set(sessionName, since);
4109
+ InputBlockedRetryService.getInstance().noteBusyHold(sessionName, Date.now() - since.at, since.message);
4110
+ return {
4111
+ success: true,
4112
+ queued: true,
4113
+ message: '[AGENT_BUSY] Message queued for delivery when agent becomes idle',
4114
+ };
4115
+ }
4092
4116
  if (delivered) {
4093
4117
  // Never re-send a confirmed delivery (the scanner's re-queue).
4094
4118
  for (const e of this.sentMessageTracker.get(sessionName) ?? [])
4095
4119
  if (e.message === message)
4096
4120
  e.confirmed = true;
4097
4121
  this.guardRefusalCount.delete(sessionName);
4122
+ this.busyHoldSince.delete(sessionName);
4098
4123
  InputBlockedRetryService.getInstance().noteDelivered(sessionName);
4099
4124
  }
4100
4125
  if (!delivered && refusal) {
@@ -4243,7 +4268,7 @@ Loop until done, blocked, or explicitly reassigned:
4243
4268
  // idle-detection already refuses to suspend on this signal; delivery
4244
4269
  // now refuses to write on it.
4245
4270
  const currentOutput = sessionHelper.capturePane(sessionName);
4246
- if (this.isClaudeAtPrompt(currentOutput, runtimeType) && !(await this.isBusyByWorkingStatus(sessionName))) {
4271
+ if (this.isAtIdlePrompt(sessionHelper, sessionName, currentOutput, runtimeType) && !(await this.isBusyByWorkingStatus(sessionName))) {
4247
4272
  this.logger.debug('Agent already at prompt', { sessionName });
4248
4273
  return true;
4249
4274
  }
@@ -4278,7 +4303,7 @@ Loop until done, blocked, or explicitly reassigned:
4278
4303
  return;
4279
4304
  pollCount++;
4280
4305
  const output = sessionHelper.capturePane(sessionName);
4281
- if (this.isClaudeAtPrompt(output, runtimeType)) {
4306
+ if (this.isAtIdlePrompt(sessionHelper, sessionName, output, runtimeType)) {
4282
4307
  void this.isBusyByWorkingStatus(sessionName).then((busy) => {
4283
4308
  if (resolved || busy)
4284
4309
  return;
@@ -4296,7 +4321,7 @@ Loop until done, blocked, or explicitly reassigned:
4296
4321
  return;
4297
4322
  pollCount++;
4298
4323
  const output = sessionHelper.capturePane(sessionName, EVENT_DELIVERY_CONSTANTS.DEEP_SCAN_LINES);
4299
- if (this.isClaudeAtPrompt(output, runtimeType)) {
4324
+ if (this.isAtIdlePrompt(sessionHelper, sessionName, output, runtimeType)) {
4300
4325
  void this.isBusyByWorkingStatus(sessionName).then((busy) => {
4301
4326
  if (resolved || busy)
4302
4327
  return;
@@ -4319,7 +4344,7 @@ Loop until done, blocked, or explicitly reassigned:
4319
4344
  if (hasPromptInStream) {
4320
4345
  // Double-check with capturePane to avoid false positives from partial data
4321
4346
  const output = sessionHelper.capturePane(sessionName);
4322
- if (this.isClaudeAtPrompt(output, runtimeType)) {
4347
+ if (this.isAtIdlePrompt(sessionHelper, sessionName, output, runtimeType)) {
4323
4348
  this.logger.debug('Agent at prompt (detected via stream)', { sessionName });
4324
4349
  cleanup();
4325
4350
  resolve(true);
@@ -4328,6 +4353,33 @@ Loop until done, blocked, or explicitly reassigned:
4328
4353
  });
4329
4354
  });
4330
4355
  }
4356
+ /**
4357
+ * Messages held for a busy agent are normally sent when it goes idle
4358
+ * (agent:idle drains the queue). A busy screen the activity monitor has
4359
+ * not yet registered produces no idle transition, so the queue is also
4360
+ * retried on a timer until it is empty. Each retry re-checks: still busy
4361
+ * means held again.
4362
+ *
4363
+ * @param sessionName - The agent session
4364
+ */
4365
+ scheduleBusyHoldRecheck(sessionName) {
4366
+ if (this.busyHoldTimers.has(sessionName))
4367
+ return;
4368
+ const timer = setTimeout(() => {
4369
+ this.busyHoldTimers.delete(sessionName);
4370
+ const queue = SubAgentMessageQueue.getInstance();
4371
+ if (!queue.hasPending(sessionName))
4372
+ return;
4373
+ void queue
4374
+ .flush(sessionName, (data) => this.sendMessageToAgent(sessionName, data), SUB_AGENT_QUEUE_CONSTANTS.FLUSH_INTER_MESSAGE_DELAY)
4375
+ .catch((err) => this.logger.warn('Retrying messages held for a busy agent failed', {
4376
+ sessionName,
4377
+ error: err instanceof Error ? err.message : String(err),
4378
+ }));
4379
+ }, TUI_INPUT_GUARD.BUSY_HOLD_RECHECK_MS);
4380
+ timer.unref?.();
4381
+ this.busyHoldTimers.set(sessionName, timer);
4382
+ }
4331
4383
  /**
4332
4384
  * Send message with retry logic for reliable delivery to Claude Code.
4333
4385
  * Uses SessionCommandHelper.sendMessage() (proven two-step write pattern)
@@ -4390,6 +4442,17 @@ Loop until done, blocked, or explicitly reassigned:
4390
4442
  messageLength: message.length,
4391
4443
  runtimeType,
4392
4444
  });
4445
+ // Never paste into a busy Claude Code box: mid-turn it renders a
4446
+ // paste late, so the guard sees an empty box, presses no Enter, and
4447
+ // the paste then sits there. The message is queued instead and
4448
+ // delivered when the agent goes idle. Checked before the resize
4449
+ // below, which repaints the screen.
4450
+ const busyCheck = sessionHelper.isAgentBusy;
4451
+ if (isClaudeCode && typeof busyCheck === 'function' && await busyCheck.call(sessionHelper, sessionName)) {
4452
+ this.logger.info('Agent is mid-turn — not pasting into its input box; queued for when it is idle', { sessionName, attempt });
4453
+ this.busyHold.add(sessionName);
4454
+ return false;
4455
+ }
4393
4456
  // Verify agent is at prompt before sending
4394
4457
  const output = sessionHelper.capturePane(sessionName);
4395
4458
  // Gemini CLI stuck-connectivity guard (#128): If the CLI is in a
@@ -4403,7 +4466,7 @@ Loop until done, blocked, or explicitly reassigned:
4403
4466
  });
4404
4467
  return false;
4405
4468
  }
4406
- if (!this.isClaudeAtPrompt(output, runtimeType)) {
4469
+ if (!this.isAtIdlePrompt(sessionHelper, sessionName, output, runtimeType)) {
4407
4470
  if (attempt === maxAttempts) {
4408
4471
  // On the final attempt, check if the agent is DEFINITELY busy
4409
4472
  // before force-delivering. Use PTY idle time for robust
@@ -4687,7 +4750,7 @@ Loop until done, blocked, or explicitly reassigned:
4687
4750
  // duplicate is recoverable on the agent side; a silent loss
4688
4751
  // is not.
4689
4752
  if (attempt > 1) {
4690
- const notAtPrompt = !this.isClaudeAtPrompt(preWriteCheck, runtimeType);
4753
+ const notAtPrompt = !this.isAtIdlePrompt(sessionHelper, sessionName, preWriteCheck, runtimeType);
4691
4754
  if (notAtPrompt) {
4692
4755
  const msgSnippet = (message.length > 20
4693
4756
  ? message.substring(0, 80)
@@ -4708,8 +4771,7 @@ Loop until done, blocked, or explicitly reassigned:
4708
4771
  // 2. await delay(scaled) — waits for paste processing
4709
4772
  // 3. session.write('\r') — sends Enter separately
4710
4773
  // 4. await delay(KEY_DELAY) — waits for key processing
4711
- // Recorded marker: this path checks the box after delivery.
4712
- await sessionHelper.sendMessage(sessionName, message, { recordPasteMarker: true });
4774
+ await sessionHelper.sendMessage(sessionName, message);
4713
4775
  // Register for background stuck-detection safety net (all runtimes).
4714
4776
  // If progressive verification below misses an Enter drop, the
4715
4777
  // background scanner will catch it within 30s.
@@ -5575,6 +5637,58 @@ Loop until done, blocked, or explicitly reassigned:
5575
5637
  * @param runtimeType - The runtime type for pattern selection
5576
5638
  * @returns true if the agent appears to be at a prompt
5577
5639
  */
5640
+ /**
5641
+ * Claude Code's idle prompt read from the faint-free input box rather
5642
+ * than from screen text. The text check ({@link isClaudeAtPrompt}) wants
5643
+ * `❯` alone on its line, but an idle box shows a faint placeholder or
5644
+ * prompt suggestion (`❯ Try "edit <filepath> to..."`) that plain capture
5645
+ * includes — so idle read as "not at prompt" and deliveries went through
5646
+ * only on the final attempt — while a busy box with nothing typed reads
5647
+ * as a bare `❯`. Here: idle at the prompt = a readable Claude Code box
5648
+ * (bare or labelled top rule) that is empty or holds our own pending
5649
+ * paste, and no turn on screen (busy bar or spinner line).
5650
+ *
5651
+ * @param sessionHelper - Session helper (needs `readInputBox`)
5652
+ * @param sessionName - The session
5653
+ * @param screen - Captured screen text (for the turn-in-progress check)
5654
+ * @param runtimeType - The session's runtime, if known
5655
+ * @returns true/false when decided from the box; null when it cannot be
5656
+ * (another runtime, no styled capture, unreadable screen)
5657
+ */
5658
+ claudeIdlePromptFromBox(sessionHelper, sessionName, screen, runtimeType) {
5659
+ if (runtimeType !== undefined && runtimeType !== RUNTIME_TYPES.CLAUDE_CODE)
5660
+ return null;
5661
+ const read = sessionHelper?.readInputBox;
5662
+ if (typeof read !== 'function')
5663
+ return null;
5664
+ let reading;
5665
+ try {
5666
+ reading = read.call(sessionHelper, sessionName, '', 'recovery');
5667
+ }
5668
+ catch {
5669
+ return null;
5670
+ }
5671
+ if (!reading || reading.state === 'unknown' || reading.layout !== 'claude-code')
5672
+ return null;
5673
+ const tail = (screen || '').split('\n').slice(-TUI_INPUT_GUARD.BUSY_BAR_TAIL_LINES).join('\n');
5674
+ if (screenShowsTurnInProgress(tail))
5675
+ return false;
5676
+ return reading.state === 'empty' || reading.ownPasteMarker === true;
5677
+ }
5678
+ /**
5679
+ * Whether the agent is idle at its input prompt, for deciding when to
5680
+ * write: the input box for Claude Code ({@link claudeIdlePromptFromBox}),
5681
+ * the screen-text check otherwise.
5682
+ *
5683
+ * @param sessionHelper - Session helper
5684
+ * @param sessionName - The session
5685
+ * @param screen - Captured screen text
5686
+ * @param runtimeType - The session's runtime, if known
5687
+ * @returns True when idle at the prompt
5688
+ */
5689
+ isAtIdlePrompt(sessionHelper, sessionName, screen, runtimeType) {
5690
+ return this.claudeIdlePromptFromBox(sessionHelper, sessionName, screen, runtimeType) ?? this.isClaudeAtPrompt(screen, runtimeType);
5691
+ }
5578
5692
  /**
5579
5693
  * Whether ActivityMonitor says this agent is mid-task.
5580
5694
  *