very-happy-cli 0.2.108 → 0.2.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{AcpBackend-D4vV6SVl.cjs → AcpBackend-D0_wbTs6.cjs} +1 -1
  2. package/dist/{AcpBackend-DwR8rrkS.mjs → AcpBackend-DzZR7TEv.mjs} +1 -1
  3. package/dist/{AcpSessionManager-CPHDZ-nz.cjs → AcpSessionManager-Dcj_iHw8.cjs} +1 -1
  4. package/dist/{AcpSessionManager-C057dkLF.mjs → AcpSessionManager-tWuFWeIF.mjs} +1 -1
  5. package/dist/{config-BvVwprR0.cjs → config-CTCGilz_.cjs} +2 -2
  6. package/dist/{config-DKMwLc1k.mjs → config-CTyyVXHj.mjs} +2 -2
  7. package/dist/{index-CzSv4d_S.mjs → index-BW9kDx5m.mjs} +237 -91
  8. package/dist/{index-BXnHT-Wu.mjs → index-Brhvj7p0.mjs} +5 -5
  9. package/dist/{index-DM0Ku1Rv.cjs → index-CvnMiYgh.cjs} +247 -94
  10. package/dist/{index-Db5m34eh.cjs → index-D-adqB7g.cjs} +4 -4
  11. package/dist/{index-C7N-ONU_.cjs → index-DW78HyvK.cjs} +2 -2
  12. package/dist/{index-DYY1DUOD.mjs → index-jYltd-ZW.mjs} +2 -2
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-Cs49gg_E.mjs → installTerminalHooks-CJyoR9jp.mjs} +2 -2
  16. package/dist/{installTerminalHooks-BpvWmkZ9.cjs → installTerminalHooks-DhQ6k7YD.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-DY17lhMH.mjs → mcp-BO9-sxMr.mjs} +2 -2
  20. package/dist/{mcp-rTSmj-tn.cjs → mcp-Dr1PegOw.cjs} +2 -2
  21. package/dist/{runGemini-Bqs52hw_.mjs → runGemini-CuzqF6Ys.mjs} +4 -4
  22. package/dist/{runGemini-B3Mme1zr.cjs → runGemini-D6Wwfk_0.cjs} +4 -4
  23. package/dist/{runOpenClaw-CpbsQ7NZ.mjs → runOpenClaw-B5pkmSTb.mjs} +3 -3
  24. package/dist/{runOpenClaw-B2V9j9Le.cjs → runOpenClaw-BYmlS-4Q.cjs} +3 -3
  25. package/dist/{send-uHgpvIqm.mjs → send-Btz1IZ9U.mjs} +2 -2
  26. package/dist/{send-CLdMBQ8e.cjs → send-CuajooZC.cjs} +2 -2
  27. package/dist/sessions-CVUBS3LG.cjs +204 -0
  28. package/dist/sessions-DDwCphTQ.mjs +200 -0
  29. package/dist/{spawn-BXbVzbMs.cjs → spawn-CwS8CPbr.cjs} +16 -3
  30. package/dist/{spawn-Dt4ujmHH.mjs → spawn-DcA80_VN.mjs} +16 -3
  31. package/dist/{types-D4bwyM-D.cjs → types--t4zLibT.cjs} +18 -10
  32. package/dist/{types-D0-nhvuW.mjs → types-BdO0jg7G.mjs} +18 -10
  33. package/package.json +7 -7
@@ -3,7 +3,7 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var persistence = require('./types-D4bwyM-D.cjs');
6
+ var persistence = require('./types--t4zLibT.cjs');
7
7
  var spawn = require('cross-spawn');
8
8
  var path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -4444,16 +4444,23 @@ function claudeModeHash(mode) {
4444
4444
  });
4445
4445
  }
4446
4446
 
4447
- function assistantSpawnTags(env = process.env) {
4448
- if (env.HAPPY_SPAWNED_BY !== "assistant") return void 0;
4447
+ const SPAWN_ORIGIN_TAG_MAX = 24;
4448
+ function spawnOriginTags(env = process.env) {
4449
4449
  if (env.HAPPY_SESSION_VARIANT === "assistant") return void 0;
4450
- return ["assistant"];
4450
+ const origin = (env.HAPPY_SPAWNED_BY ?? "").trim();
4451
+ if (!origin) return void 0;
4452
+ if (origin.length > SPAWN_ORIGIN_TAG_MAX) return void 0;
4453
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(origin)) return void 0;
4454
+ return [origin];
4455
+ }
4456
+ function isValidSpawnOrigin(value) {
4457
+ return spawnOriginTags({ HAPPY_SPAWNED_BY: value }) !== void 0;
4451
4458
  }
4452
4459
  function createSessionMetadata(opts) {
4453
4460
  const state = {
4454
4461
  controlledByUser: false
4455
4462
  };
4456
- const assistantTags = assistantSpawnTags();
4463
+ const originTags = spawnOriginTags();
4457
4464
  const metadata = {
4458
4465
  path: process.cwd(),
4459
4466
  host: os.hostname(),
@@ -4474,10 +4481,10 @@ function createSessionMetadata(opts) {
4474
4481
  dangerouslySkipPermissions: opts.dangerouslySkipPermissions ?? null,
4475
4482
  ...opts.parentSessionId ? { parentSessionId: opts.parentSessionId } : {},
4476
4483
  ...opts.forkedFromMessageId ? { forkedFromMessageId: opts.forkedFromMessageId } : {},
4477
- // B-091: assistant-dispatched sessions carry the 'assistant' tag from
4478
- // birth (see assistantSpawnTags) — applies to every flavor the daemon
4484
+ // B-091/B-303: dispatched sessions carry their spawn origin as a tag
4485
+ // from birth (see spawnOriginTags) — applies to every flavor the daemon
4479
4486
  // can spawn, since HAPPY_SPAWNED_BY is exported flavor-independently.
4480
- ...assistantTags ? { tags: assistantTags } : {}
4487
+ ...originTags ? { tags: originTags } : {}
4481
4488
  };
4482
4489
  return { state, metadata };
4483
4490
  }
@@ -6936,7 +6943,7 @@ function parseJsonObject(text) {
6936
6943
  return null;
6937
6944
  }
6938
6945
  }
6939
- 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-DM0Ku1Rv.cjs', document.baseURI).href))).resolve(s)) {
6946
+ 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-CvnMiYgh.cjs', document.baseURI).href))).resolve(s)) {
6940
6947
  const platformArch = `${platform}-${arch}`;
6941
6948
  const candidates = [
6942
6949
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -7438,12 +7445,38 @@ function createMirrorScanner(opts) {
7438
7445
  };
7439
7446
  }
7440
7447
 
7448
+ const PENDING_CREATE_BACKOFF_MS = [1e4, 3e4, 6e4, 12e4, 3e5];
7449
+ const PENDING_CREATE_MAX_AGE_MS = 30 * 6e4;
7450
+ function backoffForAttempt(attempts) {
7451
+ const index = Math.min(Math.max(attempts, 1), PENDING_CREATE_BACKOFF_MS.length) - 1;
7452
+ return PENDING_CREATE_BACKOFF_MS[index];
7453
+ }
7454
+ function pendingCreateAfterFailure(previous, event, now) {
7455
+ const sameConversation = previous?.event.claudeSessionId === event.claudeSessionId;
7456
+ const attempts = sameConversation ? previous.attempts + 1 : 1;
7457
+ return {
7458
+ event,
7459
+ attempts,
7460
+ firstFailedAt: sameConversation ? previous.firstFailedAt : now,
7461
+ nextAttemptAt: now + backoffForAttempt(attempts)
7462
+ };
7463
+ }
7464
+ function planPendingCreate(pending, now) {
7465
+ if (now - pending.firstFailedAt >= PENDING_CREATE_MAX_AGE_MS) return "drop";
7466
+ return now >= pending.nextAttemptAt ? "retry" : "wait";
7467
+ }
7468
+ function pendingCreateSupersededBy(pending, event) {
7469
+ if (event.event === "SessionStart") return true;
7470
+ return pending.event.claudeSessionId === event.claudeSessionId;
7471
+ }
7472
+
7441
7473
  const TERMINAL_MIRROR_FLAVOR = "terminal-mirror";
7442
7474
  const RECONCILE_HYSTERESIS_MS = 2e4;
7443
7475
  function createMirrorManager(deps) {
7444
7476
  const backfillLines = deps.backfillLines ?? persistence.MIRROR_BACKFILL_LINES_DEFAULT;
7445
7477
  const bindings = /* @__PURE__ */ new Map();
7446
7478
  const lastEndedAt = /* @__PURE__ */ new Map();
7479
+ const pendingCreates = /* @__PURE__ */ new Map();
7447
7480
  let chain = Promise.resolve();
7448
7481
  const reactivate = async (binding) => {
7449
7482
  try {
@@ -7603,6 +7636,14 @@ function createMirrorManager(deps) {
7603
7636
  persistence.logger.debug(`[MIRROR] reconcile reactivated (in place) terminal ${binding.terminalId}`);
7604
7637
  deps.onBindingsChanged?.();
7605
7638
  };
7639
+ const parkFailedCreate = (event, reason) => {
7640
+ const pending = pendingCreateAfterFailure(pendingCreates.get(event.terminalId), event, Date.now());
7641
+ pendingCreates.set(event.terminalId, pending);
7642
+ persistence.logger.debug(
7643
+ `[MIRROR] mirror bind failed for terminal ${event.terminalId} (attempt ${pending.attempts}, retrying):`,
7644
+ reason
7645
+ );
7646
+ };
7606
7647
  const createBinding = async (event, replaces) => {
7607
7648
  if (replaces) {
7608
7649
  await teardownBinding(replaces, "superseded by a fresh conversation");
@@ -7624,11 +7665,18 @@ function createMirrorManager(deps) {
7624
7665
  terminalId: event.terminalId,
7625
7666
  claudeSessionId: event.claudeSessionId
7626
7667
  };
7627
- const response = await deps.api.getOrCreateSession({ tag: node_crypto.randomUUID(), metadata, state: {} });
7668
+ let response = null;
7669
+ try {
7670
+ response = await deps.api.getOrCreateSession({ tag: node_crypto.randomUUID(), metadata, state: {} });
7671
+ } catch (error) {
7672
+ parkFailedCreate(event, error);
7673
+ return;
7674
+ }
7628
7675
  if (!response) {
7629
- persistence.logger.debug(`[MIRROR] server unreachable \u2014 dropping mirror bind for terminal ${event.terminalId}`);
7676
+ parkFailedCreate(event, "server unreachable");
7630
7677
  return;
7631
7678
  }
7679
+ pendingCreates.delete(event.terminalId);
7632
7680
  const client = deps.api.sessionSyncClient(response);
7633
7681
  client.skipExistingMessages();
7634
7682
  const binding = {
@@ -7684,6 +7732,8 @@ function createMirrorManager(deps) {
7684
7732
  persistence.logger.debug(`[MIRROR] terminal ${binding.terminalId} mirror continues as claude ${event.claudeSessionId}`);
7685
7733
  };
7686
7734
  const handleEvent = async (event) => {
7735
+ const parked = pendingCreates.get(event.terminalId);
7736
+ if (parked && pendingCreateSupersededBy(parked, event)) pendingCreates.delete(event.terminalId);
7687
7737
  const binding = bindings.get(event.terminalId) ?? null;
7688
7738
  const decision = persistence.decideMirrorBinding(
7689
7739
  event,
@@ -7716,6 +7766,7 @@ function createMirrorManager(deps) {
7716
7766
  });
7717
7767
  },
7718
7768
  onTerminalClosed(terminalId) {
7769
+ pendingCreates.delete(terminalId);
7719
7770
  const binding = bindings.get(terminalId);
7720
7771
  if (!binding) return;
7721
7772
  chain = chain.then(() => teardownBinding(binding, "terminal closed")).catch((error) => {
@@ -7739,6 +7790,7 @@ function createMirrorManager(deps) {
7739
7790
  for (const item of list) {
7740
7791
  const binding = bindings.get(item.id) ?? null;
7741
7792
  if (item.agentState === "shell") {
7793
+ pendingCreates.delete(item.id);
7742
7794
  if (binding && binding.status === "active") {
7743
7795
  chain = chain.then(() => {
7744
7796
  const b = bindings.get(item.id);
@@ -7762,6 +7814,21 @@ function createMirrorManager(deps) {
7762
7814
  return b && b.status === "ended" ? reactivateInPlace(b) : void 0;
7763
7815
  }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile in-place reactivate failed for ${item.id}:`, e));
7764
7816
  } else if (!binding) {
7817
+ const pending = pendingCreates.get(item.id);
7818
+ if (pending) {
7819
+ const decision = planPendingCreate(pending, now);
7820
+ if (decision === "drop") {
7821
+ pendingCreates.delete(item.id);
7822
+ persistence.logger.debug(`[MIRROR] giving up on the parked mirror bind for terminal ${item.id}`);
7823
+ } else if (decision === "retry") {
7824
+ chain = chain.then(() => {
7825
+ const still = pendingCreates.get(item.id);
7826
+ if (!still || still !== pending || bindings.has(item.id)) return void 0;
7827
+ return createBinding(still.event);
7828
+ }).catch((e) => persistence.logger.debug(`[MIRROR] reconcile create retry failed for ${item.id}:`, e));
7829
+ }
7830
+ continue;
7831
+ }
7765
7832
  persisted ??= persistence.readPersistedSessions();
7766
7833
  const p = persisted;
7767
7834
  chain = chain.then(() => {
@@ -9319,6 +9386,109 @@ function formatTranscript(bodies) {
9319
9386
  return lines.join("\n");
9320
9387
  }
9321
9388
 
9389
+ const SESSION_OPS_CLIENT = "session-ops";
9390
+ const DEFAULT_RECENT_LIMIT = 15;
9391
+ const MAX_READ_LIMIT = 100;
9392
+ function mergeSessionSummaries(live, persisted, options = {}) {
9393
+ const recentLimit = Math.max(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);
9394
+ const seen = /* @__PURE__ */ new Set();
9395
+ const summaries = [];
9396
+ for (const child of live) {
9397
+ const id = typeof child.happySessionId === "string" ? child.happySessionId : void 0;
9398
+ if (!id || seen.has(id)) continue;
9399
+ seen.add(id);
9400
+ summaries.push(toSummary(id, persisted[id], {
9401
+ live: true,
9402
+ pid: typeof child.pid === "number" ? child.pid : void 0
9403
+ }));
9404
+ }
9405
+ const rest = Object.entries(persisted).filter(([id]) => !seen.has(id)).filter(([, entry]) => entry.metadata?.flavor !== "terminal-mirror").sort((a, b) => b[1].savedAt - a[1].savedAt).slice(0, recentLimit);
9406
+ for (const [id, entry] of rest) {
9407
+ summaries.push(toSummary(id, entry, { live: false }));
9408
+ }
9409
+ if (!options.tag) return summaries;
9410
+ const wanted = options.tag;
9411
+ return summaries.filter((summary) => summary.tags?.includes(wanted) === true);
9412
+ }
9413
+ function toSummary(id, persisted, extra) {
9414
+ const meta = persisted?.metadata;
9415
+ return {
9416
+ id,
9417
+ live: extra.live,
9418
+ ...extra.pid !== void 0 ? { pid: extra.pid } : {},
9419
+ ...meta?.summary?.text ? { title: meta.summary.text } : {},
9420
+ ...meta?.path ? { cwd: meta.path } : {},
9421
+ ...meta?.flavor ? { flavor: meta.flavor } : {},
9422
+ ...meta?.tags?.length ? { tags: [...meta.tags] } : {},
9423
+ ...meta?.variant ? { variant: meta.variant } : {},
9424
+ url: sessionWebUrl(id),
9425
+ ...persisted?.savedAt !== void 0 ? { savedAt: persisted.savedAt } : {}
9426
+ };
9427
+ }
9428
+ async function listSessions(options = {}) {
9429
+ const live = await listDaemonSessions();
9430
+ return mergeSessionSummaries(live, persistence.readPersistedSessions(), options);
9431
+ }
9432
+ async function bearerToken() {
9433
+ const credentials = await persistence.readCredentialsForConfiguredRelay();
9434
+ if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9435
+ return credentials.token;
9436
+ }
9437
+ async function readSessionTranscript(sessionId, limit) {
9438
+ const persisted = persistence.readPersistedSessions()[sessionId];
9439
+ if (!persisted) {
9440
+ throw new Error(
9441
+ `No local key for session ${sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`
9442
+ );
9443
+ }
9444
+ const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
9445
+ const token = await bearerToken();
9446
+ const response = await axios.get(
9447
+ `${persistence.configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
9448
+ {
9449
+ params: { before_seq: 2147483647, limit: bounded },
9450
+ headers: {
9451
+ "Authorization": `Bearer ${token}`,
9452
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
9453
+ },
9454
+ timeout: 15e3
9455
+ }
9456
+ );
9457
+ const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9458
+ messages.reverse();
9459
+ const key = persistence.decodeBase64(persisted.encryptionKey);
9460
+ const bodies = messages.map((message) => {
9461
+ if (message.content?.t !== "encrypted") return null;
9462
+ try {
9463
+ return persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(message.content.c));
9464
+ } catch {
9465
+ return null;
9466
+ }
9467
+ });
9468
+ return {
9469
+ summary: toSummary(sessionId, persisted, { live: false }),
9470
+ messageCount: messages.length,
9471
+ transcript: formatTranscript(bodies)
9472
+ };
9473
+ }
9474
+ async function stopSession(sessionId) {
9475
+ return await stopDaemonSession(sessionId);
9476
+ }
9477
+ async function archiveSession(sessionId) {
9478
+ const token = await bearerToken();
9479
+ await axios.post(
9480
+ `${persistence.configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}/archive`,
9481
+ {},
9482
+ {
9483
+ headers: {
9484
+ "Authorization": `Bearer ${token}`,
9485
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${persistence.configuration.currentCliVersion}`
9486
+ },
9487
+ timeout: 1e4
9488
+ }
9489
+ );
9490
+ }
9491
+
9322
9492
  const PERSONAL_MEMORY_SOFT_LIMIT_CHARS = 2e3;
9323
9493
  function headingTitle(line) {
9324
9494
  const m = /^##(?!#)\s*(.*?)\s*$/.exec(line);
@@ -9415,19 +9585,24 @@ function fail(text) {
9415
9585
  function describeSession(id, persisted, extra) {
9416
9586
  const meta = persisted?.metadata;
9417
9587
  const parts = [id];
9418
- if (extra?.live !== void 0) parts.push(extra.live ? "[running]" : "[not running]");
9419
9588
  if (meta?.summary?.text) parts.push(`title="${meta.summary.text}"`);
9420
9589
  if (meta?.path) parts.push(`cwd=${meta.path}`);
9421
9590
  if (meta?.flavor) parts.push(`agent=${meta.flavor}`);
9591
+ if (meta?.tags?.length) parts.push(`tags=${meta.tags.join(",")}`);
9422
9592
  if (meta?.variant === "assistant") parts.push("(assistant)");
9423
- if (extra?.pid) parts.push(`pid=${extra.pid}`);
9424
9593
  parts.push(sessionWebUrl(id));
9425
9594
  return parts.join(" ");
9426
9595
  }
9427
- async function bearerToken() {
9428
- const credentials = await persistence.readCredentialsForConfiguredRelay();
9429
- if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9430
- return credentials.token;
9596
+ function describeSummary(summary) {
9597
+ const parts = [summary.id, summary.live ? "[running]" : "[not running]"];
9598
+ if (summary.title) parts.push(`title="${summary.title}"`);
9599
+ if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
9600
+ if (summary.flavor) parts.push(`agent=${summary.flavor}`);
9601
+ if (summary.tags?.length) parts.push(`tags=${summary.tags.join(",")}`);
9602
+ if (summary.variant === "assistant") parts.push("(assistant)");
9603
+ if (summary.pid !== void 0) parts.push(`pid=${summary.pid}`);
9604
+ parts.push(summary.url);
9605
+ return parts.join(" ");
9431
9606
  }
9432
9607
  function registerAssistantTools(mcp) {
9433
9608
  mcp.registerTool("sessions_list", {
@@ -9436,22 +9611,10 @@ function registerAssistantTools(mcp) {
9436
9611
  inputSchema: {}
9437
9612
  }, async () => {
9438
9613
  try {
9439
- const live = await listDaemonSessions();
9440
- const persisted = persistence.readPersistedSessions();
9441
- const lines = [];
9442
- const seen = /* @__PURE__ */ new Set();
9443
- for (const child of live) {
9444
- const id = child.happySessionId;
9445
- seen.add(id);
9446
- lines.push(describeSession(id, persisted[id], { pid: child.pid, live: true }));
9447
- }
9448
- const rest = Object.entries(persisted).filter(([id]) => !seen.has(id)).filter(([, entry]) => entry.metadata?.flavor !== "terminal-mirror").sort((a, b) => b[1].savedAt - a[1].savedAt).slice(0, 15);
9449
- for (const [id, entry] of rest) {
9450
- lines.push(describeSession(id, entry, { live: false }));
9451
- }
9452
- if (lines.length === 0) return ok("No sessions found on this machine.");
9453
- return ok(`${lines.length} session(s):
9454
- ${lines.join("\n")}`);
9614
+ const sessions = await listSessions();
9615
+ if (sessions.length === 0) return ok("No sessions found on this machine.");
9616
+ return ok(`${sessions.length} session(s):
9617
+ ${sessions.map(describeSummary).join("\n")}`);
9455
9618
  } catch (error) {
9456
9619
  return fail(`Failed to list sessions: ${error instanceof Error ? error.message : String(error)}`);
9457
9620
  }
@@ -9465,37 +9628,12 @@ ${lines.join("\n")}`);
9465
9628
  }
9466
9629
  }, async (args) => {
9467
9630
  if (!persistence.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9468
- const limit = Math.max(1, Math.min(100, Math.floor(args.limit ?? 20)));
9469
9631
  try {
9470
- const persisted = persistence.readPersistedSessions()[args.sessionId];
9471
- if (!persisted) {
9472
- return fail(`No local key for session ${args.sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`);
9473
- }
9474
- const token = await bearerToken();
9475
- const response = await axios.get(
9476
- `${persistence.configuration.serverUrl}/v3/sessions/${encodeURIComponent(args.sessionId)}/messages`,
9477
- {
9478
- params: { before_seq: 2147483647, limit },
9479
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${persistence.configuration.currentCliVersion}` },
9480
- timeout: 15e3
9481
- }
9482
- );
9483
- const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9484
- messages.reverse();
9485
- const key = persistence.decodeBase64(persisted.encryptionKey);
9486
- const bodies = messages.map((m) => {
9487
- if (m.content?.t !== "encrypted") return null;
9488
- try {
9489
- return persistence.decrypt(key, persisted.encryptionVariant, persistence.decodeBase64(m.content.c));
9490
- } catch {
9491
- return null;
9492
- }
9493
- });
9494
- const transcript = formatTranscript(bodies);
9495
- const header = describeSession(args.sessionId, persisted);
9496
- return ok(`${header}
9497
- --- last ${messages.length} message(s) ---
9498
- ${transcript.length > 0 ? transcript : "(no readable conversation content in this range)"}`);
9632
+ const result = await readSessionTranscript(args.sessionId, args.limit ?? 20);
9633
+ const body = result.transcript.length > 0 ? result.transcript : "(no readable conversation content in this range)";
9634
+ return ok(`${describeSummary(result.summary)}
9635
+ --- last ${result.messageCount} message(s) ---
9636
+ ${body}`);
9499
9637
  } catch (error) {
9500
9638
  return fail(`Failed to read session: ${error instanceof Error ? error.message : String(error)}`);
9501
9639
  }
@@ -9560,7 +9698,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9560
9698
  if (!persistence.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9561
9699
  try {
9562
9700
  const persisted = persistence.readPersistedSessions()[args.sessionId];
9563
- const success = await stopDaemonSession(args.sessionId);
9701
+ const success = await stopSession(args.sessionId);
9564
9702
  if (!success) return fail(`Session ${args.sessionId} was not found among the daemon's running sessions.`);
9565
9703
  return ok(`Stopped ${describeSession(args.sessionId, persisted)}`);
9566
9704
  } catch (error) {
@@ -9576,15 +9714,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9576
9714
  }, async (args) => {
9577
9715
  if (!persistence.isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9578
9716
  try {
9579
- const token = await bearerToken();
9580
- await axios.post(
9581
- `${persistence.configuration.serverUrl}/v1/sessions/${encodeURIComponent(args.sessionId)}/archive`,
9582
- {},
9583
- {
9584
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${persistence.configuration.currentCliVersion}` },
9585
- timeout: 1e4
9586
- }
9587
- );
9717
+ await archiveSession(args.sessionId);
9588
9718
  const persisted = persistence.readPersistedSessions()[args.sessionId];
9589
9719
  return ok(`Archived ${describeSession(args.sessionId, persisted)}`);
9590
9720
  } catch (error) {
@@ -10260,7 +10390,7 @@ function registerSideQuestionHandler(rpc, deps) {
10260
10390
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
10261
10391
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
10262
10392
  const run = deps.run ?? (async (input) => {
10263
- const { query } = await Promise.resolve().then(function () { return require('./index-C7N-ONU_.cjs'); });
10393
+ const { query } = await Promise.resolve().then(function () { return require('./index-DW78HyvK.cjs'); });
10264
10394
  return runSideQuestion(query, input);
10265
10395
  });
10266
10396
  const slots = /* @__PURE__ */ new Map();
@@ -10362,7 +10492,7 @@ const MAX_TITLE_CHARS = 60;
10362
10492
  const GENERATION_TIMEOUT_MS = 3e4;
10363
10493
  function resolveClaudeBinary() {
10364
10494
  try {
10365
- 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-DM0Ku1Rv.cjs', document.baseURI).href)));
10495
+ 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-CvnMiYgh.cjs', document.baseURI).href)));
10366
10496
  const utilsPath = path.resolve(path.join(persistence.projectPath(), "scripts", "claude_version_utils.cjs"));
10367
10497
  const { getClaudeCliPath } = require$1(utilsPath);
10368
10498
  const path$1 = getClaudeCliPath();
@@ -10800,6 +10930,7 @@ async function runClaude(credentials, options = {}) {
10800
10930
  const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
10801
10931
  const importedFromClaudeSessionId = process.env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID;
10802
10932
  const importTitle = process.env.HAPPY_IMPORT_TITLE;
10933
+ const originTags = spawnOriginTags();
10803
10934
  let metadata = {
10804
10935
  path: workingDirectory,
10805
10936
  host: os.hostname(),
@@ -10832,12 +10963,13 @@ async function runClaude(credentials, options = {}) {
10832
10963
  ...importedFromClaudeSessionId ? { importedFromClaudeSessionId } : {},
10833
10964
  ...importTitle ? { summary: { text: importTitle, updatedAt: Date.now() } } : {},
10834
10965
  ...isAssistantVariant ? { variant: "assistant" } : {},
10835
- // B-091: sessions the assistant dispatched (daemon spawn RPC exports
10836
- // HAPPY_SPAWNED_BY, B-069) are born with the 'assistant' tag so every
10837
- // list shows their origin. NOT on the meta-agent itself — that one is
10838
- // the variant above and never joins the lists at all. (Shared helper:
10839
- // the other flavors get the same tag via createSessionMetadata.)
10840
- ...assistantSpawnTags() ? { tags: assistantSpawnTags() } : {}
10966
+ // B-091/B-303: dispatched sessions (daemon spawn RPC exports
10967
+ // HAPPY_SPAWNED_BY, B-069) are born tagged with their spawn origin so
10968
+ // every list shows where the work came from. NOT on the meta-agent
10969
+ // itself — that one is the variant above and never joins the lists at
10970
+ // all. (Shared helper: the other flavors get the same tag via
10971
+ // createSessionMetadata.)
10972
+ ...originTags ? { tags: originTags } : {}
10841
10973
  };
10842
10974
  const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
10843
10975
  const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
@@ -12562,9 +12694,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
12562
12694
  };
12563
12695
  }
12564
12696
 
12565
- 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-DM0Ku1Rv.cjs', document.baseURI).href)));
12697
+ 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-CvnMiYgh.cjs', document.baseURI).href)));
12566
12698
  const __dirname$1 = path.dirname(__filename$1);
12567
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DM0Ku1Rv.cjs', document.baseURI).href)));
12699
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CvnMiYgh.cjs', document.baseURI).href)));
12568
12700
  const PRISMA_QUERY_ENGINE_FILES = {
12569
12701
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
12570
12702
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -15188,7 +15320,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15188
15320
  process.exit(0);
15189
15321
  } else if (subcommand === "install-terminal-hooks") {
15190
15322
  try {
15191
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-BpvWmkZ9.cjs'); });
15323
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-DhQ6k7YD.cjs'); });
15192
15324
  const command = parseTerminalHooksArgs(args.slice(1));
15193
15325
  if (command.action === "help") {
15194
15326
  console.log(TERMINAL_HOOKS_HELP);
@@ -15205,7 +15337,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15205
15337
  }
15206
15338
  } else if (subcommand === "spawn") {
15207
15339
  try {
15208
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-BXbVzbMs.cjs'); });
15340
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CwS8CPbr.cjs'); });
15209
15341
  await handleSpawnCommand(args.slice(1));
15210
15342
  } catch (error) {
15211
15343
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15215,9 +15347,21 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15215
15347
  process.exit(1);
15216
15348
  }
15217
15349
  return;
15350
+ } else if (subcommand === "sessions") {
15351
+ try {
15352
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-CVUBS3LG.cjs'); });
15353
+ await handleSessionsCommand(args.slice(1));
15354
+ } catch (error) {
15355
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
15356
+ if (process.env.DEBUG) {
15357
+ console.error(error);
15358
+ }
15359
+ process.exit(1);
15360
+ }
15361
+ return;
15218
15362
  } else if (subcommand === "send") {
15219
15363
  try {
15220
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-CLdMBQ8e.cjs'); });
15364
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-CuajooZC.cjs'); });
15221
15365
  await handleSendCommand(args.slice(1));
15222
15366
  } catch (error) {
15223
15367
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15323,9 +15467,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15323
15467
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
15324
15468
  const projectId = args[3];
15325
15469
  try {
15326
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-BvVwprR0.cjs'); });
15327
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types-D4bwyM-D.cjs'); }).then(function (n) { return n.persistence; });
15328
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-D4bwyM-D.cjs'); }).then(function (n) { return n.api; });
15470
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-CTCGilz_.cjs'); });
15471
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./types--t4zLibT.cjs'); }).then(function (n) { return n.persistence; });
15472
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types--t4zLibT.cjs'); }).then(function (n) { return n.api; });
15329
15473
  let userEmail = void 0;
15330
15474
  try {
15331
15475
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -15356,7 +15500,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15356
15500
  }
15357
15501
  if (geminiSubcommand === "project" && args[2] === "get") {
15358
15502
  try {
15359
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-BvVwprR0.cjs'); });
15503
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-CTCGilz_.cjs'); });
15360
15504
  const config = readGeminiLocalConfig();
15361
15505
  if (config.googleCloudProject) {
15362
15506
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -15396,7 +15540,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15396
15540
  process.exit(0);
15397
15541
  }
15398
15542
  try {
15399
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-B3Mme1zr.cjs'); });
15543
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-D6Wwfk_0.cjs'); });
15400
15544
  let startedBy = void 0;
15401
15545
  for (let i = 1; i < args.length; i++) {
15402
15546
  if (args[i] === "--started-by") {
@@ -15418,7 +15562,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15418
15562
  return;
15419
15563
  } else if (subcommand === "acp") {
15420
15564
  try {
15421
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-Db5m34eh.cjs'); });
15565
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-D-adqB7g.cjs'); });
15422
15566
  let startedBy = void 0;
15423
15567
  let verbose = false;
15424
15568
  const acpArgs = [];
@@ -15458,7 +15602,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15458
15602
  return;
15459
15603
  } else if (subcommand === "openclaw") {
15460
15604
  try {
15461
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-B2V9j9Le.cjs'); });
15605
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-BYmlS-4Q.cjs'); });
15462
15606
  let startedBy = void 0;
15463
15607
  let verbose = false;
15464
15608
  let gatewayUrl;
@@ -15509,7 +15653,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15509
15653
  return;
15510
15654
  } else if (subcommand === "mcp" && args.length === 1) {
15511
15655
  try {
15512
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-rTSmj-tn.cjs'); });
15656
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-Dr1PegOw.cjs'); });
15513
15657
  await handleMcpCommand();
15514
15658
  } catch (error) {
15515
15659
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -15724,6 +15868,8 @@ ${chalk.bold("Usage:")}
15724
15868
  print its web URL (for automation; see spawn --help)
15725
15869
  very-happy send Send a message into an existing session
15726
15870
  (for automation; see send --help)
15871
+ very-happy sessions List, read, stop or archive sessions on this
15872
+ machine (for automation; see sessions --help)
15727
15873
  very-happy codex Start Codex mode
15728
15874
  very-happy gemini Start Gemini mode (ACP)
15729
15875
  very-happy acp Start a generic ACP-compatible agent
@@ -15869,23 +16015,30 @@ exports.BasePermissionHandler = BasePermissionHandler;
15869
16015
  exports.BaseReasoningProcessor = BaseReasoningProcessor;
15870
16016
  exports.CHANGE_TITLE_INSTRUCTION = CHANGE_TITLE_INSTRUCTION;
15871
16017
  exports.DEFAULT_GEMINI_MODEL = DEFAULT_GEMINI_MODEL;
16018
+ exports.DEFAULT_RECENT_LIMIT = DEFAULT_RECENT_LIMIT;
15872
16019
  exports.GEMINI_API_KEY_ENV = GEMINI_API_KEY_ENV;
15873
16020
  exports.GEMINI_MODEL_ENV = GEMINI_MODEL_ENV;
15874
16021
  exports.GOOGLE_API_KEY_ENV = GOOGLE_API_KEY_ENV;
16022
+ exports.MAX_READ_LIMIT = MAX_READ_LIMIT;
15875
16023
  exports.MessageBuffer = MessageBuffer;
15876
16024
  exports.MessageQueue2 = MessageQueue2;
16025
+ exports.archiveSession = archiveSession;
15877
16026
  exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
15878
16027
  exports.createSessionMetadata = createSessionMetadata;
15879
16028
  exports.hashObject = hashObject;
15880
16029
  exports.initialMachineMetadata = initialMachineMetadata;
16030
+ exports.isValidSpawnOrigin = isValidSpawnOrigin;
16031
+ exports.listSessions = listSessions;
15881
16032
  exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
15882
16033
  exports.pushClipboardViaDaemon = pushClipboardViaDaemon;
15883
16034
  exports.query = query;
16035
+ exports.readSessionTranscript = readSessionTranscript;
15884
16036
  exports.registerKillSessionHandler = registerKillSessionHandler;
15885
16037
  exports.sendUserMessage = sendUserMessage;
15886
16038
  exports.sessionWebUrl = sessionWebUrl;
15887
16039
  exports.setupOfflineReconnection = setupOfflineReconnection;
15888
16040
  exports.spawnDaemonSession = spawnDaemonSession;
15889
16041
  exports.startHappyServer = startHappyServer;
16042
+ exports.stopSession = stopSession;
15890
16043
  exports.tmuxSupportsSessionEnv = tmuxSupportsSessionEnv;
15891
16044
  exports.waitForSessionKey = waitForSessionKey;
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-D4vV6SVl.cjs');
4
- var persistence = require('./types-D4bwyM-D.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-CPHDZ-nz.cjs');
3
+ var AcpBackend = require('./AcpBackend-D0_wbTs6.cjs');
4
+ var persistence = require('./types--t4zLibT.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-Dcj_iHw8.cjs');
6
6
  var node_crypto = require('node:crypto');
7
7
  var path = require('node:path');
8
- var index = require('./index-DM0Ku1Rv.cjs');
8
+ var index = require('./index-CvnMiYgh.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DM0Ku1Rv.cjs');
3
+ var index = require('./index-CvnMiYgh.cjs');
4
4
  var claudeAgentSdk = require('@anthropic-ai/claude-agent-sdk');
5
5
  require('chalk');
6
6
  require('node:os');
7
7
  require('node:crypto');
8
- require('./types-D4bwyM-D.cjs');
8
+ require('./types--t4zLibT.cjs');
9
9
  require('axios');
10
10
  require('node:util');
11
11
  require('node:fs');
@@ -1,9 +1,9 @@
1
- import { q as query } from './index-CzSv4d_S.mjs';
1
+ import { q as query } from './index-BW9kDx5m.mjs';
2
2
  export { AbortError } from '@anthropic-ai/claude-agent-sdk';
3
3
  import 'chalk';
4
4
  import 'node:os';
5
5
  import 'node:crypto';
6
- import './types-D0-nhvuW.mjs';
6
+ import './types-BdO0jg7G.mjs';
7
7
  import 'axios';
8
8
  import 'node:util';
9
9
  import 'node:fs';