happy-imou-cloud 2.0.1 → 2.0.3

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 (27) hide show
  1. package/dist/{BaseReasoningProcessor-DQE2l7Xu.mjs → BaseReasoningProcessor-B37yOHxo.mjs} +2 -2
  2. package/dist/{BaseReasoningProcessor-01KqA3Kz.cjs → BaseReasoningProcessor-_wxlqKB8.cjs} +4 -4
  3. package/dist/{api-B8v4tczT.cjs → api-D9dIR956.cjs} +97 -44
  4. package/dist/{api-B5Ui8Fw0.mjs → api-DpQIC-DJ.mjs} +56 -3
  5. package/dist/{command-D8yNlaDo.cjs → command-CdXv1zNF.cjs} +3 -3
  6. package/dist/{command-BfIuJmeo.mjs → command-DRqrBuHM.mjs} +3 -3
  7. package/dist/{index-BByhFIIq.mjs → index-CriPm_z9.mjs} +15 -17
  8. package/dist/{index-BOqJ9hwi.cjs → index-LYPXVO_L.cjs} +98 -100
  9. package/dist/index.cjs +3 -3
  10. package/dist/index.mjs +3 -3
  11. package/dist/lib.cjs +1 -1
  12. package/dist/lib.d.cts +6 -0
  13. package/dist/lib.d.mts +6 -0
  14. package/dist/lib.mjs +1 -1
  15. package/dist/{persistence-CzpZpiL3.mjs → persistence-CqgPgbzN.mjs} +29 -7
  16. package/dist/{persistence-C33AMdtv.cjs → persistence-PzKU0QCa.cjs} +54 -32
  17. package/dist/{registerKillSessionHandler-BkzQulD9.cjs → registerKillSessionHandler-BDBPoQSA.cjs} +2 -2
  18. package/dist/{registerKillSessionHandler-BtSK7IOa.mjs → registerKillSessionHandler-C3M_-4Zg.mjs} +2 -2
  19. package/dist/{runClaude-C_WLfM6c.mjs → runClaude-D6Pdkevn.mjs} +250 -46
  20. package/dist/{runClaude-CNVufgZb.cjs → runClaude-IeRSC5qX.cjs} +270 -66
  21. package/dist/{runCodex-8eWjTPJr.mjs → runCodex-CsfUU1Wb.mjs} +216 -401
  22. package/dist/{runCodex-Dzy8anlX.cjs → runCodex-WRmgSK6L.cjs} +216 -401
  23. package/dist/{runGemini-nbr0mm-S.mjs → runGemini-CrH3dQ0Y.mjs} +5 -5
  24. package/dist/{runGemini-CgsVKP7m.cjs → runGemini-qBh6zs5G.cjs} +5 -5
  25. package/package.json +1 -2
  26. package/dist/future-Dq4Ha1Dn.cjs +0 -24
  27. package/dist/future-xRdLl3vf.mjs +0 -22
@@ -2,19 +2,18 @@
2
2
 
3
3
  var os = require('node:os');
4
4
  var node_crypto = require('node:crypto');
5
- var api = require('./api-B8v4tczT.cjs');
6
- var index = require('./index-BOqJ9hwi.cjs');
7
- var future = require('./future-Dq4Ha1Dn.cjs');
5
+ var api = require('./api-D9dIR956.cjs');
6
+ var index = require('./index-LYPXVO_L.cjs');
8
7
  var types = require('./types-DVk3crez.cjs');
9
- var path = require('node:path');
8
+ var node_path = require('node:path');
10
9
  var promises = require('node:fs/promises');
11
10
  var fs = require('fs/promises');
12
11
  var ink = require('ink');
13
- var registerKillSessionHandler = require('./registerKillSessionHandler-BkzQulD9.cjs');
12
+ var registerKillSessionHandler = require('./registerKillSessionHandler-BDBPoQSA.cjs');
14
13
  var React = require('react');
15
14
  var node_child_process = require('node:child_process');
16
15
  var node_readline = require('node:readline');
17
- var fs$1 = require('node:fs');
16
+ var node_fs = require('node:fs');
18
17
  var node_url = require('node:url');
19
18
  require('axios');
20
19
  require('node:events');
@@ -23,7 +22,7 @@ require('tweetnacl');
23
22
  require('expo-server-sdk');
24
23
  require('chalk');
25
24
  var node_util = require('node:util');
26
- var persistence = require('./persistence-C33AMdtv.cjs');
25
+ var persistence = require('./persistence-PzKU0QCa.cjs');
27
26
  var node_http = require('node:http');
28
27
  require('fs');
29
28
  require('zod');
@@ -62,6 +61,7 @@ class Session {
62
61
  /** JavaScript runtime to use for spawning Claude Code (default: 'node') */
63
62
  jsRuntime;
64
63
  sessionId;
64
+ transcriptPath = null;
65
65
  mode = "local";
66
66
  thinking = false;
67
67
  /** Callbacks to be notified when session ID is found/changed */
@@ -115,15 +115,33 @@ class Session {
115
115
  * Updates internal state, syncs to API metadata, and notifies
116
116
  * all registered callbacks (e.g., SessionScanner) about the change.
117
117
  */
118
- onSessionFound = (sessionId) => {
118
+ onSessionFound = (sessionId, hookData) => {
119
+ const nextTranscriptPathRaw = hookData?.transcript_path ?? hookData?.transcriptPath;
120
+ const nextTranscriptPath = typeof nextTranscriptPathRaw === "string" ? nextTranscriptPathRaw : null;
121
+ const previousSessionId = this.sessionId;
122
+ const previousTranscriptPath = this.transcriptPath;
119
123
  this.sessionId = sessionId;
124
+ if (previousSessionId !== sessionId) {
125
+ this.transcriptPath = nextTranscriptPath;
126
+ } else if (nextTranscriptPath) {
127
+ this.transcriptPath = nextTranscriptPath;
128
+ }
120
129
  this.client.updateMetadata((metadata) => ({
121
130
  ...metadata,
122
- claudeSessionId: sessionId
131
+ claudeSessionId: sessionId,
132
+ claudeTranscriptPath: this.transcriptPath || void 0
123
133
  }));
124
- api.logger.debug(`[Session] Claude Code session ID ${sessionId} added to metadata`);
134
+ api.logger.debug(`[Session] Claude session info updated: sessionId=${sessionId}, transcriptPath=${this.transcriptPath ?? "none"}`);
135
+ const didTranscriptPathChange = nextTranscriptPath !== null && nextTranscriptPath !== previousTranscriptPath;
136
+ if (previousSessionId === sessionId && !didTranscriptPathChange) {
137
+ return;
138
+ }
139
+ const info = {
140
+ sessionId,
141
+ transcriptPath: this.transcriptPath
142
+ };
125
143
  for (const callback of this.sessionFoundCallbacks) {
126
- callback(sessionId);
144
+ callback(info);
127
145
  }
128
146
  };
129
147
  /**
@@ -146,6 +164,7 @@ class Session {
146
164
  */
147
165
  clearSessionId = () => {
148
166
  this.sessionId = null;
167
+ this.transcriptPath = null;
149
168
  api.logger.debug("[Session] Session ID cleared");
150
169
  };
151
170
  /**
@@ -182,6 +201,27 @@ class Session {
182
201
  };
183
202
  }
184
203
 
204
+ class Future {
205
+ _resolve;
206
+ _reject;
207
+ _promise;
208
+ constructor() {
209
+ this._promise = new Promise((resolve, reject) => {
210
+ this._resolve = resolve;
211
+ this._reject = reject;
212
+ });
213
+ }
214
+ resolve(value) {
215
+ this._resolve(value);
216
+ }
217
+ reject(reason) {
218
+ this._reject(reason);
219
+ }
220
+ get promise() {
221
+ return this._promise;
222
+ }
223
+ }
224
+
185
225
  class InvalidateSync {
186
226
  _invalidated = false;
187
227
  _invalidatedDouble = false;
@@ -250,9 +290,51 @@ class InvalidateSync {
250
290
 
251
291
  function startFileWatcher(file, onFileChange) {
252
292
  const abortController = new AbortController();
293
+ const parentDir = node_path.dirname(file);
294
+ const targetName = node_path.basename(file);
253
295
  void (async () => {
254
296
  while (true) {
255
297
  try {
298
+ try {
299
+ await fs.stat(file);
300
+ } catch (e) {
301
+ if (abortController.signal.aborted) {
302
+ return;
303
+ }
304
+ if (e?.code === "ENOENT") {
305
+ api.logger.debug(`[FILE_WATCHER] Waiting for file to exist: ${file}`);
306
+ const dirWatcher = fs.watch(parentDir, { persistent: true, signal: abortController.signal });
307
+ try {
308
+ await fs.stat(file);
309
+ } catch (err) {
310
+ if (err?.code !== "ENOENT") {
311
+ throw err;
312
+ }
313
+ for await (const event of dirWatcher) {
314
+ if (abortController.signal.aborted) {
315
+ return;
316
+ }
317
+ const name = typeof event?.filename === "string" ? String(event.filename) : null;
318
+ if (name && name !== targetName) {
319
+ continue;
320
+ }
321
+ try {
322
+ await fs.stat(file);
323
+ api.logger.debug(`[FILE_WATCHER] File appeared: ${file}`);
324
+ break;
325
+ } catch (nextErr) {
326
+ if (nextErr?.code === "ENOENT") {
327
+ continue;
328
+ }
329
+ throw nextErr;
330
+ }
331
+ }
332
+ }
333
+ } else {
334
+ throw e;
335
+ }
336
+ }
337
+ onFileChange(file);
256
338
  api.logger.debug(`[FILE_WATCHER] Starting watcher for ${file}`);
257
339
  const watcher = fs.watch(file, { persistent: true, signal: abortController.signal });
258
340
  for await (const event of watcher) {
@@ -282,19 +364,67 @@ const INTERNAL_CLAUDE_EVENT_TYPES = /* @__PURE__ */ new Set([
282
364
  "queue-operation"
283
365
  ]);
284
366
  async function createSessionScanner(opts) {
285
- const projectDir = index.getProjectPath(opts.workingDirectory);
367
+ const initialProjectDir = index.getProjectPath(opts.workingDirectory);
368
+ let projectDirOverride = null;
369
+ const sessionFileOverrides = /* @__PURE__ */ new Map();
370
+ const transcriptMissingWarningMs = opts.transcriptMissingWarningMs ?? 5e3;
371
+ const warnedMissingTranscripts = /* @__PURE__ */ new Set();
372
+ const missingTranscriptTimers = /* @__PURE__ */ new Map();
373
+ function effectiveProjectDir() {
374
+ return projectDirOverride ?? initialProjectDir;
375
+ }
376
+ function getSessionFilePath(sessionId) {
377
+ const override = sessionFileOverrides.get(sessionId);
378
+ return override ?? node_path.join(effectiveProjectDir(), `${sessionId}.jsonl`);
379
+ }
380
+ function scheduleTranscriptMissingWarning(sessionId) {
381
+ if (!opts.onTranscriptMissing) {
382
+ return;
383
+ }
384
+ if (!Number.isFinite(transcriptMissingWarningMs) || transcriptMissingWarningMs <= 0) {
385
+ return;
386
+ }
387
+ if (warnedMissingTranscripts.has(sessionId) || missingTranscriptTimers.has(sessionId)) {
388
+ return;
389
+ }
390
+ const timeoutId = setTimeout(async () => {
391
+ missingTranscriptTimers.delete(sessionId);
392
+ if (warnedMissingTranscripts.has(sessionId)) {
393
+ return;
394
+ }
395
+ const filePath = getSessionFilePath(sessionId);
396
+ try {
397
+ await promises.readFile(filePath, "utf-8");
398
+ return;
399
+ } catch {
400
+ }
401
+ warnedMissingTranscripts.add(sessionId);
402
+ try {
403
+ opts.onTranscriptMissing?.({ sessionId, filePath });
404
+ } catch (err) {
405
+ api.logger.debug("[SESSION_SCANNER] onTranscriptMissing callback threw:", err);
406
+ }
407
+ }, transcriptMissingWarningMs);
408
+ missingTranscriptTimers.set(sessionId, timeoutId);
409
+ }
286
410
  let finishedSessions = /* @__PURE__ */ new Set();
287
411
  let pendingSessions = /* @__PURE__ */ new Set();
288
412
  let currentSessionId = null;
289
413
  let watchers = /* @__PURE__ */ new Map();
290
414
  let processedMessageKeys = /* @__PURE__ */ new Set();
415
+ if (opts.sessionId && typeof opts.transcriptPath === "string" && opts.transcriptPath.trim()) {
416
+ const transcriptPath = opts.transcriptPath.trim();
417
+ sessionFileOverrides.set(opts.sessionId, transcriptPath);
418
+ projectDirOverride = node_path.dirname(transcriptPath);
419
+ }
291
420
  if (opts.sessionId) {
292
- let messages = await readSessionLog(projectDir, opts.sessionId);
421
+ let messages = await readSessionLog(getSessionFilePath(opts.sessionId));
293
422
  api.logger.debug(`[SESSION_SCANNER] Marking ${messages.length} existing messages as processed from session ${opts.sessionId}`);
294
423
  for (let m of messages) {
295
424
  processedMessageKeys.add(messageKey(m));
296
425
  }
297
426
  currentSessionId = opts.sessionId;
427
+ scheduleTranscriptMissingWarning(opts.sessionId);
298
428
  }
299
429
  const sync = new InvalidateSync(async () => {
300
430
  let sessions = [];
@@ -310,7 +440,7 @@ async function createSessionScanner(opts) {
310
440
  }
311
441
  }
312
442
  for (let session of sessions) {
313
- const sessionMessages = await readSessionLog(projectDir, session);
443
+ const sessionMessages = await readSessionLog(getSessionFilePath(session));
314
444
  let skipped = 0;
315
445
  let sent = 0;
316
446
  for (let file of sessionMessages) {
@@ -335,11 +465,27 @@ async function createSessionScanner(opts) {
335
465
  }
336
466
  }
337
467
  for (let p of sessions) {
338
- if (!watchers.has(p)) {
468
+ const desiredPath = getSessionFilePath(p);
469
+ const existing = watchers.get(p);
470
+ if (!existing) {
339
471
  api.logger.debug(`[SESSION_SCANNER] Starting watcher for session: ${p}`);
340
- watchers.set(p, startFileWatcher(path.join(projectDir, `${p}.jsonl`), () => {
341
- sync.invalidate();
342
- }));
472
+ watchers.set(p, {
473
+ filePath: desiredPath,
474
+ stop: startFileWatcher(desiredPath, () => {
475
+ sync.invalidate();
476
+ })
477
+ });
478
+ continue;
479
+ }
480
+ if (existing.filePath !== desiredPath) {
481
+ api.logger.debug(`[SESSION_SCANNER] Restarting watcher for session: ${p} (${existing.filePath} -> ${desiredPath})`);
482
+ existing.stop();
483
+ watchers.set(p, {
484
+ filePath: desiredPath,
485
+ stop: startFileWatcher(desiredPath, () => {
486
+ sync.invalidate();
487
+ })
488
+ });
343
489
  }
344
490
  }
345
491
  });
@@ -351,23 +497,55 @@ async function createSessionScanner(opts) {
351
497
  cleanup: async () => {
352
498
  clearInterval(intervalId);
353
499
  for (let w of watchers.values()) {
354
- w();
500
+ w.stop();
355
501
  }
356
502
  watchers.clear();
503
+ for (const timeoutId of missingTranscriptTimers.values()) {
504
+ clearTimeout(timeoutId);
505
+ }
506
+ missingTranscriptTimers.clear();
357
507
  await sync.invalidateAndAwait();
358
508
  sync.stop();
359
509
  },
360
- onNewSession: (sessionId) => {
510
+ onNewSession: (arg) => {
511
+ const sessionId = typeof arg === "string" ? arg : arg.sessionId;
512
+ const transcriptPathRaw = typeof arg === "string" ? null : arg.transcriptPath;
513
+ const transcriptPath = typeof transcriptPathRaw === "string" && transcriptPathRaw.trim() ? transcriptPathRaw.trim() : null;
514
+ let didUpdatePaths = false;
515
+ if (transcriptPath) {
516
+ const previousOverride = sessionFileOverrides.get(sessionId);
517
+ if (previousOverride !== transcriptPath) {
518
+ sessionFileOverrides.set(sessionId, transcriptPath);
519
+ didUpdatePaths = true;
520
+ }
521
+ const nextProjectDir = node_path.dirname(transcriptPath);
522
+ if (projectDirOverride !== nextProjectDir) {
523
+ projectDirOverride = nextProjectDir;
524
+ didUpdatePaths = true;
525
+ }
526
+ }
361
527
  if (currentSessionId === sessionId) {
362
- api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is the same as the current session, skipping`);
528
+ if (didUpdatePaths) {
529
+ sync.invalidate();
530
+ } else {
531
+ api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is the same as the current session, skipping`);
532
+ }
363
533
  return;
364
534
  }
365
535
  if (finishedSessions.has(sessionId)) {
366
- api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is already finished, skipping`);
536
+ if (didUpdatePaths) {
537
+ sync.invalidate();
538
+ } else {
539
+ api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is already finished, skipping`);
540
+ }
367
541
  return;
368
542
  }
369
543
  if (pendingSessions.has(sessionId)) {
370
- api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is already pending, skipping`);
544
+ if (didUpdatePaths) {
545
+ sync.invalidate();
546
+ } else {
547
+ api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is already pending, skipping`);
548
+ }
371
549
  return;
372
550
  }
373
551
  if (currentSessionId) {
@@ -375,6 +553,7 @@ async function createSessionScanner(opts) {
375
553
  }
376
554
  api.logger.debug(`[SESSION_SCANNER] New session: ${sessionId}`);
377
555
  currentSessionId = sessionId;
556
+ scheduleTranscriptMissingWarning(sessionId);
378
557
  sync.invalidate();
379
558
  }
380
559
  };
@@ -392,14 +571,13 @@ function messageKey(message) {
392
571
  throw Error();
393
572
  }
394
573
  }
395
- async function readSessionLog(projectDir, sessionId) {
396
- const expectedSessionFile = path.join(projectDir, `${sessionId}.jsonl`);
397
- api.logger.debug(`[SESSION_SCANNER] Reading session file: ${expectedSessionFile}`);
574
+ async function readSessionLog(sessionFilePath) {
575
+ api.logger.debug(`[SESSION_SCANNER] Reading session file: ${sessionFilePath}`);
398
576
  let file;
399
577
  try {
400
- file = await promises.readFile(expectedSessionFile, "utf-8");
578
+ file = await promises.readFile(sessionFilePath, "utf-8");
401
579
  } catch (error) {
402
- api.logger.debug(`[SESSION_SCANNER] Session file not found: ${expectedSessionFile}`);
580
+ api.logger.debug(`[SESSION_SCANNER] Session file not found: ${sessionFilePath}`);
403
581
  return [];
404
582
  }
405
583
  let lines = file.split("\n");
@@ -429,20 +607,30 @@ async function readSessionLog(projectDir, sessionId) {
429
607
  async function claudeLocalLauncher(session) {
430
608
  const scanner = await createSessionScanner({
431
609
  sessionId: session.sessionId,
610
+ transcriptPath: session.transcriptPath,
432
611
  workingDirectory: session.path,
433
612
  onMessage: (message) => {
434
613
  if (message.type !== "summary") {
435
614
  session.client.sendClaudeSessionMessage(message);
436
615
  }
616
+ },
617
+ onTranscriptMissing: () => {
618
+ session.client.sendSessionEvent({
619
+ type: "message",
620
+ message: "Claude transcript file not found yet, waiting for it to appear..."
621
+ });
437
622
  }
438
623
  });
439
- const scannerSessionCallback = (sessionId) => {
440
- scanner.onNewSession(sessionId);
624
+ const scannerSessionCallback = (info) => {
625
+ scanner.onNewSession({
626
+ sessionId: info.sessionId,
627
+ transcriptPath: info.transcriptPath
628
+ });
441
629
  };
442
630
  session.addSessionFoundCallback(scannerSessionCallback);
443
631
  let exitReason = null;
444
632
  const processAbortController = new AbortController();
445
- let exutFuture = new future.Future();
633
+ let exutFuture = new Future();
446
634
  try {
447
635
  async function abort() {
448
636
  if (!processAbortController.signal.aborted) {
@@ -475,7 +663,6 @@ async function claudeLocalLauncher(session) {
475
663
  }
476
664
  const handleSessionStart = (sessionId) => {
477
665
  session.onSessionFound(sessionId);
478
- scanner.onNewSession(sessionId);
479
666
  };
480
667
  while (true) {
481
668
  if (exitReason) {
@@ -750,8 +937,8 @@ class AbortError extends Error {
750
937
  }
751
938
  }
752
939
 
753
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-CNVufgZb.cjs', document.baseURI).href)));
754
- const __dirname$1 = path.join(__filename$1, "..");
940
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-IeRSC5qX.cjs', document.baseURI).href)));
941
+ const __dirname$1 = node_path.join(__filename$1, "..");
755
942
  function getGlobalClaudeVersion() {
756
943
  try {
757
944
  const cleanEnv = getCleanEnv();
@@ -815,7 +1002,7 @@ function findGlobalClaudePath() {
815
1002
  cwd: homeDir,
816
1003
  env: cleanEnv
817
1004
  }).trim();
818
- if (result && fs$1.existsSync(result)) {
1005
+ if (result && node_fs.existsSync(result)) {
819
1006
  api.logger.debug(`[Claude SDK] Found global claude path via which: ${result}`);
820
1007
  return result;
821
1008
  }
@@ -825,7 +1012,7 @@ function findGlobalClaudePath() {
825
1012
  return null;
826
1013
  }
827
1014
  function getDefaultClaudeCodePath() {
828
- const nodeModulesPath = path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
1015
+ const nodeModulesPath = node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
829
1016
  if (process.env.HAPPY_CLAUDE_PATH) {
830
1017
  api.logger.debug(`[Claude SDK] Using HAPPY_CLAUDE_PATH: ${process.env.HAPPY_CLAUDE_PATH}`);
831
1018
  return process.env.HAPPY_CLAUDE_PATH;
@@ -1114,7 +1301,7 @@ function query(config) {
1114
1301
  }
1115
1302
  const isJsFile = pathToClaudeCodeExecutable.endsWith(".js") || pathToClaudeCodeExecutable.endsWith(".cjs");
1116
1303
  const isCommandOnly = pathToClaudeCodeExecutable === "claude";
1117
- if (!isCommandOnly && !fs$1.existsSync(pathToClaudeCodeExecutable)) {
1304
+ if (!isCommandOnly && !node_fs.existsSync(pathToClaudeCodeExecutable)) {
1118
1305
  throw new ReferenceError(`Claude Code executable not found at ${pathToClaudeCodeExecutable}. Is options.pathToClaudeCodeExecutable set?`);
1119
1306
  }
1120
1307
  const spawnCommand = isJsFile ? executable : pathToClaudeCodeExecutable;
@@ -1371,17 +1558,17 @@ async function awaitFileExist(file, timeout = 1e4) {
1371
1558
  }
1372
1559
 
1373
1560
  function getClaudeSettingsPath() {
1374
- const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
1375
- return path.join(claudeConfigDir, "settings.json");
1561
+ const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || node_path.join(os.homedir(), ".claude");
1562
+ return node_path.join(claudeConfigDir, "settings.json");
1376
1563
  }
1377
1564
  function readClaudeSettings() {
1378
1565
  try {
1379
1566
  const settingsPath = getClaudeSettingsPath();
1380
- if (!fs$1.existsSync(settingsPath)) {
1567
+ if (!node_fs.existsSync(settingsPath)) {
1381
1568
  api.logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
1382
1569
  return null;
1383
1570
  }
1384
- const settingsContent = fs$1.readFileSync(settingsPath, "utf-8");
1571
+ const settingsContent = node_fs.readFileSync(settingsPath, "utf-8");
1385
1572
  const settings = JSON.parse(settingsContent);
1386
1573
  api.logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
1387
1574
  api.logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
@@ -1424,7 +1611,7 @@ const systemPrompt = (() => {
1424
1611
 
1425
1612
  async function claudeRemote(opts) {
1426
1613
  let startFrom = opts.sessionId;
1427
- if (opts.sessionId && !index.claudeCheckSession(opts.sessionId, opts.path)) {
1614
+ if (opts.sessionId && !index.claudeCheckSession(opts.sessionId, opts.path, opts.transcriptPath)) {
1428
1615
  startFrom = null;
1429
1616
  }
1430
1617
  if (!startFrom && opts.claudeArgs) {
@@ -1490,7 +1677,7 @@ async function claudeRemote(opts) {
1490
1677
  executable: opts.jsRuntime ?? "node",
1491
1678
  abort: opts.signal,
1492
1679
  pathToClaudeCodeExecutable: (() => {
1493
- return path.resolve(path.join(index.projectPath(), "scripts", "claude_remote_launcher.cjs"));
1680
+ return node_path.resolve(node_path.join(index.projectPath(), "scripts", "claude_remote_launcher.cjs"));
1494
1681
  })(),
1495
1682
  settingsPath: opts.hookSettingsPath
1496
1683
  };
@@ -1526,11 +1713,14 @@ async function claudeRemote(opts) {
1526
1713
  updateThinking(true);
1527
1714
  const systemInit = message;
1528
1715
  if (systemInit.session_id) {
1529
- api.logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${systemInit.session_id}`);
1530
- const projectDir = index.getProjectPath(opts.path);
1531
- const found = await awaitFileExist(path.join(projectDir, `${systemInit.session_id}.jsonl`));
1716
+ const transcriptPath = opts.transcriptPath && opts.sessionId === systemInit.session_id ? opts.transcriptPath : node_path.join(index.getProjectPath(opts.path), `${systemInit.session_id}.jsonl`);
1717
+ api.logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${transcriptPath}`);
1718
+ const found = await awaitFileExist(transcriptPath);
1532
1719
  api.logger.debug(`[claudeRemote] Session file found: ${systemInit.session_id} ${found}`);
1533
- opts.onSessionFound(systemInit.session_id);
1720
+ opts.onSessionFound(systemInit.session_id, {
1721
+ transcript_path: transcriptPath,
1722
+ transcriptPath
1723
+ });
1534
1724
  }
1535
1725
  }
1536
1726
  if (message.type === "result") {
@@ -1983,11 +2173,21 @@ function formatClaudeMessageForInk(message, messageBuffer, onAssistantResult) {
1983
2173
  case "assistant": {
1984
2174
  const assistantMsg = message;
1985
2175
  if (assistantMsg.message && assistantMsg.message.content) {
1986
- messageBuffer.addMessage("\u{1F916} Assistant:", "assistant");
2176
+ let assistantHeaderShown = false;
1987
2177
  for (const block of assistantMsg.message.content) {
1988
- if (block.type === "text") {
2178
+ if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim()) {
2179
+ messageBuffer.addMessage(`[Thinking] ${block.thinking}`, "status");
2180
+ } else if (block.type === "text") {
2181
+ if (!assistantHeaderShown) {
2182
+ messageBuffer.addMessage("\u{1F916} Assistant:", "assistant");
2183
+ assistantHeaderShown = true;
2184
+ }
1989
2185
  messageBuffer.addMessage(block.text || "", "assistant");
1990
2186
  } else if (block.type === "tool_use") {
2187
+ if (!assistantHeaderShown) {
2188
+ messageBuffer.addMessage("\u{1F916} Assistant:", "assistant");
2189
+ assistantHeaderShown = true;
2190
+ }
1991
2191
  messageBuffer.addMessage(`\u{1F527} Tool: ${block.name}`, "tool");
1992
2192
  if (block.input) {
1993
2193
  const inputStr = JSON.stringify(block.input, null, 2);
@@ -2627,12 +2827,13 @@ async function claudeRemoteLauncher(session) {
2627
2827
  previousSessionId = session.sessionId;
2628
2828
  const controller = new AbortController();
2629
2829
  abortController = controller;
2630
- abortFuture = new future.Future();
2830
+ abortFuture = new Future();
2631
2831
  let modeHash = null;
2632
2832
  let mode = null;
2633
2833
  try {
2634
2834
  const remoteResult = await claudeRemote({
2635
2835
  sessionId: session.sessionId,
2836
+ transcriptPath: session.transcriptPath,
2636
2837
  path: session.path,
2637
2838
  allowedTools: session.allowedTools ?? [],
2638
2839
  mcpServers: session.mcpServers,
@@ -2666,9 +2867,9 @@ async function claudeRemoteLauncher(session) {
2666
2867
  }
2667
2868
  return null;
2668
2869
  },
2669
- onSessionFound: (sessionId) => {
2870
+ onSessionFound: (sessionId, data) => {
2670
2871
  sdkToLogConverter.updateSessionId(sessionId);
2671
- session.onSessionFound(sessionId);
2872
+ session.onSessionFound(sessionId, data);
2672
2873
  },
2673
2874
  onThinkingChange: session.onThinkingChange,
2674
2875
  claudeEnvVars: session.claudeEnvVars,
@@ -2857,13 +3058,20 @@ async function startHookServer(options) {
2857
3058
  }
2858
3059
  clearTimeout(timeout);
2859
3060
  const body = Buffer.concat(chunks).toString("utf-8");
2860
- api.logger.debug("[hookServer] Received session hook:", body);
2861
3061
  let data = {};
2862
3062
  try {
2863
3063
  data = JSON.parse(body);
2864
3064
  } catch (parseError) {
2865
3065
  api.logger.debug("[hookServer] Failed to parse hook data as JSON:", parseError);
2866
3066
  }
3067
+ api.logger.debug("[hookServer] Received session hook", {
3068
+ sessionId: data.session_id || data.sessionId || null,
3069
+ transcriptPath: data.transcript_path || data.transcriptPath || null,
3070
+ cwd: data.cwd,
3071
+ hookEventName: data.hook_event_name,
3072
+ source: data.source,
3073
+ bodyLength: body.length
3074
+ });
2867
3075
  const sessionId = data.session_id || data.sessionId;
2868
3076
  if (sessionId) {
2869
3077
  api.logger.debug(`[hookServer] Session hook received session ID: ${sessionId}`);
@@ -2907,11 +3115,11 @@ async function startHookServer(options) {
2907
3115
  }
2908
3116
 
2909
3117
  function generateHookSettingsFile(port) {
2910
- const hooksDir = path.join(api.configuration.happyCloudHomeDir, "tmp", "hooks");
2911
- fs$1.mkdirSync(hooksDir, { recursive: true });
3118
+ const hooksDir = node_path.join(api.configuration.happyCloudHomeDir, "tmp", "hooks");
3119
+ node_fs.mkdirSync(hooksDir, { recursive: true });
2912
3120
  const filename = `session-hook-${process.pid}.json`;
2913
- const filepath = path.join(hooksDir, filename);
2914
- const forwarderScript = path.resolve(index.projectPath(), "scripts", "session_hook_forwarder.cjs");
3121
+ const filepath = node_path.join(hooksDir, filename);
3122
+ const forwarderScript = node_path.resolve(index.projectPath(), "scripts", "session_hook_forwarder.cjs");
2915
3123
  const hookCommand = `node "${forwarderScript}" ${port}`;
2916
3124
  const settings = {
2917
3125
  hooks: {
@@ -2928,14 +3136,14 @@ function generateHookSettingsFile(port) {
2928
3136
  ]
2929
3137
  }
2930
3138
  };
2931
- fs$1.writeFileSync(filepath, JSON.stringify(settings, null, 2));
3139
+ node_fs.writeFileSync(filepath, JSON.stringify(settings, null, 2));
2932
3140
  api.logger.debug(`[generateHookSettings] Created hook settings file: ${filepath}`);
2933
3141
  return filepath;
2934
3142
  }
2935
3143
  function cleanupHookSettingsFile(filepath) {
2936
3144
  try {
2937
- if (fs$1.existsSync(filepath)) {
2938
- fs$1.unlinkSync(filepath);
3145
+ if (node_fs.existsSync(filepath)) {
3146
+ node_fs.unlinkSync(filepath);
2939
3147
  api.logger.debug(`[generateHookSettings] Cleaned up hook settings file: ${filepath}`);
2940
3148
  }
2941
3149
  } catch (error) {
@@ -2976,7 +3184,7 @@ async function runClaude(credentials, options = {}) {
2976
3184
  homeDir: os.homedir(),
2977
3185
  happyHomeDir: api.configuration.happyCloudHomeDir,
2978
3186
  happyLibDir: index.projectPath(),
2979
- happyToolsDir: path.resolve(index.projectPath(), "tools", "unpacked"),
3187
+ happyToolsDir: node_path.resolve(index.projectPath(), "tools", "unpacked"),
2980
3188
  startedFromDaemon: options.startedBy === "daemon",
2981
3189
  hostPid: process.pid,
2982
3190
  startedBy: options.startedBy || "terminal",
@@ -3065,11 +3273,7 @@ async function runClaude(credentials, options = {}) {
3065
3273
  onSessionHook: (sessionId, data) => {
3066
3274
  api.logger.debug(`[START] Session hook received: ${sessionId}`, data);
3067
3275
  if (currentSession) {
3068
- const previousSessionId = currentSession.sessionId;
3069
- if (previousSessionId !== sessionId) {
3070
- api.logger.debug(`[START] Claude session ID changed: ${previousSessionId} -> ${sessionId}`);
3071
- currentSession.onSessionFound(sessionId);
3072
- }
3276
+ currentSession.onSessionFound(sessionId, data);
3073
3277
  }
3074
3278
  }
3075
3279
  });