happy-imou-cloud 2.1.4 → 2.1.6

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 (28) hide show
  1. package/bin/happy-cloud.mjs +38 -38
  2. package/dist/{BaseReasoningProcessor-DgdsExMH.cjs → BaseReasoningProcessor-C3oDrA4i.cjs} +17 -4
  3. package/dist/{BaseReasoningProcessor-lTsZVuAU.mjs → BaseReasoningProcessor-CRXr7Axk.mjs} +16 -3
  4. package/dist/{ProviderSelectionHandler-CGTnB7ba.mjs → ProviderSelectionHandler-C3kHFqeq.mjs} +2 -2
  5. package/dist/{ProviderSelectionHandler-Bavm9TDG.cjs → ProviderSelectionHandler-Drg2Pp1-.cjs} +2 -2
  6. package/dist/{api-B6ESNpGB.cjs → api-CvtU4DI-.cjs} +2 -2
  7. package/dist/{api-l8X03rs-.mjs → api-DF9A136-.mjs} +3 -3
  8. package/dist/{command-DPLKOzMr.cjs → command-UZr1nodh.cjs} +3 -3
  9. package/dist/{command-BVCkEMtp.mjs → command-hO52qTzQ.mjs} +3 -3
  10. package/dist/{index-D72RMo5Z.mjs → index-B5e-MA1d.mjs} +268 -32
  11. package/dist/{index-D1BP-fEm.cjs → index-DA-K28E3.cjs} +264 -24
  12. package/dist/index.cjs +3 -3
  13. package/dist/index.mjs +3 -3
  14. package/dist/lib.cjs +1 -1
  15. package/dist/lib.d.cts +36 -36
  16. package/dist/lib.d.mts +36 -36
  17. package/dist/lib.mjs +1 -1
  18. package/dist/{persistence-CyFjFOlN.mjs → persistence-BsWBBi7E.mjs} +1 -1
  19. package/dist/{persistence-EDmI-c8T.cjs → persistence-CKgPuZRR.cjs} +1 -1
  20. package/dist/{registerKillSessionHandler-71xCO8e_.cjs → registerKillSessionHandler-3ytO-yBI.cjs} +72 -79
  21. package/dist/{registerKillSessionHandler-DAVhkb-l.mjs → registerKillSessionHandler-DmG1p8l7.mjs} +73 -78
  22. package/dist/{runClaude-BRhQLKjh.mjs → runClaude-MF34EsCp.mjs} +103 -40
  23. package/dist/{runClaude-DjnTGJGC.cjs → runClaude-bAlUdUGw.cjs} +106 -43
  24. package/dist/{runCodex-DUs_jBE-.mjs → runCodex-B31D_imZ.mjs} +10 -8
  25. package/dist/{runCodex-BHq7Rnq7.cjs → runCodex-CiIbJ1wa.cjs} +12 -10
  26. package/dist/{runGemini-pmvBZ6qU.mjs → runGemini-BJ7PxLyl.mjs} +5 -5
  27. package/dist/{runGemini-hkZeOnA_.cjs → runGemini-BZJR84o-.cjs} +7 -7
  28. package/package.json +2 -2
@@ -1,9 +1,9 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
- import { l as logger, e as encodeBase64, c as configuration, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders, q as decodeBase64, r as HAPPY_CLOUD_DAEMON_PORT, p as packageJson, A as ApiClient, t as HeadTailPreviewBuffer, u as getLatestDaemonLog } from './api-l8X03rs-.mjs';
3
- import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-CyFjFOlN.mjs';
2
+ import { l as logger, e as encodeBase64, c as configuration, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders, p as decodeBase64, q as HAPPY_CLOUD_DAEMON_PORT, r as packageJson, A as ApiClient, t as HeadTailPreviewBuffer, u as getLatestDaemonLog } from './api-DF9A136-.mjs';
3
+ import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-BsWBBi7E.mjs';
4
4
  import { z } from 'zod';
5
5
  import fs, { writeFile as writeFile$1, rename, unlink as unlink$1 } from 'fs/promises';
6
- import os, { homedir } from 'os';
6
+ import os$1, { homedir } from 'os';
7
7
  import * as tmp from 'tmp';
8
8
  import { randomUUID, randomBytes } from 'node:crypto';
9
9
  import tweetnacl from 'tweetnacl';
@@ -11,7 +11,7 @@ import axios from 'axios';
11
11
  import qrcode from 'qrcode-terminal';
12
12
  import { writeFile, unlink, readdir, readFile, mkdir } from 'node:fs/promises';
13
13
  import { createRequire } from 'node:module';
14
- import os$1, { tmpdir, homedir as homedir$1 } from 'node:os';
14
+ import os, { tmpdir, homedir as homedir$1 } from 'node:os';
15
15
  import path, { join, resolve as resolve$1, isAbsolute, delimiter, normalize, dirname as dirname$1 } from 'node:path';
16
16
  import open from 'open';
17
17
  import React, { useState } from 'react';
@@ -2499,6 +2499,128 @@ async function runDaemonHealthCheck({
2499
2499
  await writeHeartbeat();
2500
2500
  }
2501
2501
 
2502
+ async function closeProviderSession(session, opts = {}) {
2503
+ let firstError;
2504
+ const captureError = (error) => {
2505
+ if (firstError === void 0) {
2506
+ firstError = error;
2507
+ }
2508
+ };
2509
+ if (opts.archiveOnClose || opts.archiveReason) {
2510
+ try {
2511
+ session.updateMetadata((currentMetadata) => {
2512
+ const { archiveReason: _existingArchiveReason, ...metadataWithoutArchiveReason } = currentMetadata;
2513
+ return {
2514
+ ...metadataWithoutArchiveReason,
2515
+ lifecycleState: "archived",
2516
+ lifecycleStateSince: Date.now(),
2517
+ archivedBy: opts.archivedBy ?? "cli",
2518
+ ...opts.archiveReason ? { archiveReason: opts.archiveReason } : {}
2519
+ };
2520
+ });
2521
+ } catch (error) {
2522
+ captureError(error);
2523
+ }
2524
+ }
2525
+ try {
2526
+ session.sendSessionDeath();
2527
+ } catch (error) {
2528
+ captureError(error);
2529
+ }
2530
+ try {
2531
+ await session.flush();
2532
+ } catch (error) {
2533
+ captureError(error);
2534
+ }
2535
+ try {
2536
+ await session.close();
2537
+ } catch (error) {
2538
+ captureError(error);
2539
+ }
2540
+ if (firstError !== void 0) {
2541
+ throw firstError;
2542
+ }
2543
+ }
2544
+
2545
+ function createSessionMetadata(opts) {
2546
+ const state = {
2547
+ controlledByUser: false
2548
+ };
2549
+ const metadataPath = opts.path ?? process.cwd();
2550
+ const metadataHostPid = opts.hostPid === void 0 ? process.pid : opts.hostPid;
2551
+ const metadata = {
2552
+ path: metadataPath,
2553
+ host: os.hostname(),
2554
+ version: packageJson.version,
2555
+ os: os.platform(),
2556
+ machineId: opts.machineId,
2557
+ homeDir: os.homedir(),
2558
+ happyHomeDir: configuration.happyCloudHomeDir,
2559
+ happyLibDir: projectPath(),
2560
+ happyToolsDir: resolve$1(projectPath(), "tools", "unpacked"),
2561
+ startedFromDaemon: opts.startedBy === "daemon",
2562
+ startedBy: opts.startedBy || "terminal",
2563
+ lifecycleState: "running",
2564
+ lifecycleStateSince: Date.now(),
2565
+ flavor: opts.flavor,
2566
+ ...metadataHostPid == null ? {} : { hostPid: metadataHostPid }
2567
+ };
2568
+ return { state, metadata };
2569
+ }
2570
+
2571
+ async function archiveManagedSessionById(opts) {
2572
+ try {
2573
+ const sessionClient = opts.api.sessionSyncClient({ id: opts.sessionId });
2574
+ await closeProviderSession(sessionClient, {
2575
+ archiveReason: opts.archiveReason,
2576
+ archivedBy: "daemon"
2577
+ });
2578
+ } catch (error) {
2579
+ logger.debug(
2580
+ `[DAEMON RUN] Failed to archive managed session ${opts.sessionId}: ${opts.archiveReason}`,
2581
+ error
2582
+ );
2583
+ }
2584
+ }
2585
+ async function precreateDaemonManagedSession(opts) {
2586
+ const { state, metadata } = createSessionMetadata({
2587
+ flavor: opts.flavor,
2588
+ machineId: opts.machineId,
2589
+ startedBy: "daemon",
2590
+ path: opts.directory,
2591
+ hostPid: null
2592
+ });
2593
+ try {
2594
+ return await opts.api.getOrCreateSession({
2595
+ tag: opts.sessionTag,
2596
+ metadata,
2597
+ state
2598
+ });
2599
+ } catch (error) {
2600
+ logger.debug(`[DAEMON RUN] Failed to precreate ${opts.flavor} session for tag ${opts.sessionTag}`, error);
2601
+ return null;
2602
+ }
2603
+ }
2604
+ async function archivePrecreatedManagedSession(opts) {
2605
+ await archiveManagedSessionById({
2606
+ api: opts.api,
2607
+ sessionId: opts.session.id,
2608
+ archiveReason: opts.archiveReason
2609
+ });
2610
+ }
2611
+ async function archiveDetachedManagedSessionIfNeeded(opts) {
2612
+ const { trackedSession } = opts;
2613
+ if (trackedSession.startedBy !== "daemon" || !trackedSession.happySessionId || trackedSession.happySessionMetadataFromLocalWebhook) {
2614
+ return false;
2615
+ }
2616
+ await archiveManagedSessionById({
2617
+ api: opts.api,
2618
+ sessionId: trackedSession.happySessionId,
2619
+ archiveReason: opts.archiveReason
2620
+ });
2621
+ return true;
2622
+ }
2623
+
2502
2624
  const NON_SESSION_PROCESS_TYPES$1 = /* @__PURE__ */ new Set([
2503
2625
  "current",
2504
2626
  "daemon",
@@ -2821,6 +2943,19 @@ async function recoverTrackedSessionsFromLocalRegistry({
2821
2943
  return { recoveredCount, removedStaleCount };
2822
2944
  }
2823
2945
 
2946
+ const HAPPY_MANAGED_SESSION_TAG_ENV = "HAPPY_MANAGED_SESSION_TAG";
2947
+ function readManagedSessionTag(env = process.env) {
2948
+ const raw = env[HAPPY_MANAGED_SESSION_TAG_ENV];
2949
+ if (typeof raw !== "string") {
2950
+ return null;
2951
+ }
2952
+ const trimmed = raw.trim();
2953
+ return trimmed.length > 0 ? trimmed : null;
2954
+ }
2955
+ function resolveManagedSessionTag(env = process.env) {
2956
+ return readManagedSessionTag(env) ?? randomUUID();
2957
+ }
2958
+
2824
2959
  const DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS = 45e3;
2825
2960
  function resolveSessionWebhookTimeoutMs() {
2826
2961
  const parsed = Number.parseInt(process.env.HAPPY_DAEMON_SESSION_WEBHOOK_TIMEOUT || "", 10);
@@ -2830,10 +2965,10 @@ function resolveSessionWebhookTimeoutMs() {
2830
2965
  return DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS;
2831
2966
  }
2832
2967
  const initialMachineMetadata = {
2833
- host: os.hostname(),
2834
- platform: os.platform(),
2968
+ host: os$1.hostname(),
2969
+ platform: os$1.platform(),
2835
2970
  happyCliVersion: packageJson.version,
2836
- homeDir: os.homedir(),
2971
+ homeDir: os$1.homedir(),
2837
2972
  happyHomeDir: configuration.happyCloudHomeDir,
2838
2973
  happyLibDir: projectPath()
2839
2974
  };
@@ -2917,9 +3052,21 @@ async function startDaemon() {
2917
3052
  }
2918
3053
  const { credentials, machineId } = await authAndSetupMachineIfNeeded();
2919
3054
  logger.debug("[DAEMON RUN] Auth and machine setup complete");
3055
+ let api = null;
2920
3056
  const pidToTrackedSession = /* @__PURE__ */ new Map();
2921
3057
  const pidToAwaiter = /* @__PURE__ */ new Map();
2922
3058
  const getCurrentChildren = () => Array.from(pidToTrackedSession.values());
3059
+ const removeTrackedSession = (pid, archiveReason) => {
3060
+ const trackedSession = pidToTrackedSession.get(pid);
3061
+ if (trackedSession && api) {
3062
+ void archiveDetachedManagedSessionIfNeeded({
3063
+ api,
3064
+ trackedSession,
3065
+ archiveReason
3066
+ });
3067
+ }
3068
+ pidToTrackedSession.delete(pid);
3069
+ };
2923
3070
  const onHappySessionWebhook = (sessionId, sessionMetadata) => {
2924
3071
  logger.debugLargeJson(`[DAEMON RUN] Session reported`, sessionMetadata);
2925
3072
  const pid = sessionMetadata.hostPid;
@@ -2956,8 +3103,9 @@ async function startDaemon() {
2956
3103
  };
2957
3104
  const spawnSession = async (options) => {
2958
3105
  logger.debugLargeJson("[DAEMON RUN] Spawning session", options);
2959
- const { directory, sessionId, machineId: machineId2, approvedNewDirectoryCreation = true } = options;
3106
+ const { directory, sessionId, machineId: _requestedMachineId, approvedNewDirectoryCreation = true } = options;
2960
3107
  let directoryCreated = false;
3108
+ let precreatedCodexSession = null;
2961
3109
  try {
2962
3110
  await fs.access(directory);
2963
3111
  logger.debug(`[DAEMON RUN] Directory exists: ${directory}`);
@@ -3031,6 +3179,11 @@ async function startDaemon() {
3031
3179
  }
3032
3180
  let extraEnv = { ...profileEnv, ...authEnv };
3033
3181
  logger.debug(`[DAEMON RUN] Final environment variable keys (before expansion) (${Object.keys(extraEnv).length}): ${Object.keys(extraEnv).join(", ")}`);
3182
+ const managedSessionTag = options.agent === "codex" ? randomUUID() : null;
3183
+ if (managedSessionTag) {
3184
+ extraEnv[HAPPY_MANAGED_SESSION_TAG_ENV] = managedSessionTag;
3185
+ logger.debug(`[DAEMON RUN] Reserved managed Codex session tag ${managedSessionTag}`);
3186
+ }
3034
3187
  extraEnv = expandEnvironmentVariables(extraEnv, process.env);
3035
3188
  logger.debug(`[DAEMON RUN] After variable expansion: ${Object.keys(extraEnv).join(", ")}`);
3036
3189
  const potentialAuthVars = ["ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "OPENAI_API_KEY", "CODEX_HOME", "AZURE_OPENAI_API_KEY", "TOGETHER_API_KEY"];
@@ -3069,6 +3222,18 @@ async function startDaemon() {
3069
3222
  const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${buildDaemonSpawnArgs(agent).join(" ")}`;
3070
3223
  const windowName = `happy-${Date.now()}-${agent}`;
3071
3224
  const tmuxEnv = buildDaemonChildEnv(process.env, extraEnv);
3225
+ if (managedSessionTag && api) {
3226
+ precreatedCodexSession = await precreateDaemonManagedSession({
3227
+ api,
3228
+ sessionTag: managedSessionTag,
3229
+ machineId,
3230
+ directory,
3231
+ flavor: "codex"
3232
+ });
3233
+ if (precreatedCodexSession) {
3234
+ logger.debug(`[DAEMON RUN] Precreated Codex session ${precreatedCodexSession.id} before tmux spawn`);
3235
+ }
3236
+ }
3072
3237
  const tmuxResult = await tmux.spawnInTmux([fullCommand], {
3073
3238
  sessionName: tmuxSessionName,
3074
3239
  windowName,
@@ -3081,6 +3246,7 @@ async function startDaemon() {
3081
3246
  }
3082
3247
  const trackedSession = {
3083
3248
  startedBy: "daemon",
3249
+ happySessionId: precreatedCodexSession?.id,
3084
3250
  pid: tmuxResult.pid,
3085
3251
  // Real PID from tmux -P flag
3086
3252
  tmuxSessionId: tmuxResult.sessionId,
@@ -3088,6 +3254,13 @@ async function startDaemon() {
3088
3254
  message: directoryCreated ? `The path '${directory}' did not exist. We created a new folder and spawned a new session in tmux session '${tmuxSessionName}'. Use 'tmux attach -t ${tmuxSessionName}' to view the session.` : `Spawned new session in tmux session '${tmuxSessionName}'. Use 'tmux attach -t ${tmuxSessionName}' to view the session.`
3089
3255
  };
3090
3256
  pidToTrackedSession.set(tmuxResult.pid, trackedSession);
3257
+ if (precreatedCodexSession) {
3258
+ logger.debug(`[DAEMON RUN] Returning precreated Codex session ${precreatedCodexSession.id} without waiting for webhook`);
3259
+ return {
3260
+ type: "success",
3261
+ sessionId: precreatedCodexSession.id
3262
+ };
3263
+ }
3091
3264
  logger.debug(`[DAEMON RUN] Waiting for session webhook for PID ${tmuxResult.pid} (tmux)`);
3092
3265
  return new Promise((resolve) => {
3093
3266
  const timeout = setTimeout(() => {
@@ -3108,6 +3281,14 @@ async function startDaemon() {
3108
3281
  });
3109
3282
  });
3110
3283
  } else {
3284
+ if (precreatedCodexSession && api) {
3285
+ await archivePrecreatedManagedSession({
3286
+ api,
3287
+ session: precreatedCodexSession,
3288
+ archiveReason: `tmux spawn failed before Codex session ${precreatedCodexSession.id} attached`
3289
+ });
3290
+ precreatedCodexSession = null;
3291
+ }
3111
3292
  logger.debug(`[DAEMON RUN] Failed to spawn in tmux: ${tmuxResult.error}, falling back to regular spawning`);
3112
3293
  useTmux = false;
3113
3294
  }
@@ -3127,6 +3308,18 @@ async function startDaemon() {
3127
3308
  errorMessage: spawnError.errorMessage
3128
3309
  };
3129
3310
  }
3311
+ if (managedSessionTag && api) {
3312
+ precreatedCodexSession = await precreateDaemonManagedSession({
3313
+ api,
3314
+ sessionTag: managedSessionTag,
3315
+ machineId,
3316
+ directory,
3317
+ flavor: "codex"
3318
+ });
3319
+ if (precreatedCodexSession) {
3320
+ logger.debug(`[DAEMON RUN] Precreated Codex session ${precreatedCodexSession.id} before process spawn`);
3321
+ }
3322
+ }
3130
3323
  const happyProcess = spawnHappyCLI(args, {
3131
3324
  cwd: directory,
3132
3325
  detached: true,
@@ -3145,6 +3338,14 @@ async function startDaemon() {
3145
3338
  }
3146
3339
  if (!happyProcess.pid) {
3147
3340
  logger.debug("[DAEMON RUN] Failed to spawn process - no PID returned");
3341
+ if (precreatedCodexSession && api) {
3342
+ await archivePrecreatedManagedSession({
3343
+ api,
3344
+ session: precreatedCodexSession,
3345
+ archiveReason: `spawn produced no PID for precreated Codex session ${precreatedCodexSession.id}`
3346
+ });
3347
+ precreatedCodexSession = null;
3348
+ }
3148
3349
  const spawnError = createSpawnSessionError(
3149
3350
  SPAWN_SESSION_ERROR_CODES.SPAWN_NO_PID,
3150
3351
  "Failed to spawn Happy process - no PID returned"
@@ -3157,6 +3358,7 @@ async function startDaemon() {
3157
3358
  logger.debug(`[DAEMON RUN] Spawned process with PID ${happyProcess.pid}`);
3158
3359
  const trackedSession = {
3159
3360
  startedBy: "daemon",
3361
+ happySessionId: precreatedCodexSession?.id,
3160
3362
  pid: happyProcess.pid,
3161
3363
  childProcess: happyProcess,
3162
3364
  directoryCreated,
@@ -3175,6 +3377,13 @@ async function startDaemon() {
3175
3377
  onChildExited(happyProcess.pid);
3176
3378
  }
3177
3379
  });
3380
+ if (precreatedCodexSession) {
3381
+ logger.debug(`[DAEMON RUN] Returning precreated Codex session ${precreatedCodexSession.id} without waiting for webhook`);
3382
+ return {
3383
+ type: "success",
3384
+ sessionId: precreatedCodexSession.id
3385
+ };
3386
+ }
3178
3387
  logger.debug(`[DAEMON RUN] Waiting for session webhook for PID ${happyProcess.pid}`);
3179
3388
  return new Promise((resolve) => {
3180
3389
  const timeout = setTimeout(() => {
@@ -3202,6 +3411,13 @@ async function startDaemon() {
3202
3411
  } catch (error) {
3203
3412
  const errorMessage = error instanceof Error ? error.message : String(error);
3204
3413
  logger.debug("[DAEMON RUN] Failed to spawn session:", error);
3414
+ if (precreatedCodexSession && api) {
3415
+ await archivePrecreatedManagedSession({
3416
+ api,
3417
+ session: precreatedCodexSession,
3418
+ archiveReason: `spawn failed before precreated Codex session ${precreatedCodexSession.id} attached: ${errorMessage}`
3419
+ });
3420
+ }
3205
3421
  const spawnError = createSpawnSessionError(
3206
3422
  SPAWN_SESSION_ERROR_CODES.SPAWN_FAILED,
3207
3423
  `Failed to spawn session: ${errorMessage}`
@@ -3231,7 +3447,7 @@ async function startDaemon() {
3231
3447
  logger.debug(`[DAEMON RUN] Failed to kill external session PID ${pid}:`, error);
3232
3448
  }
3233
3449
  }
3234
- pidToTrackedSession.delete(pid);
3450
+ removeTrackedSession(pid, `session ${sessionId} was stopped from daemon control before local attachment completed`);
3235
3451
  logger.debug(`[DAEMON RUN] Removed session ${sessionId} from tracking`);
3236
3452
  return true;
3237
3453
  }
@@ -3253,7 +3469,7 @@ async function startDaemon() {
3253
3469
  errorMessage: spawnError.errorMessage
3254
3470
  });
3255
3471
  }
3256
- pidToTrackedSession.delete(pid);
3472
+ removeTrackedSession(pid, `process ${pid} exited before the managed session fully attached`);
3257
3473
  };
3258
3474
  const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
3259
3475
  getChildren: getCurrentChildren,
@@ -3291,22 +3507,23 @@ async function startDaemon() {
3291
3507
  httpPort: controlPort,
3292
3508
  startedAt: Date.now()
3293
3509
  };
3294
- const api = await ApiClient.create(credentials);
3510
+ api = await ApiClient.create(credentials);
3511
+ const activeApi = api;
3295
3512
  let userScopedObserver = null;
3296
3513
  if (credentials.signing) {
3297
3514
  try {
3298
- userScopedObserver = api.userScopedObserverClient?.() ?? null;
3515
+ userScopedObserver = activeApi.userScopedObserverClient?.() ?? null;
3299
3516
  } catch (error) {
3300
3517
  logger.debug("[DAEMON RUN] Failed to start user-scoped observer, continuing without it", error);
3301
3518
  }
3302
3519
  }
3303
- const machine = await api.getOrCreateMachine({
3520
+ const machine = await activeApi.getOrCreateMachine({
3304
3521
  machineId,
3305
3522
  metadata: initialMachineMetadata,
3306
3523
  daemonState: initialDaemonState
3307
3524
  });
3308
3525
  logger.debug(`[DAEMON RUN] Machine registered: ${machine.id}`);
3309
- const apiMachine = api.machineSyncClient(machine);
3526
+ const apiMachine = activeApi.machineSyncClient(machine);
3310
3527
  apiMachine.setRPCHandlers({
3311
3528
  spawnSession,
3312
3529
  stopSession,
@@ -3314,7 +3531,7 @@ async function startDaemon() {
3314
3531
  });
3315
3532
  const runRemoteSessionIndexRecovery = async (label) => {
3316
3533
  const recoveryResult = await recoverTrackedSessionsFromRemoteIndex({
3317
- api,
3534
+ api: activeApi,
3318
3535
  machineId,
3319
3536
  trackedSessionPids: pidToTrackedSession.keys(),
3320
3537
  trackSession: (pid, trackedSession) => {
@@ -3370,7 +3587,7 @@ async function startDaemon() {
3370
3587
  await runDaemonHealthCheck({
3371
3588
  trackedSessionPids: pidToTrackedSession.keys(),
3372
3589
  removeTrackedSession: (pid) => {
3373
- pidToTrackedSession.delete(pid);
3590
+ removeTrackedSession(pid, `health check removed stale tracked PID ${pid} before local attachment completed`);
3374
3591
  },
3375
3592
  currentCliVersion: configuration.currentCliVersion,
3376
3593
  onDaemonOutdated: async () => {
@@ -3513,10 +3730,10 @@ async function install$1() {
3513
3730
  <true/>
3514
3731
 
3515
3732
  <key>StandardErrorPath</key>
3516
- <string>${os.homedir()}/.happy/daemon.err</string>
3733
+ <string>${os$1.homedir()}/.happy/daemon.err</string>
3517
3734
 
3518
3735
  <key>StandardOutPath</key>
3519
- <string>${os.homedir()}/.happy/daemon.log</string>
3736
+ <string>${os$1.homedir()}/.happy/daemon.log</string>
3520
3737
 
3521
3738
  <key>WorkingDirectory</key>
3522
3739
  <string>/tmp</string>
@@ -3649,7 +3866,7 @@ async function handleAuthLogin(args) {
3649
3866
  if (existingCreds && settings?.machineId) {
3650
3867
  console.log(chalk.green("\u2713 Already authenticated"));
3651
3868
  console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
3652
- console.log(chalk.gray(` Host: ${os$1.hostname()}`));
3869
+ console.log(chalk.gray(` Host: ${os.hostname()}`));
3653
3870
  console.log(chalk.gray(` Use 'hicloud auth login --force' to re-authenticate`));
3654
3871
  return;
3655
3872
  } else if (existingCreds && !settings?.machineId) {
@@ -3728,7 +3945,7 @@ async function handleAuthStatus() {
3728
3945
  if (settings?.machineId) {
3729
3946
  console.log(chalk.green("\u2713 Machine registered"));
3730
3947
  console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
3731
- console.log(chalk.gray(` Host: ${os$1.hostname()}`));
3948
+ console.log(chalk.gray(` Host: ${os.hostname()}`));
3732
3949
  } else {
3733
3950
  console.log(chalk.yellow("\u26A0\uFE0F Machine not registered"));
3734
3951
  console.log(chalk.gray(' Run "hicloud auth login --force" to fix this'));
@@ -7925,7 +8142,7 @@ function firstExistingPath(candidates) {
7925
8142
  }
7926
8143
  function resolveCodexExecutable() {
7927
8144
  if (process.platform === "win32") {
7928
- const appData = process.env.APPDATA || path.join(os$1.homedir(), "AppData", "Roaming");
8145
+ const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
7929
8146
  const npmGlobalBin = path.join(appData, "npm");
7930
8147
  const resolved = firstExistingPath([
7931
8148
  path.join(npmGlobalBin, "codex.cmd"),
@@ -8365,6 +8582,7 @@ class Query {
8365
8582
  }
8366
8583
  pendingControlResponses = /* @__PURE__ */ new Map();
8367
8584
  cancelControllers = /* @__PURE__ */ new Map();
8585
+ controlTasks = /* @__PURE__ */ new Set();
8368
8586
  sdkMessages;
8369
8587
  inputStream = new Stream();
8370
8588
  canCallTool;
@@ -8413,7 +8631,12 @@ class Query {
8413
8631
  }
8414
8632
  continue;
8415
8633
  } else if (message.type === "control_request") {
8416
- await this.handleControlRequest(message);
8634
+ const controlTask = this.handleControlRequest(message).catch((error) => {
8635
+ logger.debug("[ClaudeSDK] Failed to handle control request:", error);
8636
+ }).finally(() => {
8637
+ this.controlTasks.delete(controlTask);
8638
+ });
8639
+ this.controlTasks.add(controlTask);
8417
8640
  continue;
8418
8641
  } else if (message.type === "control_cancel_request") {
8419
8642
  this.handleControlCancelRequest(message);
@@ -8429,8 +8652,11 @@ class Query {
8429
8652
  } catch (error) {
8430
8653
  this.inputStream.error(error);
8431
8654
  } finally {
8432
- this.inputStream.done();
8433
8655
  this.cleanupControllers();
8656
+ if (this.controlTasks.size > 0) {
8657
+ await Promise.allSettled(Array.from(this.controlTasks));
8658
+ }
8659
+ this.inputStream.done();
8434
8660
  rl.close();
8435
8661
  }
8436
8662
  }
@@ -8495,7 +8721,7 @@ class Query {
8495
8721
  response
8496
8722
  }
8497
8723
  };
8498
- this.childStdin.write(JSON.stringify(controlResponse) + "\n");
8724
+ this.writeControlResponse(controlResponse);
8499
8725
  } catch (error) {
8500
8726
  const controlErrorResponse = {
8501
8727
  type: "control_response",
@@ -8505,11 +8731,21 @@ class Query {
8505
8731
  error: error instanceof Error ? error.message : String(error)
8506
8732
  }
8507
8733
  };
8508
- this.childStdin.write(JSON.stringify(controlErrorResponse) + "\n");
8734
+ this.writeControlResponse(controlErrorResponse);
8509
8735
  } finally {
8510
8736
  this.cancelControllers.delete(request.request_id);
8511
8737
  }
8512
8738
  }
8739
+ writeControlResponse(response) {
8740
+ if (!this.childStdin || this.childStdin.destroyed || !this.childStdin.writable) {
8741
+ return;
8742
+ }
8743
+ try {
8744
+ this.childStdin.write(JSON.stringify(response) + "\n");
8745
+ } catch (error) {
8746
+ logger.debug("[ClaudeSDK] Failed to write control response:", error);
8747
+ }
8748
+ }
8513
8749
  /**
8514
8750
  * Handle control cancel requests
8515
8751
  * Replicates the exact logic from the SDK's handleControlCancelRequest method
@@ -9494,11 +9730,11 @@ var launch = /*#__PURE__*/Object.freeze({
9494
9730
 
9495
9731
  const unifiedProviderExecutors = {
9496
9732
  claude: async (opts) => {
9497
- const { runClaude } = await import('./runClaude-BRhQLKjh.mjs');
9733
+ const { runClaude } = await import('./runClaude-MF34EsCp.mjs');
9498
9734
  await runClaude(opts.credentials, opts.claudeOptions ?? {});
9499
9735
  },
9500
9736
  codex: async (opts) => {
9501
- const { runCodex } = await import('./runCodex-DUs_jBE-.mjs');
9737
+ const { runCodex } = await import('./runCodex-B31D_imZ.mjs');
9502
9738
  await runCodex({
9503
9739
  credentials: opts.credentials,
9504
9740
  startedBy: opts.startedBy,
@@ -9507,7 +9743,7 @@ const unifiedProviderExecutors = {
9507
9743
  });
9508
9744
  },
9509
9745
  gemini: async (opts) => {
9510
- const { runGemini } = await import('./runGemini-pmvBZ6qU.mjs');
9746
+ const { runGemini } = await import('./runGemini-BJ7PxLyl.mjs');
9511
9747
  await runGemini({
9512
9748
  credentials: opts.credentials,
9513
9749
  startedBy: opts.startedBy
@@ -9583,7 +9819,7 @@ function shouldRunMainClaudeFlow(opts) {
9583
9819
  return;
9584
9820
  } else if (subcommand === "runtime") {
9585
9821
  if (args[1] === "providers") {
9586
- const { renderRuntimeProviders } = await import('./command-BVCkEMtp.mjs');
9822
+ const { renderRuntimeProviders } = await import('./command-hO52qTzQ.mjs');
9587
9823
  console.log(renderRuntimeProviders());
9588
9824
  return;
9589
9825
  }
@@ -9761,8 +9997,8 @@ function shouldRunMainClaudeFlow(opts) {
9761
9997
  const projectId = args[3];
9762
9998
  try {
9763
9999
  const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
9764
- const { readCredentials: readCredentials2 } = await import('./persistence-CyFjFOlN.mjs');
9765
- const { ApiClient: ApiClient2 } = await import('./api-l8X03rs-.mjs').then(function (n) { return n.v; });
10000
+ const { readCredentials: readCredentials2 } = await import('./persistence-BsWBBi7E.mjs');
10001
+ const { ApiClient: ApiClient2 } = await import('./api-DF9A136-.mjs').then(function (n) { return n.v; });
9766
10002
  let userEmail = void 0;
9767
10003
  try {
9768
10004
  const credentials = await readCredentials2();
@@ -10178,4 +10414,4 @@ ${chalk.bold("Examples:")}
10178
10414
  }
10179
10415
  }
10180
10416
 
10181
- export { AbortError as A, ExitCodeError as E, Future as F, GEMINI_MODEL_ENV as G, PushableAsyncIterable as P, RuntimeShell as R, createGeminiBackend as a, stopCaffeinate as b, createDefaultRuntimeShell as c, createCodexBackend as d, projectPath as e, formatDisplayMessage as f, getInitialGeminiModel as g, resolveCanonicalToolNameV2 as h, initialMachineMetadata as i, getProjectPath as j, claudeLocal as k, trimIdent as l, createClaudeBackend as m, claudeCheckSession as n, mapToClaudeMode as o, publishSessionRegistration as p, query as q, readGeminiLocalConfig as r, saveGeminiModelToConfig as s, truncateDisplayMessage as t, getEnvironmentInfo as u, validateCodexAcpSpawn as v, startCaffeinate as w };
10417
+ export { AbortError as A, startCaffeinate as B, ExitCodeError as E, Future as F, GEMINI_MODEL_ENV as G, PushableAsyncIterable as P, RuntimeShell as R, createSessionMetadata as a, closeProviderSession as b, createDefaultRuntimeShell as c, createGeminiBackend as d, stopCaffeinate as e, formatDisplayMessage as f, getInitialGeminiModel as g, createCodexBackend as h, readManagedSessionTag as i, resolveManagedSessionTag as j, initialMachineMetadata as k, resolveCanonicalToolNameV2 as l, getProjectPath as m, claudeLocal as n, trimIdent as o, publishSessionRegistration as p, createClaudeBackend as q, readGeminiLocalConfig as r, saveGeminiModelToConfig as s, truncateDisplayMessage as t, claudeCheckSession as u, validateCodexAcpSpawn as v, projectPath as w, mapToClaudeMode as x, query as y, getEnvironmentInfo as z };