very-happy-cli 0.2.150 → 0.2.151

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 (50) hide show
  1. package/dist/{AcpBackend-C-dhGJgd.mjs → AcpBackend-BjTA5gUB.mjs} +2 -2
  2. package/dist/{AcpBackend-CXawaWWx.cjs → AcpBackend-blE9IEod.cjs} +2 -2
  3. package/dist/{AcpSessionManager-DKOtSqJc.mjs → AcpSessionManager-D-9mrp40.mjs} +1 -1
  4. package/dist/{AcpSessionManager-TrwEZR7y.cjs → AcpSessionManager-bF8PYu_B.cjs} +1 -1
  5. package/dist/{agentGuidance-Dla-BXO8.cjs → agentGuidance-BXa9ikrZ.cjs} +1 -1
  6. package/dist/{agentGuidance-DrfhtVP1.mjs → agentGuidance-Dvo3cZMW.mjs} +1 -1
  7. package/dist/{agentHome-Dm-Wjs38.mjs → agentHome-CeFDNzvq.mjs} +2 -2
  8. package/dist/{agentHome-rylNIkix.cjs → agentHome-D6MSfwnp.cjs} +2 -2
  9. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  10. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  11. package/dist/{config-Dhu6LU0h.mjs → config-C-A1XF62.mjs} +4 -4
  12. package/dist/{config-CgPu2wtS.cjs → config-Cadw1sAk.cjs} +4 -4
  13. package/dist/{index-DEVkDRas.cjs → index-BKGspSWk.cjs} +7 -7
  14. package/dist/{index-CpNYvMTJ.mjs → index-CIyjJy8D.mjs} +214 -59
  15. package/dist/{index-K6mUS0jF.cjs → index-CqtfRzug.cjs} +220 -62
  16. package/dist/{index-CzpooENj.mjs → index-DEp6CmmS.mjs} +8 -8
  17. package/dist/{index-BsYH1sjV.mjs → index-DYQgcgL2.mjs} +4 -4
  18. package/dist/{index-DvfCUuDK.cjs → index-vyRqLjDv.cjs} +4 -4
  19. package/dist/index.cjs +4 -4
  20. package/dist/index.mjs +4 -4
  21. package/dist/{installTerminalHooks-NIbsQ1wi.cjs → installTerminalHooks-Bf_adbl3.cjs} +4 -4
  22. package/dist/{installTerminalHooks-BF_tmw9V.mjs → installTerminalHooks-Ch_yTtEA.mjs} +4 -4
  23. package/dist/lib.cjs +2 -2
  24. package/dist/lib.mjs +2 -2
  25. package/dist/{limits-FThmm7WR.mjs → limits-DS-bnYYp.mjs} +7 -7
  26. package/dist/{limits-Dyma1REp.cjs → limits-DwcY3kyi.cjs} +7 -7
  27. package/dist/{mcp-_TVUSg8W.cjs → mcp-CMxEZ6-0.cjs} +4 -4
  28. package/dist/{mcp-DzydgXpf.mjs → mcp-zu8n8TkR.mjs} +4 -4
  29. package/dist/{piRuntime-ACOhil6t.mjs → piRuntime-Cu6cXZ6N.mjs} +2 -2
  30. package/dist/{piRuntime-CSAiJk0X.cjs → piRuntime-D3ny0mVg.cjs} +2 -2
  31. package/dist/{piTerminal-BlE4vbvZ.mjs → piTerminal-M677UP7y.mjs} +6 -6
  32. package/dist/{piTerminal-DHxGB3Ic.cjs → piTerminal-kDSd_r9X.cjs} +6 -6
  33. package/dist/{runGemini-BMYCU16c.cjs → runGemini-DuwdVV1O.cjs} +6 -6
  34. package/dist/{runGemini-D5IV1MoF.mjs → runGemini-DvJwQVGp.mjs} +6 -6
  35. package/dist/{runOpenClaw-CWPXrKMJ.mjs → runOpenClaw-BG82bF18.mjs} +5 -5
  36. package/dist/{runOpenClaw-Qs4eFtcS.cjs → runOpenClaw-BsvtCE_H.cjs} +5 -5
  37. package/dist/{send-qXWGilm8.cjs → send-aUkjhey7.cjs} +20 -6
  38. package/dist/{send-Dv0km8UI.mjs → send-o6WM-1G8.mjs} +20 -6
  39. package/dist/{sessions-CpIdDhH7.cjs → sessions-B3vP18kz.cjs} +56 -10
  40. package/dist/{sessions-DoULfKui.mjs → sessions-DlU3foY_.mjs} +56 -10
  41. package/dist/{spawn-DqzG1bTH.cjs → spawn-DH6_p4-f.cjs} +166 -27
  42. package/dist/spawn-DxHIIqhi.mjs +397 -0
  43. package/dist/{teams-ByEgUzsH.cjs → teams-BW8D1n3o.cjs} +4 -4
  44. package/dist/{teams-4mhNh5cj.mjs → teams-DoAA_CcN.mjs} +4 -4
  45. package/dist/{todo-D7SDXL0c.cjs → todo-Bcc2XxGh.cjs} +3 -3
  46. package/dist/{todo-C9IAUf0K.mjs → todo-BhJbG5eq.mjs} +3 -3
  47. package/dist/{types-BD542hb1.mjs → types-BN3eiipi.mjs} +46 -3
  48. package/dist/{types-D2Gkpe0N.cjs → types-CPubg05Z.cjs} +52 -2
  49. package/package.json +7 -7
  50. package/dist/spawn-DQuOmUP7.mjs +0 -258
@@ -3,14 +3,14 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var index = require('./types-D2Gkpe0N.cjs');
7
- var persistence = require('./limits-Dyma1REp.cjs');
6
+ var index = require('./types-CPubg05Z.cjs');
7
+ var persistence = require('./limits-DwcY3kyi.cjs');
8
8
  var spawn = require('cross-spawn');
9
9
  var path = require('node:path');
10
10
  var node_readline = require('node:readline');
11
11
  var node_fs = require('node:fs');
12
12
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
13
- var agentGuidance = require('./agentGuidance-Dla-BXO8.cjs');
13
+ var agentGuidance = require('./agentGuidance-BXa9ikrZ.cjs');
14
14
  var promises = require('node:fs/promises');
15
15
  var fs = require('fs/promises');
16
16
  var ink = require('ink');
@@ -478,17 +478,23 @@ function readClaudeSettings() {
478
478
  }
479
479
  function shouldIncludeCoAuthoredBy() {
480
480
  const settings = readClaudeSettings();
481
- if (!settings || settings.includeCoAuthoredBy === void 0) {
481
+ if (!settings) {
482
+ return true;
483
+ }
484
+ if (settings.attribution?.commit === "") {
485
+ return false;
486
+ }
487
+ if (settings.includeCoAuthoredBy === void 0) {
482
488
  return true;
483
489
  }
484
490
  return settings.includeCoAuthoredBy;
485
491
  }
486
492
 
487
493
  const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
488
- ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
494
+ This chat is titled automatically from the user's first message. Call "mcp__happy__change_title" only when the topic has clearly moved on or the current title is too generic to find this chat later. Do not call it as a ritual at the start of a chat, and not for a quick question you can just answer.
489
495
  `))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + index.BUILTIN_TODO_DISCOVERY;
490
496
  const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
491
- When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
497
+ When you yourself create a git commit, instead of just giving co-credit to Claude, also give credit to Happy like so:
492
498
 
493
499
  <main commit message>
494
500
 
@@ -496,6 +502,8 @@ const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
496
502
  via [Very Happy](https://github.com/Mereithhh/very-happy)
497
503
 
498
504
  Co-Authored-By: Claude <noreply@anthropic.com>
505
+
506
+ Do not add these lines to commit message text you only draft for the user.
499
507
  `))();
500
508
  const systemPrompt = (() => {
501
509
  const includeCoAuthored = shouldIncludeCoAuthoredBy();
@@ -892,7 +900,7 @@ async function createSessionScanner(opts) {
892
900
  let processedMessageKeys = /* @__PURE__ */ new Set();
893
901
  let deadSessions = /* @__PURE__ */ new Set();
894
902
  if (opts.sessionId) {
895
- let messages = await readSessionLog(projectDir, opts.sessionId);
903
+ let messages = await readSessionLog$1(projectDir, opts.sessionId);
896
904
  persistence.logger.debug(`[SESSION_SCANNER] Marking ${messages.length} existing messages as processed from session ${opts.sessionId}`);
897
905
  for (let m of messages) {
898
906
  processedMessageKeys.add(messageKey(m));
@@ -915,7 +923,7 @@ async function createSessionScanner(opts) {
915
923
  }
916
924
  }
917
925
  for (let session of sessions) {
918
- const sessionMessages = await readSessionLog(projectDir, session);
926
+ const sessionMessages = await readSessionLog$1(projectDir, session);
919
927
  let skipped = 0;
920
928
  let sent = 0;
921
929
  for (let file of sessionMessages) {
@@ -992,7 +1000,7 @@ async function createSessionScanner(opts) {
992
1000
  return;
993
1001
  }
994
1002
  if (options?.treatExistingAsProcessed) {
995
- const existing = await readSessionLog(projectDir, sessionId);
1003
+ const existing = await readSessionLog$1(projectDir, sessionId);
996
1004
  persistence.logger.debug(`[SESSION_SCANNER] Pre-marking ${existing.length} existing messages as processed for new session ${sessionId}`);
997
1005
  for (const m of existing) {
998
1006
  processedMessageKeys.add(messageKey(m));
@@ -1020,7 +1028,7 @@ function messageKey(message) {
1020
1028
  throw Error();
1021
1029
  }
1022
1030
  }
1023
- async function readSessionLog(projectDir, sessionId) {
1031
+ async function readSessionLog$1(projectDir, sessionId) {
1024
1032
  const expectedSessionFile = path.join(projectDir, `${sessionId}.jsonl`);
1025
1033
  persistence.logger.debug(`[SESSION_SCANNER] Reading session file: ${expectedSessionFile}`);
1026
1034
  let file;
@@ -5394,8 +5402,8 @@ ${typeLabels[type] || type}:`));
5394
5402
  }
5395
5403
  console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
5396
5404
  try {
5397
- const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-rylNIkix.cjs'); });
5398
- const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-D2Gkpe0N.cjs'); }).then(function (n) { return n.index; });
5405
+ const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-D6MSfwnp.cjs'); });
5406
+ const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-CPubg05Z.cjs'); }).then(function (n) { return n.index; });
5399
5407
  const homes = await resolveAgentHomesForCli();
5400
5408
  console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
5401
5409
  console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
@@ -6106,7 +6114,9 @@ async function sendUserMessage(sessionId, persisted, text, client, options = {})
6106
6114
  },
6107
6115
  meta: {
6108
6116
  sentFrom: options.sentFrom ?? "cli",
6109
- ...options.model ? { model: options.model } : {}
6117
+ ...options.model !== void 0 ? { model: options.model } : {},
6118
+ ...options.permissionMode !== void 0 ? { permissionMode: options.permissionMode } : {},
6119
+ ...options.effort !== void 0 ? { effort: options.effort } : {}
6110
6120
  }
6111
6121
  };
6112
6122
  const encrypted = persistence.encodeBase64(persistence.encrypt(
@@ -6417,13 +6427,22 @@ function startDaemonControlServer({
6417
6427
  permissionMode: z.z.string().optional(),
6418
6428
  // B-069: spawn-origin tag ('assistant' = session_spawn). Recorded on
6419
6429
  // the TrackedSession; old clients never send it.
6420
- spawnedBy: z.z.string().optional()
6430
+ spawnedBy: z.z.string().optional(),
6431
+ // B-492: `very-happy spawn --fork` — same fields the web's fork flow
6432
+ // sends over the machine RPC. Old CLIs never send them.
6433
+ resumeClaudeSessionId: z.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).optional(),
6434
+ resumeCodexThreadId: z.z.string().min(1).max(200).optional(),
6435
+ parentSessionId: z.z.string().min(1).max(200).optional()
6421
6436
  }),
6422
6437
  response: {
6423
6438
  200: z.z.object({
6424
6439
  success: z.z.boolean(),
6425
6440
  sessionId: z.z.string().optional(),
6426
- approvedNewDirectoryCreation: z.z.boolean().optional()
6441
+ approvedNewDirectoryCreation: z.z.boolean().optional(),
6442
+ // B-492: true when resumeClaudeSessionId / resumeCodexThreadId was
6443
+ // honoured. A caller that asked for a fork and does not get this
6444
+ // back is talking to an older daemon that spawned a fresh session.
6445
+ resumed: z.z.boolean().optional()
6427
6446
  }),
6428
6447
  409: z.z.object({
6429
6448
  success: z.z.boolean(),
@@ -6438,13 +6457,14 @@ function startDaemonControlServer({
6438
6457
  }
6439
6458
  }
6440
6459
  }, async (request, reply) => {
6441
- const { directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy } = request.body;
6460
+ const { directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId } = request.body;
6442
6461
  if (!directory && variant !== "assistant") {
6443
6462
  reply.code(500);
6444
6463
  return { success: false, error: "directory is required" };
6445
6464
  }
6446
6465
  persistence.logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}, agent=${agent || "default"}, variant=${variant || "none"}, forceNew=${forceNew === true}, spawnedBy=${spawnedBy || "unset"}`);
6447
- const result = await spawnSession({ directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy });
6466
+ const result = await spawnSession({ directory, sessionId, agent, environmentVariables, variant, forceNew, permissionMode, spawnedBy, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId });
6467
+ const resumed = Boolean(resumeClaudeSessionId || resumeCodexThreadId);
6448
6468
  switch (result.type) {
6449
6469
  case "success":
6450
6470
  if (!result.sessionId) {
@@ -6457,7 +6477,8 @@ function startDaemonControlServer({
6457
6477
  return {
6458
6478
  success: true,
6459
6479
  sessionId: result.sessionId,
6460
- approvedNewDirectoryCreation: true
6480
+ approvedNewDirectoryCreation: true,
6481
+ ...resumed ? { resumed: true } : {}
6461
6482
  };
6462
6483
  case "requestToApproveDirectoryCreation":
6463
6484
  reply.code(409);
@@ -8123,7 +8144,7 @@ function parseJsonObject(text) {
8123
8144
  return null;
8124
8145
  }
8125
8146
  }
8126
- function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-K6mUS0jF.cjs', document.baseURI).href))).resolve(s)) {
8147
+ function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href))).resolve(s)) {
8127
8148
  const platformArch = `${platform}-${arch}`;
8128
8149
  const candidates = [
8129
8150
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -9389,6 +9410,8 @@ ${goal}`;
9389
9410
 
9390
9411
  const MAX_LINE_CHARS = 500;
9391
9412
  const MAX_ARGS_CHARS = 160;
9413
+ const DEFAULT_LIMITS = { line: MAX_LINE_CHARS, args: MAX_ARGS_CHARS };
9414
+ const NO_LIMITS = { line: Number.POSITIVE_INFINITY, args: Number.POSITIVE_INFINITY };
9392
9415
  function truncateText(text, max = MAX_LINE_CHARS) {
9393
9416
  const chars = Array.from(text);
9394
9417
  if (chars.length <= max) return text;
@@ -9404,12 +9427,12 @@ function safeJson(value, max) {
9404
9427
  return "[unserializable]";
9405
9428
  }
9406
9429
  }
9407
- function formatMessageBody(body) {
9430
+ function formatMessageBody(body, limits = DEFAULT_LIMITS) {
9408
9431
  if (!body || typeof body !== "object") return null;
9409
9432
  const b = body;
9410
9433
  if (b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string") {
9411
9434
  const text = squash(b.content.text);
9412
- return text.length > 0 ? `user: ${truncateText(text)}` : null;
9435
+ return text.length > 0 ? `user: ${truncateText(text, limits.line)}` : null;
9413
9436
  }
9414
9437
  if (b.role === "session" && b.content && typeof b.content === "object") {
9415
9438
  const env = b.content;
@@ -9421,18 +9444,18 @@ function formatMessageBody(body) {
9421
9444
  const text = squash(String(ev.text ?? ""));
9422
9445
  if (text.length === 0) return null;
9423
9446
  const role = env.role === "user" ? "user" : "assistant";
9424
- return `${role}: ${truncateText(text)}`;
9447
+ return `${role}: ${truncateText(text, limits.line)}`;
9425
9448
  }
9426
9449
  case "tool-call-start": {
9427
9450
  const name = String(ev.name ?? "tool");
9428
9451
  const title = squash(String(ev.title ?? ""));
9429
- const args = ev.args ? safeJson(ev.args, MAX_ARGS_CHARS) : "";
9452
+ const args = ev.args ? safeJson(ev.args, limits.args) : "";
9430
9453
  const detail = title || args;
9431
- return `[tool] ${name}${detail ? `: ${truncateText(detail, MAX_ARGS_CHARS)}` : ""}`;
9454
+ return `[tool] ${name}${detail ? `: ${truncateText(detail, limits.args)}` : ""}`;
9432
9455
  }
9433
9456
  case "service": {
9434
9457
  const text = squash(String(ev.text ?? ""));
9435
- return text.length > 0 ? `[service] ${truncateText(text, MAX_ARGS_CHARS)}` : null;
9458
+ return text.length > 0 ? `[service] ${truncateText(text, limits.args)}` : null;
9436
9459
  }
9437
9460
  case "turn-end": {
9438
9461
  if (ev.status && ev.status !== "completed") return `[turn ${ev.status}]`;
@@ -9447,12 +9470,12 @@ function formatMessageBody(body) {
9447
9470
  switch (d.type) {
9448
9471
  case "message": {
9449
9472
  const text = squash(String(d.message ?? ""));
9450
- return text.length > 0 ? `assistant: ${truncateText(text)}` : null;
9473
+ return text.length > 0 ? `assistant: ${truncateText(text, limits.line)}` : null;
9451
9474
  }
9452
9475
  case "tool-call":
9453
- return `[tool] ${String(d.name ?? "tool")}: ${safeJson(d.input, MAX_ARGS_CHARS)}`;
9476
+ return `[tool] ${String(d.name ?? "tool")}: ${safeJson(d.input, limits.args)}`;
9454
9477
  case "permission-request":
9455
- return `[permission] ${String(d.toolName ?? "")}: ${truncateText(squash(String(d.description ?? "")), MAX_ARGS_CHARS)}`;
9478
+ return `[permission] ${String(d.toolName ?? "")}: ${truncateText(squash(String(d.description ?? "")), limits.args)}`;
9456
9479
  default:
9457
9480
  return null;
9458
9481
  }
@@ -9463,22 +9486,111 @@ function formatMessageBody(body) {
9463
9486
  const texts = msg.content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => squash(c.text)).filter((t) => t.length > 0);
9464
9487
  if (texts.length > 0) {
9465
9488
  const role = msg.role === "user" ? "user" : "assistant";
9466
- return `${role}: ${truncateText(texts.join(" "))}`;
9489
+ return `${role}: ${truncateText(texts.join(" "), limits.line)}`;
9467
9490
  }
9468
9491
  }
9469
9492
  return null;
9470
9493
  }
9471
9494
  return null;
9472
9495
  }
9473
- function formatTranscript(bodies) {
9496
+ function formatTranscript(bodies, options = {}) {
9497
+ const limits = options.full ? NO_LIMITS : DEFAULT_LIMITS;
9474
9498
  const lines = [];
9475
9499
  for (const body of bodies) {
9476
- const line = formatMessageBody(body);
9500
+ const line = formatMessageBody(body, limits);
9477
9501
  if (line !== null) lines.push(line);
9478
9502
  }
9479
9503
  return lines.join("\n");
9480
9504
  }
9481
9505
 
9506
+ function asRecord(value) {
9507
+ return value && typeof value === "object" ? value : null;
9508
+ }
9509
+ function sessionEnvelope(body) {
9510
+ const b = asRecord(body);
9511
+ if (!b || b.role !== "session") return null;
9512
+ const env = asRecord(b.content);
9513
+ if (!env) return null;
9514
+ const ev = asRecord(env.ev);
9515
+ return ev && typeof ev.t === "string" ? { role: env.role, ev } : null;
9516
+ }
9517
+ function isUserPrompt(body) {
9518
+ const b = asRecord(body);
9519
+ return !!b && b.role === "user" && b.content?.type === "text" && typeof b.content.text === "string";
9520
+ }
9521
+ function agentPiece(body) {
9522
+ const env = sessionEnvelope(body);
9523
+ if (env && env.role !== "user") {
9524
+ if (env.ev.t === "text" && !env.ev.thinking) {
9525
+ const text = String(env.ev.text ?? "");
9526
+ return text.trim().length > 0 ? { kind: "text", text } : null;
9527
+ }
9528
+ if (env.ev.t === "tool-call-start") return { kind: "tool" };
9529
+ return null;
9530
+ }
9531
+ const b = asRecord(body);
9532
+ if (b?.role === "agent" && b.content?.type === "acp") {
9533
+ const d = asRecord(b.content.data);
9534
+ if (d?.type === "message") {
9535
+ const text = String(d.message ?? "");
9536
+ return text.trim().length > 0 ? { kind: "text", text } : null;
9537
+ }
9538
+ if (d?.type === "tool-call") return { kind: "tool" };
9539
+ }
9540
+ return null;
9541
+ }
9542
+ function analyzeLatestTurn(entries) {
9543
+ const lastSeq = entries.length > 0 ? entries[entries.length - 1].seq : 0;
9544
+ let userIndex = -1;
9545
+ for (let i = entries.length - 1; i >= 0; i--) {
9546
+ if (isUserPrompt(entries[i].body)) {
9547
+ userIndex = i;
9548
+ break;
9549
+ }
9550
+ }
9551
+ let ended = false;
9552
+ let status = null;
9553
+ let error = null;
9554
+ let texts = [];
9555
+ let lastMarker = null;
9556
+ let startedAfterPrompt = false;
9557
+ const emitsStarts = entries.some((entry) => sessionEnvelope(entry.body)?.ev?.t === "turn-start");
9558
+ for (let i = userIndex + 1; i < entries.length; i++) {
9559
+ const body = entries[i].body;
9560
+ const env = sessionEnvelope(body);
9561
+ if (env?.ev?.t === "turn-start") {
9562
+ lastMarker = "start";
9563
+ startedAfterPrompt = true;
9564
+ ended = false;
9565
+ status = null;
9566
+ error = null;
9567
+ texts = [];
9568
+ continue;
9569
+ }
9570
+ if (env?.ev?.t === "turn-end") {
9571
+ lastMarker = "end";
9572
+ if (userIndex >= 0 && emitsStarts && !startedAfterPrompt) continue;
9573
+ ended = true;
9574
+ status = typeof env.ev.status === "string" ? env.ev.status : null;
9575
+ error = typeof env.ev.error === "string" ? env.ev.error : null;
9576
+ continue;
9577
+ }
9578
+ const piece = agentPiece(body);
9579
+ if (!piece) continue;
9580
+ if (piece.kind === "tool") texts = [];
9581
+ else texts.push(piece.text);
9582
+ }
9583
+ if (userIndex < 0) ended = lastMarker === "end";
9584
+ return {
9585
+ userSeq: userIndex >= 0 ? entries[userIndex].seq : null,
9586
+ ended,
9587
+ status: ended ? status : null,
9588
+ error: ended ? error : null,
9589
+ answer: texts.join("\n\n").trim(),
9590
+ lastSeq
9591
+ };
9592
+ }
9593
+
9482
9594
  const USER_SOCKET_CONNECT_TIMEOUT_MS = 15e3;
9483
9595
  async function openUserScopedSocket(token, options = {}) {
9484
9596
  const socket = socket_ioClient.io(persistence.configuration.serverUrl, {
@@ -9824,19 +9936,21 @@ async function bearerToken() {
9824
9936
  if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9825
9937
  return credentials.token;
9826
9938
  }
9827
- async function readSessionTranscript(sessionId, limit) {
9939
+ function requirePersisted(sessionId) {
9828
9940
  const persisted = persistence.readPersistedSessions()[sessionId];
9829
9941
  if (!persisted) {
9830
9942
  throw new Error(
9831
9943
  `No local key for session ${sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`
9832
9944
  );
9833
9945
  }
9834
- const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
9946
+ return persisted;
9947
+ }
9948
+ async function readSessionLog(sessionId, persisted, limit) {
9835
9949
  const token = await bearerToken();
9836
9950
  const response = await axios.get(
9837
9951
  `${persistence.configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
9838
9952
  {
9839
- params: { before_seq: 2147483647, limit: bounded },
9953
+ params: { before_seq: 2147483647, limit },
9840
9954
  headers: {
9841
9955
  "Authorization": `Bearer ${token}`,
9842
9956
  "X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
@@ -9847,21 +9961,62 @@ async function readSessionTranscript(sessionId, limit) {
9847
9961
  const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9848
9962
  messages.reverse();
9849
9963
  const key = persistence.decodeBase64(persisted.encryptionKey);
9850
- const live = await index.listDaemonSessions();
9851
- const bodies = messages.map((message) => {
9852
- if (message.content?.t !== "encrypted") return null;
9964
+ return messages.map((message) => {
9965
+ if (message.content?.t !== "encrypted") return { seq: message.seq, body: null };
9853
9966
  try {
9854
- return persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(message.content.c));
9967
+ return { seq: message.seq, body: persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(message.content.c)) };
9855
9968
  } catch {
9856
- return null;
9969
+ return { seq: message.seq, body: null };
9857
9970
  }
9858
9971
  });
9972
+ }
9973
+ const TURN_WINDOW = 500;
9974
+ async function readSessionTranscript(sessionId, limit, options = {}) {
9975
+ const persisted = requirePersisted(sessionId);
9976
+ const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
9977
+ const entries = await readSessionLog(sessionId, persisted, Math.max(bounded, TURN_WINDOW));
9978
+ const shown = entries.slice(-bounded);
9979
+ const live = await index.listDaemonSessions();
9859
9980
  return {
9860
9981
  summary: toSummary(sessionId, persisted, sessionLiveness(live, sessionId)),
9861
- messageCount: messages.length,
9862
- transcript: formatTranscript(bodies)
9982
+ messageCount: shown.length,
9983
+ transcript: formatTranscript(shown.map((entry) => entry.body), { full: options.full }),
9984
+ turn: analyzeLatestTurn(entries)
9863
9985
  };
9864
9986
  }
9987
+ async function readSessionMetadata(sessionId, persisted) {
9988
+ const token = await bearerToken();
9989
+ const response = await axios.get(`${persistence.configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
9990
+ headers: {
9991
+ "Authorization": `Bearer ${token}`,
9992
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
9993
+ },
9994
+ timeout: 15e3
9995
+ });
9996
+ const ciphertext = response.data?.session?.metadata;
9997
+ if (typeof ciphertext !== "string" || ciphertext.length === 0) return null;
9998
+ return persistence.decrypt(persistence.decodeBase64(persisted.encryptionKey), persisted.encryptionVariant, persistence.decodeBase64(ciphertext));
9999
+ }
10000
+ class TurnWaitTimeoutError extends Error {
10001
+ constructor(sessionId, timeoutMs, turn) {
10002
+ super(`Session ${sessionId}: the latest turn did not end within ${Math.round(timeoutMs / 1e3)}s`);
10003
+ this.sessionId = sessionId;
10004
+ this.timeoutMs = timeoutMs;
10005
+ this.turn = turn;
10006
+ }
10007
+ }
10008
+ async function waitForTurnEnd(sessionId, options) {
10009
+ const persisted = requirePersisted(sessionId);
10010
+ const pollMs = options.pollMs ?? 3e3;
10011
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
10012
+ const deadline = Date.now() + options.timeoutMs;
10013
+ while (true) {
10014
+ const turn = analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW));
10015
+ if (turn.ended) return turn;
10016
+ if (Date.now() + pollMs > deadline) throw new TurnWaitTimeoutError(sessionId, options.timeoutMs, turn);
10017
+ await sleep(pollMs);
10018
+ }
10019
+ }
9865
10020
  async function stopSession(sessionId) {
9866
10021
  return await index.stopDaemonSession(sessionId);
9867
10022
  }
@@ -12599,7 +12754,7 @@ function registerSideQuestionHandler(rpc, deps) {
12599
12754
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12600
12755
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12601
12756
  const fork = deps.run ?? (async (input) => {
12602
- const { query } = await Promise.resolve().then(function () { return require('./index-DvfCUuDK.cjs'); });
12757
+ const { query } = await Promise.resolve().then(function () { return require('./index-vyRqLjDv.cjs'); });
12603
12758
  return runSideQuestion(query, input);
12604
12759
  });
12605
12760
  const run = async (input, live) => {
@@ -12718,7 +12873,7 @@ const MAX_TITLE_CHARS = 60;
12718
12873
  const GENERATION_TIMEOUT_MS = 3e4;
12719
12874
  function resolveClaudeBinary() {
12720
12875
  try {
12721
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-K6mUS0jF.cjs', document.baseURI).href)));
12876
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
12722
12877
  const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
12723
12878
  const { getClaudeCliPath } = require$1(utilsPath);
12724
12879
  const path$1 = getClaudeCliPath();
@@ -14947,9 +15102,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
14947
15102
  };
14948
15103
  }
14949
15104
 
14950
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-K6mUS0jF.cjs', document.baseURI).href)));
15105
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
14951
15106
  const __dirname$1 = path.dirname(__filename$1);
14952
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-K6mUS0jF.cjs', document.baseURI).href)));
15107
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
14953
15108
  const PRISMA_QUERY_ENGINE_FILES = {
14954
15109
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
14955
15110
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -17887,7 +18042,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17887
18042
  return;
17888
18043
  } else if (subcommand === "install-terminal-hooks") {
17889
18044
  try {
17890
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-NIbsQ1wi.cjs'); });
18045
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-Bf_adbl3.cjs'); });
17891
18046
  const command = parseTerminalHooksArgs(args.slice(1));
17892
18047
  if (command.action === "help") {
17893
18048
  console.log(TERMINAL_HOOKS_HELP);
@@ -17904,7 +18059,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17904
18059
  }
17905
18060
  } else if (subcommand === "agent-home") {
17906
18061
  try {
17907
- const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-rylNIkix.cjs'); });
18062
+ const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-D6MSfwnp.cjs'); });
17908
18063
  await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
17909
18064
  process.exit(0);
17910
18065
  } catch (error) {
@@ -17913,7 +18068,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17913
18068
  }
17914
18069
  } else if (subcommand === "spawn") {
17915
18070
  try {
17916
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DqzG1bTH.cjs'); });
18071
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DH6_p4-f.cjs'); });
17917
18072
  await handleSpawnCommand(args.slice(1));
17918
18073
  } catch (error) {
17919
18074
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17925,7 +18080,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17925
18080
  return;
17926
18081
  } else if (subcommand === "sessions") {
17927
18082
  try {
17928
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-CpIdDhH7.cjs'); });
18083
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-B3vP18kz.cjs'); });
17929
18084
  await handleSessionsCommand(args.slice(1));
17930
18085
  } catch (error) {
17931
18086
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17937,7 +18092,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17937
18092
  return;
17938
18093
  } else if (subcommand === "send") {
17939
18094
  try {
17940
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-qXWGilm8.cjs'); });
18095
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-aUkjhey7.cjs'); });
17941
18096
  await handleSendCommand(args.slice(1));
17942
18097
  } catch (error) {
17943
18098
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18043,9 +18198,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18043
18198
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
18044
18199
  const projectId = args[3];
18045
18200
  try {
18046
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-CgPu2wtS.cjs'); });
18047
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-Dyma1REp.cjs'); }).then(function (n) { return n.persistence; });
18048
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-D2Gkpe0N.cjs'); }).then(function (n) { return n.api; });
18201
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-Cadw1sAk.cjs'); });
18202
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DwcY3kyi.cjs'); }).then(function (n) { return n.persistence; });
18203
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CPubg05Z.cjs'); }).then(function (n) { return n.api; });
18049
18204
  let userEmail = void 0;
18050
18205
  try {
18051
18206
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -18076,7 +18231,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18076
18231
  }
18077
18232
  if (geminiSubcommand === "project" && args[2] === "get") {
18078
18233
  try {
18079
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-CgPu2wtS.cjs'); });
18234
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-Cadw1sAk.cjs'); });
18080
18235
  const config = readGeminiLocalConfig();
18081
18236
  if (config.googleCloudProject) {
18082
18237
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -18116,7 +18271,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18116
18271
  process.exit(0);
18117
18272
  }
18118
18273
  try {
18119
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-BMYCU16c.cjs'); });
18274
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-DuwdVV1O.cjs'); });
18120
18275
  let startedBy = void 0;
18121
18276
  for (let i = 1; i < args.length; i++) {
18122
18277
  if (args[i] === "--started-by") {
@@ -18139,7 +18294,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18139
18294
  } else if (subcommand === "pi") {
18140
18295
  if (args[1] === "--terminal") {
18141
18296
  try {
18142
- const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-DHxGB3Ic.cjs'); });
18297
+ const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-kDSd_r9X.cjs'); });
18143
18298
  process.exitCode = await runPiTerminal(args.slice(2));
18144
18299
  } catch (error) {
18145
18300
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18156,7 +18311,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18156
18311
  }
18157
18312
  };
18158
18313
  try {
18159
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEVkDRas.cjs'); });
18314
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BKGspSWk.cjs'); });
18160
18315
  const parsed = parsePiRunnerArgs(args.slice(1));
18161
18316
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
18162
18317
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -18184,7 +18339,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18184
18339
  return;
18185
18340
  } else if (subcommand === "acp") {
18186
18341
  try {
18187
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEVkDRas.cjs'); });
18342
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BKGspSWk.cjs'); });
18188
18343
  let startedBy = void 0;
18189
18344
  let verbose = false;
18190
18345
  const acpArgs = [];
@@ -18224,7 +18379,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18224
18379
  return;
18225
18380
  } else if (subcommand === "openclaw") {
18226
18381
  try {
18227
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-Qs4eFtcS.cjs'); });
18382
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-BsvtCE_H.cjs'); });
18228
18383
  let startedBy = void 0;
18229
18384
  let verbose = false;
18230
18385
  let gatewayUrl;
@@ -18275,7 +18430,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18275
18430
  return;
18276
18431
  } else if (subcommand === "todo") {
18277
18432
  try {
18278
- const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-D7SDXL0c.cjs'); });
18433
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-Bcc2XxGh.cjs'); });
18279
18434
  await handleTodoCommand(args.slice(1));
18280
18435
  } catch (error) {
18281
18436
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -18284,7 +18439,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18284
18439
  return;
18285
18440
  } else if (subcommand === "teams") {
18286
18441
  try {
18287
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-ByEgUzsH.cjs'); });
18442
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-BW8D1n3o.cjs'); });
18288
18443
  await handleTeamsCommand(args.slice(1));
18289
18444
  } catch (error) {
18290
18445
  console.error(error instanceof Error ? error.message : "Teams command failed");
@@ -18293,7 +18448,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18293
18448
  return;
18294
18449
  } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
18295
18450
  try {
18296
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-_TVUSg8W.cjs'); });
18451
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-CMxEZ6-0.cjs'); });
18297
18452
  await handleMcpCommand(args[1] === "--terminal-tools");
18298
18453
  } catch (error) {
18299
18454
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -18679,6 +18834,7 @@ exports.SPAWN_AGENTS = SPAWN_AGENTS;
18679
18834
  exports.StreamRelay = StreamRelay;
18680
18835
  exports.TEAM_SKILL = TEAM_SKILL;
18681
18836
  exports.TitleGenerator = TitleGenerator;
18837
+ exports.TurnWaitTimeoutError = TurnWaitTimeoutError;
18682
18838
  exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
18683
18839
  exports.archiveSession = archiveSession;
18684
18840
  exports.checkPreviewPath = checkPreviewPath;
@@ -18690,6 +18846,7 @@ exports.listAccountSessions = listAccountSessions;
18690
18846
  exports.listSessions = listSessions;
18691
18847
  exports.normalizeAcpPermissionMode = normalizeAcpPermissionMode;
18692
18848
  exports.query = query;
18849
+ exports.readSessionMetadata = readSessionMetadata;
18693
18850
  exports.readSessionTranscript = readSessionTranscript;
18694
18851
  exports.registerAgentAttachmentDownloads = registerAgentAttachmentDownloads;
18695
18852
  exports.registerAssistantSessionTools = registerAssistantSessionTools;
@@ -18705,4 +18862,5 @@ exports.startHappyServer = startHappyServer;
18705
18862
  exports.stopSession = stopSession;
18706
18863
  exports.tmuxSupportsSessionEnv = tmuxSupportsSessionEnv;
18707
18864
  exports.waitForSessionKey = waitForSessionKey;
18865
+ exports.waitForTurnEnd = waitForTurnEnd;
18708
18866
  exports.writeSessionModeFile = writeSessionModeFile;