crewly 1.20.59 → 1.20.60

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 (33) hide show
  1. package/dist/backend/backend/src/constants.d.ts +19 -0
  2. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  3. package/dist/backend/backend/src/constants.js +19 -0
  4. package/dist/backend/backend/src/constants.js.map +1 -1
  5. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  6. package/dist/backend/backend/src/controllers/slack/slack.controller.js +4 -0
  7. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  8. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +36 -0
  9. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  10. package/dist/backend/backend/src/services/agent/agent-registration.service.js +93 -15
  11. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  12. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +35 -21
  13. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
  14. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js +105 -5
  15. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
  16. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.d.ts +15 -0
  17. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.d.ts.map +1 -1
  18. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js +15 -0
  19. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js.map +1 -1
  20. package/dist/backend/build-info.json +2 -2
  21. package/dist/cli/backend/src/constants.d.ts +19 -0
  22. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  23. package/dist/cli/backend/src/constants.js +19 -0
  24. package/dist/cli/backend/src/constants.js.map +1 -1
  25. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +36 -0
  26. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  27. package/dist/cli/backend/src/services/agent/agent-registration.service.js +93 -15
  28. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  29. package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts +35 -21
  30. package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
  31. package/dist/cli/backend/src/services/cloud/cloud-sync.service.js +105 -5
  32. package/dist/cli/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
  33. package/package.json +1 -1
@@ -7,7 +7,7 @@ import { createSessionCommandHelper, getSessionBackendSync, createSessionBackend
7
7
  import { RuntimeServiceFactory } from './runtime-service.factory.js';
8
8
  import { registerInProcessRuntime, unregisterInProcessRuntime, } from './crewly-agent/in-process-runtime-registry.js';
9
9
  import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
10
- 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, GEMINI_STUCK_CONNECTIVITY_PATTERN, GEMINI_ERROR_STATE_CONSTANTS, REGISTRATION_DELIVERY_CONSTANTS, } from '../../constants.js';
10
+ import { CREWLY_CONSTANTS, ENV_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, } from '../../constants.js';
11
11
  import { WEB_CONSTANTS } from '../../../../config/constants.js';
12
12
  import { delay } from '../../utils/async.utils.js';
13
13
  import { buildRuntimeModelFlags } from '../../utils/runtime-model-flags.utils.js';
@@ -1474,7 +1474,10 @@ export class AgentRegistrationService {
1474
1474
  * Most aggressive approach - completely recreates the session from scratch
1475
1475
  */
1476
1476
  async tryFullRecreation(sessionName, role, timeout, projectPath, memberId, runtimeType = RUNTIME_TYPES.CLAUDE_CODE, runtimeFlags) {
1477
- // Kill existing session
1477
+ // Kill existing session. Same D2(a) rule as forceRecreate: a monitor entry
1478
+ // for the old incarnation must not outlive it, or it false-declares the
1479
+ // replacement PTY dead before startMonitoring() below replaces it.
1480
+ RuntimeExitMonitorService.getInstance().stopMonitoring(sessionName);
1478
1481
  await (await this.getSessionHelper()).killSession(sessionName);
1479
1482
  // Wait for cleanup
1480
1483
  await delay(1000);
@@ -1511,6 +1514,8 @@ export class AgentRegistrationService {
1511
1514
  sessionName,
1512
1515
  projectPath: orchestratorCwd,
1513
1516
  });
1517
+ // D3: let the shell print its prompt before the init sequence's Ctrl-C.
1518
+ await this.waitForShellReady(sessionName);
1514
1519
  // Initialize runtime for orchestrator using script (always fresh start)
1515
1520
  const runtimeService = this.createRuntimeService(runtimeType);
1516
1521
  const launchedAtMs = Date.now();
@@ -1559,8 +1564,17 @@ export class AgentRegistrationService {
1559
1564
  this.logger.debug('Runtime confirmed ready for orchestrator in Step 3', { sessionName, runtimeType });
1560
1565
  }
1561
1566
  else {
1562
- // For other roles, create basic session and initialize Claude (always fresh start)
1563
- await (await this.getSessionHelper()).createSession(sessionName, projectPath || process.cwd());
1567
+ // For other roles, create basic session and initialize Claude (always fresh start).
1568
+ // D1 (2026-09-21): spawn with the identity env exactly like the primary
1569
+ // path — this branch used to spawn env-less, so every agent that came
1570
+ // through Step 2 ran without CREWLY_SESSION_NAME (unattributed heartbeats
1571
+ // and channel replies).
1572
+ const recreationCwd = projectPath || process.cwd();
1573
+ await (await this.getSessionHelper()).createSession(sessionName, recreationCwd, {
1574
+ env: this.buildAgentIdentityEnv(sessionName, role, recreationCwd),
1575
+ });
1576
+ // D3: let the shell print its prompt before the init sequence's Ctrl-C.
1577
+ await this.waitForShellReady(sessionName);
1564
1578
  const runtimeService = this.createRuntimeService(runtimeType);
1565
1579
  const launchedAtMs = Date.now();
1566
1580
  await runtimeService.executeRuntimeInitScript(sessionName, projectPath, effectiveFlags, promptFilePath, agentName, sessionPlan.resumeSessionId ?? undefined);
@@ -2496,14 +2510,77 @@ Loop until done, blocked, or explicitly reassigned:
2496
2510
  });
2497
2511
  return;
2498
2512
  }
2499
- // Create new session for orchestrator
2500
- await (await this.getSessionHelper()).createSession(config.sessionName, config.projectPath
2501
- // windowName not used in PTY backend
2502
- );
2513
+ // Create new session for orchestrator — with the identity env (D1), the
2514
+ // same object the primary path spawns with. windowName not used in PTY backend.
2515
+ await (await this.getSessionHelper()).createSession(config.sessionName, config.projectPath, {
2516
+ env: this.buildAgentIdentityEnv(config.sessionName, ORCHESTRATOR_ROLE, config.projectPath),
2517
+ });
2503
2518
  this.logger.info('Orchestrator session created successfully', {
2504
2519
  sessionName: config.sessionName,
2505
2520
  });
2506
2521
  }
2522
+ /**
2523
+ * The identity environment every agent PTY is spawned with.
2524
+ *
2525
+ * One source of truth for the primary spawn path, the Step-2 full
2526
+ * recreation path and the orchestrator session: `CREWLY_SESSION_NAME`
2527
+ * is what every skill sends as `X-Agent-Session`, so a session spawned
2528
+ * without it produces unattributed heartbeats and channel replies. Passing
2529
+ * the env at spawn (rather than only typing `export`s afterwards) is what
2530
+ * makes it survive a shell that is still initialising.
2531
+ *
2532
+ * @param sessionName - PTY session name (also the agent's identity)
2533
+ * @param role - Agent role (orchestrator, developer, …)
2534
+ * @param cwd - Working directory the PTY is spawned in (exposed as CREWLY_PROJECT_PATH)
2535
+ * @returns Env map to pass as `createSession(..., { env })`
2536
+ */
2537
+ buildAgentIdentityEnv(sessionName, role, cwd) {
2538
+ return {
2539
+ [ENV_CONSTANTS.CREWLY_SESSION_NAME]: sessionName,
2540
+ [ENV_CONSTANTS.CREWLY_ROLE]: role,
2541
+ [ENV_CONSTANTS.CREWLY_API_URL]: `http://localhost:${WEB_CONSTANTS.PORTS.BACKEND}`,
2542
+ [ENV_CONSTANTS.CREWLY_PROJECT_PATH]: cwd,
2543
+ [ENV_CONSTANTS.CREWLY_INSTALL_DIR]: this.projectRoot,
2544
+ };
2545
+ }
2546
+ /**
2547
+ * Wait — bounded — for a freshly spawned shell to print its prompt before
2548
+ * anything is written to it.
2549
+ *
2550
+ * D3 (2026-09-21): Step 2 full recreation used to write the runtime init
2551
+ * sequence ~8 ms after spawn. That sequence leads with Ctrl-C, and a zsh that
2552
+ * young has not installed its interactive SIGINT handling, so the TTY-delivered
2553
+ * SIGINT ends the shell (exit 0, 11 ms after spawn). The primary path never
2554
+ * hits this only because it types five `export`s first (~0.5 s).
2555
+ *
2556
+ * Checks the buffer first (the prompt may have landed before we subscribe),
2557
+ * then waits for the first non-whitespace output up to
2558
+ * `SESSION_RECREATION_CONSTANTS.SHELL_READY_TIMEOUT_MS`. Never throws: on
2559
+ * timeout it logs a warning and lets the caller proceed, so a silent shell
2560
+ * degrades to today's behaviour rather than blocking recreation.
2561
+ *
2562
+ * @param sessionName - The just-created PTY session
2563
+ * @returns True when a prompt was seen, false when the window elapsed
2564
+ */
2565
+ async waitForShellReady(sessionName) {
2566
+ const { SHELL_READY_PATTERN, SHELL_READY_TIMEOUT_MS, SHELL_READY_CAPTURE_LINES } = SESSION_RECREATION_CONSTANTS;
2567
+ const helper = await this.getSessionHelper();
2568
+ try {
2569
+ if (SHELL_READY_PATTERN.test(helper.capturePane(sessionName, SHELL_READY_CAPTURE_LINES))) {
2570
+ return true;
2571
+ }
2572
+ await helper.waitForPattern(sessionName, SHELL_READY_PATTERN, SHELL_READY_TIMEOUT_MS);
2573
+ return true;
2574
+ }
2575
+ catch (error) {
2576
+ this.logger.warn('Fresh shell printed nothing within the readiness window — proceeding with runtime init anyway', {
2577
+ sessionName,
2578
+ timeoutMs: SHELL_READY_TIMEOUT_MS,
2579
+ error: error instanceof Error ? error.message : String(error),
2580
+ });
2581
+ return false;
2582
+ }
2583
+ }
2507
2584
  /**
2508
2585
  * Unified session creation that handles both orchestrator and team members
2509
2586
  * @param config Session configuration
@@ -2677,6 +2754,12 @@ Loop until done, blocked, or explicitly reassigned:
2677
2754
  // Skip recovery, kill immediately (used during server startup for stale sessions)
2678
2755
  this.logger.info('Session exists but forceRecreate is set, killing for clean restart', { sessionName });
2679
2756
  try {
2757
+ // D2(a) (2026-09-21): the previous incarnation's RuntimeExitMonitor
2758
+ // entry must go BEFORE the kill. It resolves the child by session
2759
+ // NAME and its 30s poll grace is long expired, so left alive it
2760
+ // judges the seconds-old replacement PTY (no runtime child yet)
2761
+ // dead and force-kills it — Step 1 then fails and Step 2 runs.
2762
+ RuntimeExitMonitorService.getInstance().stopMonitoring(sessionName);
2680
2763
  const runtimeService = this.createRuntimeService(runtimeType);
2681
2764
  runtimeService.clearDetectionCache(sessionName);
2682
2765
  await (await this.getSessionHelper()).killSession(sessionName);
@@ -2908,14 +2991,9 @@ Loop until done, blocked, or explicitly reassigned:
2908
2991
  // can be lost, and every skill then runs without
2909
2992
  // CREWLY_SESSION_NAME — no X-Agent-Session header, and
2910
2993
  // reply-channel fails with a misleading 404 (Think Tank, 2026-09-18).
2994
+ // Same env object as the Step-2 recreation path (buildAgentIdentityEnv).
2911
2995
  const createdSession = await sessionHelper.createSession(sessionName, cwdToUse, {
2912
- env: {
2913
- [ENV_CONSTANTS.CREWLY_SESSION_NAME]: sessionName,
2914
- [ENV_CONSTANTS.CREWLY_ROLE]: role,
2915
- [ENV_CONSTANTS.CREWLY_API_URL]: `http://localhost:${WEB_CONSTANTS.PORTS.BACKEND}`,
2916
- [ENV_CONSTANTS.CREWLY_PROJECT_PATH]: cwdToUse,
2917
- [ENV_CONSTANTS.CREWLY_INSTALL_DIR]: this.projectRoot,
2918
- },
2996
+ env: this.buildAgentIdentityEnv(sessionName, role, cwdToUse),
2919
2997
  });
2920
2998
  this.logger.info('PTY session created successfully', {
2921
2999
  sessionName,