very-happy-cli 0.2.108 → 0.2.109

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-Cc4Ilypg.cjs} +1 -1
  2. package/dist/{AcpBackend-DwR8rrkS.mjs → AcpBackend-DOzPxcth.mjs} +1 -1
  3. package/dist/{AcpSessionManager-CPHDZ-nz.cjs → AcpSessionManager-CLfzatC_.cjs} +1 -1
  4. package/dist/{AcpSessionManager-C057dkLF.mjs → AcpSessionManager-DDN-Yir4.mjs} +1 -1
  5. package/dist/{config-BvVwprR0.cjs → config-DiVKfYr0.cjs} +2 -2
  6. package/dist/{config-DKMwLc1k.mjs → config-DnvzvFHR.mjs} +2 -2
  7. package/dist/{index-C7N-ONU_.cjs → index-B2U15moC.cjs} +2 -2
  8. package/dist/{index-DYY1DUOD.mjs → index-CGKxZKlo.mjs} +2 -2
  9. package/dist/{index-Db5m34eh.cjs → index-D2qU5HP4.cjs} +4 -4
  10. package/dist/{index-CzSv4d_S.mjs → index-DO-9Ep72.mjs} +175 -89
  11. package/dist/{index-BXnHT-Wu.mjs → index-DaQIcHvY.mjs} +5 -5
  12. package/dist/{index-DM0Ku1Rv.cjs → index-dd2R8uSE.cjs} +185 -92
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-Cs49gg_E.mjs → installTerminalHooks-BDagPTQW.mjs} +2 -2
  16. package/dist/{installTerminalHooks-BpvWmkZ9.cjs → installTerminalHooks-BoUmuYdq.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-DY17lhMH.mjs → mcp-Ck_y0mCB.mjs} +2 -2
  20. package/dist/{mcp-rTSmj-tn.cjs → mcp-Du1QEnIf.cjs} +2 -2
  21. package/dist/{runGemini-Bqs52hw_.mjs → runGemini-D20S6Lc3.mjs} +4 -4
  22. package/dist/{runGemini-B3Mme1zr.cjs → runGemini-DV2M0A_k.cjs} +4 -4
  23. package/dist/{runOpenClaw-B2V9j9Le.cjs → runOpenClaw-C1Q2zl--.cjs} +3 -3
  24. package/dist/{runOpenClaw-CpbsQ7NZ.mjs → runOpenClaw-CuuXkfDE.mjs} +3 -3
  25. package/dist/{send-CLdMBQ8e.cjs → send-CtgnnJic.cjs} +2 -2
  26. package/dist/{send-uHgpvIqm.mjs → send-yUiBqHHL.mjs} +2 -2
  27. package/dist/sessions-BsaCq0Do.mjs +200 -0
  28. package/dist/sessions-CbbZhOTy.cjs +204 -0
  29. package/dist/{spawn-Dt4ujmHH.mjs → spawn-BKBY9P3J.mjs} +16 -3
  30. package/dist/{spawn-BXbVzbMs.cjs → spawn-CTO_hR8v.cjs} +16 -3
  31. package/dist/{types-D0-nhvuW.mjs → types-BKCvo4Cv.mjs} +8 -8
  32. package/dist/{types-D4bwyM-D.cjs → types-Cg8zzX_f.cjs} +8 -8
  33. package/package.json +7 -7
@@ -3,7 +3,7 @@
3
3
  var node_child_process = require('node:child_process');
4
4
  var sdk = require('@agentclientprotocol/sdk');
5
5
  var node_crypto = require('node:crypto');
6
- var persistence = require('./types-D4bwyM-D.cjs');
6
+ var persistence = require('./types-Cg8zzX_f.cjs');
7
7
 
8
8
  const DEFAULT_TIMEOUTS = {
9
9
  /** Default initialization timeout: 60 seconds */
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-D0-nhvuW.mjs';
4
+ import { l as logger, d as contentLogMetadata, e as errorLogMetadata, f as packageJson, g as delay } from './types-BKCvo4Cv.mjs';
5
5
 
6
6
  const DEFAULT_TIMEOUTS = {
7
7
  /** Default initialization timeout: 60 seconds */
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var cuid2 = require('@paralleldrive/cuid2');
4
- var persistence = require('./types-D4bwyM-D.cjs');
4
+ var persistence = require('./types-Cg8zzX_f.cjs');
5
5
 
6
6
  function turnOptions(turnId, time) {
7
7
  return turnId ? { turn: turnId, time } : { time };
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { b as createEnvelope } from './types-D0-nhvuW.mjs';
2
+ import { b as createEnvelope } from './types-BKCvo4Cv.mjs';
3
3
 
4
4
  function turnOptions(turnId, time) {
5
5
  return turnId ? { turn: turnId, time } : { time };
@@ -4,8 +4,8 @@ var fs = require('fs');
4
4
  var path = require('path');
5
5
  var os = require('os');
6
6
  var child_process = require('child_process');
7
- var persistence = require('./types-D4bwyM-D.cjs');
8
- var index = require('./index-DM0Ku1Rv.cjs');
7
+ var persistence = require('./types-Cg8zzX_f.cjs');
8
+ var index = require('./index-dd2R8uSE.cjs');
9
9
  require('axios');
10
10
  require('chalk');
11
11
  require('node:util');
@@ -2,8 +2,8 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { execSync } from 'child_process';
5
- import { l as logger } from './types-D0-nhvuW.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-CzSv4d_S.mjs';
5
+ import { l as logger } from './types-BKCvo4Cv.mjs';
6
+ import { f as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-DO-9Ep72.mjs';
7
7
  import 'axios';
8
8
  import 'chalk';
9
9
  import 'node:util';
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DM0Ku1Rv.cjs');
3
+ var index = require('./index-dd2R8uSE.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-Cg8zzX_f.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-DO-9Ep72.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-BKCvo4Cv.mjs';
7
7
  import 'axios';
8
8
  import 'node:util';
9
9
  import 'node:fs';
@@ -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-Cc4Ilypg.cjs');
4
+ var persistence = require('./types-Cg8zzX_f.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-CLfzatC_.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-dd2R8uSE.cjs');
9
9
  require('node:child_process');
10
10
  require('@agentclientprotocol/sdk');
11
11
  require('axios');
@@ -1,7 +1,7 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
2
  import os, { homedir, userInfo } from 'node:os';
3
3
  import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { n as readDaemonState, l as logger, c as configuration, o as clearDaemonState, q as getProjectPath, p as projectPath, s as initializeSandbox, t as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, g as delay, u as AsyncLock, v as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, x as hardenPrivateFile, f as packageJson, i as readSettings, y as readCredentials, z as credentialRelayProblem, B as deriveLocalCliUpdateSummary, j as encodeBase64, C as updateSettings, D as encodeBase64Url, E as decodeBase64, F as writeCredentialsLegacy, G as writeCredentialsDataKey, H as readCredentialsForConfiguredRelay, J as encrypt, r as readPersistedSessions, K as isClaudeSessionId, L as getLocalHappyAgentCredentialPath, N as readLocalHappyAgentCredentials, O as decryptWithDataKey, P as decryptLegacy, Q as extractCompleteLines, S as decideBackfill, T as parseTerminalHookPayload, U as tmuxArgs, V as scrubTmuxClientEnv, W as decideMirrorBinding, X as MIRROR_BACKFILL_LINES_DEFAULT, Y as persistSession, Z as mirrorLineKey, _ as mirrorLocalId, w as writePrivateFileSync, $ as detectResumeSupport, a0 as detectCLIAvailability, a1 as acquireDaemonLock, a2 as summarizeSpawnSessionForLog, a3 as deletePersistedSessions, a4 as shellescape, a5 as decrypt, a6 as writeDaemonState, A as ApiClient, a7 as writeSettings, a8 as fetchCliUpdateState, a9 as resolveCliUpdateCheckInterval, aa as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, ab as releaseDaemonLock, ac as isValidSessionId, ad as listVhTerminals, ae as isValidTerminalId, af as readVhTerminal, ag as sendToVhTerminal, ah as TITLE_PROMPT_PREFIX, ai as BOARD_ANALYZER_PROMPT_PREFIX, h as connectionState, aj as startOfflineReconnection, ak as clearCredentials, al as clearMachineId, k as ensurePrivateDirectorySync, am as SandboxConfigSchema, b as createEnvelope, an as CodexAppServerClient, ao as getLatestDaemonLog } from './types-D0-nhvuW.mjs';
4
+ import { o as readDaemonState, l as logger, c as configuration, q as clearDaemonState, s as getProjectPath, p as projectPath, t as initializeSandbox, u as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, g as delay, v as AsyncLock, x as ensurePrivateDirectory, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, y as hardenPrivateFile, f as packageJson, j as readSettings, z as readCredentials, B as credentialRelayProblem, C as deriveLocalCliUpdateSummary, k as encodeBase64, D as updateSettings, E as encodeBase64Url, F as decodeBase64, G as writeCredentialsLegacy, H as writeCredentialsDataKey, J as readCredentialsForConfiguredRelay, K as encrypt, r as readPersistedSessions, L as isClaudeSessionId, N as getLocalHappyAgentCredentialPath, O as readLocalHappyAgentCredentials, P as decryptWithDataKey, Q as decryptLegacy, S as extractCompleteLines, T as decideBackfill, U as parseTerminalHookPayload, V as tmuxArgs, W as scrubTmuxClientEnv, X as decideMirrorBinding, Y as MIRROR_BACKFILL_LINES_DEFAULT, Z as persistSession, _ as mirrorLineKey, $ as mirrorLocalId, w as writePrivateFileSync, a0 as detectResumeSupport, a1 as detectCLIAvailability, a2 as acquireDaemonLock, a3 as summarizeSpawnSessionForLog, a4 as deletePersistedSessions, a5 as shellescape, a6 as decrypt, a7 as writeDaemonState, A as ApiClient, a8 as writeSettings, a9 as fetchCliUpdateState, aa as resolveCliUpdateCheckInterval, ab as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, ac as releaseDaemonLock, i as isValidSessionId, ad as listVhTerminals, ae as isValidTerminalId, af as readVhTerminal, ag as sendToVhTerminal, ah as TITLE_PROMPT_PREFIX, ai as BOARD_ANALYZER_PROMPT_PREFIX, h as connectionState, aj as startOfflineReconnection, ak as clearCredentials, al as clearMachineId, m as ensurePrivateDirectorySync, am as SandboxConfigSchema, b as createEnvelope, an as CodexAppServerClient, ao as getLatestDaemonLog } from './types-BKCvo4Cv.mjs';
5
5
  import spawn$1, { spawn } from 'cross-spawn';
6
6
  import path, { join, resolve, basename, delimiter, isAbsolute, normalize, sep, dirname } from 'node:path';
7
7
  import { createInterface } from 'node:readline';
@@ -4422,16 +4422,23 @@ function claudeModeHash(mode) {
4422
4422
  });
4423
4423
  }
4424
4424
 
4425
- function assistantSpawnTags(env = process.env) {
4426
- if (env.HAPPY_SPAWNED_BY !== "assistant") return void 0;
4425
+ const SPAWN_ORIGIN_TAG_MAX = 24;
4426
+ function spawnOriginTags(env = process.env) {
4427
4427
  if (env.HAPPY_SESSION_VARIANT === "assistant") return void 0;
4428
- return ["assistant"];
4428
+ const origin = (env.HAPPY_SPAWNED_BY ?? "").trim();
4429
+ if (!origin) return void 0;
4430
+ if (origin.length > SPAWN_ORIGIN_TAG_MAX) return void 0;
4431
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(origin)) return void 0;
4432
+ return [origin];
4433
+ }
4434
+ function isValidSpawnOrigin(value) {
4435
+ return spawnOriginTags({ HAPPY_SPAWNED_BY: value }) !== void 0;
4429
4436
  }
4430
4437
  function createSessionMetadata(opts) {
4431
4438
  const state = {
4432
4439
  controlledByUser: false
4433
4440
  };
4434
- const assistantTags = assistantSpawnTags();
4441
+ const originTags = spawnOriginTags();
4435
4442
  const metadata = {
4436
4443
  path: process.cwd(),
4437
4444
  host: os.hostname(),
@@ -4452,10 +4459,10 @@ function createSessionMetadata(opts) {
4452
4459
  dangerouslySkipPermissions: opts.dangerouslySkipPermissions ?? null,
4453
4460
  ...opts.parentSessionId ? { parentSessionId: opts.parentSessionId } : {},
4454
4461
  ...opts.forkedFromMessageId ? { forkedFromMessageId: opts.forkedFromMessageId } : {},
4455
- // B-091: assistant-dispatched sessions carry the 'assistant' tag from
4456
- // birth (see assistantSpawnTags) — applies to every flavor the daemon
4462
+ // B-091/B-303: dispatched sessions carry their spawn origin as a tag
4463
+ // from birth (see spawnOriginTags) — applies to every flavor the daemon
4457
4464
  // can spawn, since HAPPY_SPAWNED_BY is exported flavor-independently.
4458
- ...assistantTags ? { tags: assistantTags } : {}
4465
+ ...originTags ? { tags: originTags } : {}
4459
4466
  };
4460
4467
  return { state, metadata };
4461
4468
  }
@@ -9297,6 +9304,109 @@ function formatTranscript(bodies) {
9297
9304
  return lines.join("\n");
9298
9305
  }
9299
9306
 
9307
+ const SESSION_OPS_CLIENT = "session-ops";
9308
+ const DEFAULT_RECENT_LIMIT = 15;
9309
+ const MAX_READ_LIMIT = 100;
9310
+ function mergeSessionSummaries(live, persisted, options = {}) {
9311
+ const recentLimit = Math.max(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);
9312
+ const seen = /* @__PURE__ */ new Set();
9313
+ const summaries = [];
9314
+ for (const child of live) {
9315
+ const id = typeof child.happySessionId === "string" ? child.happySessionId : void 0;
9316
+ if (!id || seen.has(id)) continue;
9317
+ seen.add(id);
9318
+ summaries.push(toSummary(id, persisted[id], {
9319
+ live: true,
9320
+ pid: typeof child.pid === "number" ? child.pid : void 0
9321
+ }));
9322
+ }
9323
+ 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);
9324
+ for (const [id, entry] of rest) {
9325
+ summaries.push(toSummary(id, entry, { live: false }));
9326
+ }
9327
+ if (!options.tag) return summaries;
9328
+ const wanted = options.tag;
9329
+ return summaries.filter((summary) => summary.tags?.includes(wanted) === true);
9330
+ }
9331
+ function toSummary(id, persisted, extra) {
9332
+ const meta = persisted?.metadata;
9333
+ return {
9334
+ id,
9335
+ live: extra.live,
9336
+ ...extra.pid !== void 0 ? { pid: extra.pid } : {},
9337
+ ...meta?.summary?.text ? { title: meta.summary.text } : {},
9338
+ ...meta?.path ? { cwd: meta.path } : {},
9339
+ ...meta?.flavor ? { flavor: meta.flavor } : {},
9340
+ ...meta?.tags?.length ? { tags: [...meta.tags] } : {},
9341
+ ...meta?.variant ? { variant: meta.variant } : {},
9342
+ url: sessionWebUrl(id),
9343
+ ...persisted?.savedAt !== void 0 ? { savedAt: persisted.savedAt } : {}
9344
+ };
9345
+ }
9346
+ async function listSessions(options = {}) {
9347
+ const live = await listDaemonSessions();
9348
+ return mergeSessionSummaries(live, readPersistedSessions(), options);
9349
+ }
9350
+ async function bearerToken() {
9351
+ const credentials = await readCredentialsForConfiguredRelay();
9352
+ if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9353
+ return credentials.token;
9354
+ }
9355
+ async function readSessionTranscript(sessionId, limit) {
9356
+ const persisted = readPersistedSessions()[sessionId];
9357
+ if (!persisted) {
9358
+ throw new Error(
9359
+ `No local key for session ${sessionId} \u2014 it was not spawned by this machine's daemon (or is older than 14 days).`
9360
+ );
9361
+ }
9362
+ const bounded = Math.max(1, Math.min(MAX_READ_LIMIT, Math.floor(limit)));
9363
+ const token = await bearerToken();
9364
+ const response = await axios.get(
9365
+ `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(sessionId)}/messages`,
9366
+ {
9367
+ params: { before_seq: 2147483647, limit: bounded },
9368
+ headers: {
9369
+ "Authorization": `Bearer ${token}`,
9370
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
9371
+ },
9372
+ timeout: 15e3
9373
+ }
9374
+ );
9375
+ const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9376
+ messages.reverse();
9377
+ const key = decodeBase64(persisted.encryptionKey);
9378
+ const bodies = messages.map((message) => {
9379
+ if (message.content?.t !== "encrypted") return null;
9380
+ try {
9381
+ return decrypt(key, persisted.encryptionVariant, decodeBase64(message.content.c));
9382
+ } catch {
9383
+ return null;
9384
+ }
9385
+ });
9386
+ return {
9387
+ summary: toSummary(sessionId, persisted, { live: false }),
9388
+ messageCount: messages.length,
9389
+ transcript: formatTranscript(bodies)
9390
+ };
9391
+ }
9392
+ async function stopSession(sessionId) {
9393
+ return await stopDaemonSession(sessionId);
9394
+ }
9395
+ async function archiveSession(sessionId) {
9396
+ const token = await bearerToken();
9397
+ await axios.post(
9398
+ `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(sessionId)}/archive`,
9399
+ {},
9400
+ {
9401
+ headers: {
9402
+ "Authorization": `Bearer ${token}`,
9403
+ "X-Happy-Client": `${SESSION_OPS_CLIENT}/${configuration.currentCliVersion}`
9404
+ },
9405
+ timeout: 1e4
9406
+ }
9407
+ );
9408
+ }
9409
+
9300
9410
  const PERSONAL_MEMORY_SOFT_LIMIT_CHARS = 2e3;
9301
9411
  function headingTitle(line) {
9302
9412
  const m = /^##(?!#)\s*(.*?)\s*$/.exec(line);
@@ -9393,19 +9503,24 @@ function fail(text) {
9393
9503
  function describeSession(id, persisted, extra) {
9394
9504
  const meta = persisted?.metadata;
9395
9505
  const parts = [id];
9396
- if (extra?.live !== void 0) parts.push(extra.live ? "[running]" : "[not running]");
9397
9506
  if (meta?.summary?.text) parts.push(`title="${meta.summary.text}"`);
9398
9507
  if (meta?.path) parts.push(`cwd=${meta.path}`);
9399
9508
  if (meta?.flavor) parts.push(`agent=${meta.flavor}`);
9509
+ if (meta?.tags?.length) parts.push(`tags=${meta.tags.join(",")}`);
9400
9510
  if (meta?.variant === "assistant") parts.push("(assistant)");
9401
- if (extra?.pid) parts.push(`pid=${extra.pid}`);
9402
9511
  parts.push(sessionWebUrl(id));
9403
9512
  return parts.join(" ");
9404
9513
  }
9405
- async function bearerToken() {
9406
- const credentials = await readCredentialsForConfiguredRelay();
9407
- if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9408
- return credentials.token;
9514
+ function describeSummary(summary) {
9515
+ const parts = [summary.id, summary.live ? "[running]" : "[not running]"];
9516
+ if (summary.title) parts.push(`title="${summary.title}"`);
9517
+ if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
9518
+ if (summary.flavor) parts.push(`agent=${summary.flavor}`);
9519
+ if (summary.tags?.length) parts.push(`tags=${summary.tags.join(",")}`);
9520
+ if (summary.variant === "assistant") parts.push("(assistant)");
9521
+ if (summary.pid !== void 0) parts.push(`pid=${summary.pid}`);
9522
+ parts.push(summary.url);
9523
+ return parts.join(" ");
9409
9524
  }
9410
9525
  function registerAssistantTools(mcp) {
9411
9526
  mcp.registerTool("sessions_list", {
@@ -9414,22 +9529,10 @@ function registerAssistantTools(mcp) {
9414
9529
  inputSchema: {}
9415
9530
  }, async () => {
9416
9531
  try {
9417
- const live = await listDaemonSessions();
9418
- const persisted = readPersistedSessions();
9419
- const lines = [];
9420
- const seen = /* @__PURE__ */ new Set();
9421
- for (const child of live) {
9422
- const id = child.happySessionId;
9423
- seen.add(id);
9424
- lines.push(describeSession(id, persisted[id], { pid: child.pid, live: true }));
9425
- }
9426
- 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);
9427
- for (const [id, entry] of rest) {
9428
- lines.push(describeSession(id, entry, { live: false }));
9429
- }
9430
- if (lines.length === 0) return ok("No sessions found on this machine.");
9431
- return ok(`${lines.length} session(s):
9432
- ${lines.join("\n")}`);
9532
+ const sessions = await listSessions();
9533
+ if (sessions.length === 0) return ok("No sessions found on this machine.");
9534
+ return ok(`${sessions.length} session(s):
9535
+ ${sessions.map(describeSummary).join("\n")}`);
9433
9536
  } catch (error) {
9434
9537
  return fail(`Failed to list sessions: ${error instanceof Error ? error.message : String(error)}`);
9435
9538
  }
@@ -9443,37 +9546,12 @@ ${lines.join("\n")}`);
9443
9546
  }
9444
9547
  }, async (args) => {
9445
9548
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9446
- const limit = Math.max(1, Math.min(100, Math.floor(args.limit ?? 20)));
9447
9549
  try {
9448
- const persisted = readPersistedSessions()[args.sessionId];
9449
- if (!persisted) {
9450
- 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).`);
9451
- }
9452
- const token = await bearerToken();
9453
- const response = await axios.get(
9454
- `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(args.sessionId)}/messages`,
9455
- {
9456
- params: { before_seq: 2147483647, limit },
9457
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${configuration.currentCliVersion}` },
9458
- timeout: 15e3
9459
- }
9460
- );
9461
- const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9462
- messages.reverse();
9463
- const key = decodeBase64(persisted.encryptionKey);
9464
- const bodies = messages.map((m) => {
9465
- if (m.content?.t !== "encrypted") return null;
9466
- try {
9467
- return decrypt(key, persisted.encryptionVariant, decodeBase64(m.content.c));
9468
- } catch {
9469
- return null;
9470
- }
9471
- });
9472
- const transcript = formatTranscript(bodies);
9473
- const header = describeSession(args.sessionId, persisted);
9474
- return ok(`${header}
9475
- --- last ${messages.length} message(s) ---
9476
- ${transcript.length > 0 ? transcript : "(no readable conversation content in this range)"}`);
9550
+ const result = await readSessionTranscript(args.sessionId, args.limit ?? 20);
9551
+ const body = result.transcript.length > 0 ? result.transcript : "(no readable conversation content in this range)";
9552
+ return ok(`${describeSummary(result.summary)}
9553
+ --- last ${result.messageCount} message(s) ---
9554
+ ${body}`);
9477
9555
  } catch (error) {
9478
9556
  return fail(`Failed to read session: ${error instanceof Error ? error.message : String(error)}`);
9479
9557
  }
@@ -9538,7 +9616,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9538
9616
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9539
9617
  try {
9540
9618
  const persisted = readPersistedSessions()[args.sessionId];
9541
- const success = await stopDaemonSession(args.sessionId);
9619
+ const success = await stopSession(args.sessionId);
9542
9620
  if (!success) return fail(`Session ${args.sessionId} was not found among the daemon's running sessions.`);
9543
9621
  return ok(`Stopped ${describeSession(args.sessionId, persisted)}`);
9544
9622
  } catch (error) {
@@ -9554,15 +9632,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9554
9632
  }, async (args) => {
9555
9633
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9556
9634
  try {
9557
- const token = await bearerToken();
9558
- await axios.post(
9559
- `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(args.sessionId)}/archive`,
9560
- {},
9561
- {
9562
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${configuration.currentCliVersion}` },
9563
- timeout: 1e4
9564
- }
9565
- );
9635
+ await archiveSession(args.sessionId);
9566
9636
  const persisted = readPersistedSessions()[args.sessionId];
9567
9637
  return ok(`Archived ${describeSession(args.sessionId, persisted)}`);
9568
9638
  } catch (error) {
@@ -10238,7 +10308,7 @@ function registerSideQuestionHandler(rpc, deps) {
10238
10308
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
10239
10309
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
10240
10310
  const run = deps.run ?? (async (input) => {
10241
- const { query } = await import('./index-DYY1DUOD.mjs');
10311
+ const { query } = await import('./index-CGKxZKlo.mjs');
10242
10312
  return runSideQuestion(query, input);
10243
10313
  });
10244
10314
  const slots = /* @__PURE__ */ new Map();
@@ -10778,6 +10848,7 @@ async function runClaude(credentials, options = {}) {
10778
10848
  const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
10779
10849
  const importedFromClaudeSessionId = process.env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID;
10780
10850
  const importTitle = process.env.HAPPY_IMPORT_TITLE;
10851
+ const originTags = spawnOriginTags();
10781
10852
  let metadata = {
10782
10853
  path: workingDirectory,
10783
10854
  host: os.hostname(),
@@ -10810,12 +10881,13 @@ async function runClaude(credentials, options = {}) {
10810
10881
  ...importedFromClaudeSessionId ? { importedFromClaudeSessionId } : {},
10811
10882
  ...importTitle ? { summary: { text: importTitle, updatedAt: Date.now() } } : {},
10812
10883
  ...isAssistantVariant ? { variant: "assistant" } : {},
10813
- // B-091: sessions the assistant dispatched (daemon spawn RPC exports
10814
- // HAPPY_SPAWNED_BY, B-069) are born with the 'assistant' tag so every
10815
- // list shows their origin. NOT on the meta-agent itself — that one is
10816
- // the variant above and never joins the lists at all. (Shared helper:
10817
- // the other flavors get the same tag via createSessionMetadata.)
10818
- ...assistantSpawnTags() ? { tags: assistantSpawnTags() } : {}
10884
+ // B-091/B-303: dispatched sessions (daemon spawn RPC exports
10885
+ // HAPPY_SPAWNED_BY, B-069) are born tagged with their spawn origin so
10886
+ // every list shows where the work came from. NOT on the meta-agent
10887
+ // itself — that one is the variant above and never joins the lists at
10888
+ // all. (Shared helper: the other flavors get the same tag via
10889
+ // createSessionMetadata.)
10890
+ ...originTags ? { tags: originTags } : {}
10819
10891
  };
10820
10892
  const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
10821
10893
  const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
@@ -15166,7 +15238,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15166
15238
  process.exit(0);
15167
15239
  } else if (subcommand === "install-terminal-hooks") {
15168
15240
  try {
15169
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-Cs49gg_E.mjs');
15241
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-BDagPTQW.mjs');
15170
15242
  const command = parseTerminalHooksArgs(args.slice(1));
15171
15243
  if (command.action === "help") {
15172
15244
  console.log(TERMINAL_HOOKS_HELP);
@@ -15183,7 +15255,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15183
15255
  }
15184
15256
  } else if (subcommand === "spawn") {
15185
15257
  try {
15186
- const { handleSpawnCommand } = await import('./spawn-Dt4ujmHH.mjs');
15258
+ const { handleSpawnCommand } = await import('./spawn-BKBY9P3J.mjs');
15187
15259
  await handleSpawnCommand(args.slice(1));
15188
15260
  } catch (error) {
15189
15261
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15193,9 +15265,21 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15193
15265
  process.exit(1);
15194
15266
  }
15195
15267
  return;
15268
+ } else if (subcommand === "sessions") {
15269
+ try {
15270
+ const { handleSessionsCommand } = await import('./sessions-BsaCq0Do.mjs');
15271
+ await handleSessionsCommand(args.slice(1));
15272
+ } catch (error) {
15273
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
15274
+ if (process.env.DEBUG) {
15275
+ console.error(error);
15276
+ }
15277
+ process.exit(1);
15278
+ }
15279
+ return;
15196
15280
  } else if (subcommand === "send") {
15197
15281
  try {
15198
- const { handleSendCommand } = await import('./send-uHgpvIqm.mjs');
15282
+ const { handleSendCommand } = await import('./send-yUiBqHHL.mjs');
15199
15283
  await handleSendCommand(args.slice(1));
15200
15284
  } catch (error) {
15201
15285
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15301,9 +15385,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15301
15385
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
15302
15386
  const projectId = args[3];
15303
15387
  try {
15304
- const { saveGoogleCloudProjectToConfig } = await import('./config-DKMwLc1k.mjs');
15305
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-D0-nhvuW.mjs').then(function (n) { return n.ap; });
15306
- const { ApiClient: ApiClient2 } = await import('./types-D0-nhvuW.mjs').then(function (n) { return n.aq; });
15388
+ const { saveGoogleCloudProjectToConfig } = await import('./config-DnvzvFHR.mjs');
15389
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-BKCvo4Cv.mjs').then(function (n) { return n.ap; });
15390
+ const { ApiClient: ApiClient2 } = await import('./types-BKCvo4Cv.mjs').then(function (n) { return n.aq; });
15307
15391
  let userEmail = void 0;
15308
15392
  try {
15309
15393
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -15334,7 +15418,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15334
15418
  }
15335
15419
  if (geminiSubcommand === "project" && args[2] === "get") {
15336
15420
  try {
15337
- const { readGeminiLocalConfig } = await import('./config-DKMwLc1k.mjs');
15421
+ const { readGeminiLocalConfig } = await import('./config-DnvzvFHR.mjs');
15338
15422
  const config = readGeminiLocalConfig();
15339
15423
  if (config.googleCloudProject) {
15340
15424
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -15374,7 +15458,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15374
15458
  process.exit(0);
15375
15459
  }
15376
15460
  try {
15377
- const { runGemini } = await import('./runGemini-Bqs52hw_.mjs');
15461
+ const { runGemini } = await import('./runGemini-D20S6Lc3.mjs');
15378
15462
  let startedBy = void 0;
15379
15463
  for (let i = 1; i < args.length; i++) {
15380
15464
  if (args[i] === "--started-by") {
@@ -15396,7 +15480,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15396
15480
  return;
15397
15481
  } else if (subcommand === "acp") {
15398
15482
  try {
15399
- const { runAcp, resolveAcpAgentConfig } = await import('./index-BXnHT-Wu.mjs');
15483
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-DaQIcHvY.mjs');
15400
15484
  let startedBy = void 0;
15401
15485
  let verbose = false;
15402
15486
  const acpArgs = [];
@@ -15436,7 +15520,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15436
15520
  return;
15437
15521
  } else if (subcommand === "openclaw") {
15438
15522
  try {
15439
- const { runOpenClaw } = await import('./runOpenClaw-CpbsQ7NZ.mjs');
15523
+ const { runOpenClaw } = await import('./runOpenClaw-CuuXkfDE.mjs');
15440
15524
  let startedBy = void 0;
15441
15525
  let verbose = false;
15442
15526
  let gatewayUrl;
@@ -15487,7 +15571,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15487
15571
  return;
15488
15572
  } else if (subcommand === "mcp" && args.length === 1) {
15489
15573
  try {
15490
- const { handleMcpCommand } = await import('./mcp-DY17lhMH.mjs');
15574
+ const { handleMcpCommand } = await import('./mcp-Ck_y0mCB.mjs');
15491
15575
  await handleMcpCommand();
15492
15576
  } catch (error) {
15493
15577
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -15702,6 +15786,8 @@ ${chalk.bold("Usage:")}
15702
15786
  print its web URL (for automation; see spawn --help)
15703
15787
  very-happy send Send a message into an existing session
15704
15788
  (for automation; see send --help)
15789
+ very-happy sessions List, read, stop or archive sessions on this
15790
+ machine (for automation; see sessions --help)
15705
15791
  very-happy codex Start Codex mode
15706
15792
  very-happy gemini Start Gemini mode (ACP)
15707
15793
  very-happy acp Start a generic ACP-compatible agent
@@ -15843,4 +15929,4 @@ ${chalk.bold("Examples:")}
15843
15929
  }
15844
15930
  }
15845
15931
 
15846
- export { BasePermissionHandler as B, CHANGE_TITLE_INSTRUCTION as C, DEFAULT_GEMINI_MODEL as D, GEMINI_MODEL_ENV as G, MessageQueue2 as M, sessionWebUrl as a, sendUserMessage as b, checkIfDaemonRunningAndCleanupStaleState as c, createSessionMetadata as d, setupOfflineReconnection as e, startHappyServer as f, GEMINI_API_KEY_ENV as g, hashObject as h, initialMachineMetadata as i, GOOGLE_API_KEY_ENV as j, BaseReasoningProcessor as k, MessageBuffer as l, notifyDaemonSessionStarted as n, pushClipboardViaDaemon as p, query as q, registerKillSessionHandler as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, waitForSessionKey as w };
15932
+ export { BasePermissionHandler as B, CHANGE_TITLE_INSTRUCTION as C, DEFAULT_RECENT_LIMIT as D, GEMINI_MODEL_ENV as G, MAX_READ_LIMIT as M, sessionWebUrl as a, sendUserMessage as b, checkIfDaemonRunningAndCleanupStaleState as c, stopSession as d, archiveSession as e, DEFAULT_GEMINI_MODEL as f, initialMachineMetadata as g, createSessionMetadata as h, isValidSpawnOrigin as i, setupOfflineReconnection as j, MessageQueue2 as k, listSessions as l, hashObject as m, notifyDaemonSessionStarted as n, startHappyServer as o, pushClipboardViaDaemon as p, query as q, readSessionTranscript as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, registerKillSessionHandler as u, GEMINI_API_KEY_ENV as v, waitForSessionKey as w, GOOGLE_API_KEY_ENV as x, BaseReasoningProcessor as y, MessageBuffer as z };
@@ -1,10 +1,10 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-DwR8rrkS.mjs';
2
- export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-DwR8rrkS.mjs';
3
- import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-D0-nhvuW.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-C057dkLF.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-DOzPxcth.mjs';
2
+ export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-DOzPxcth.mjs';
3
+ import { h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, p as projectPath } from './types-BKCvo4Cv.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-DDN-Yir4.mjs';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
- import { i as initialMachineMetadata, d as createSessionMetadata, e as setupOfflineReconnection, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, f as startHappyServer, r as registerKillSessionHandler, B as BasePermissionHandler } from './index-CzSv4d_S.mjs';
7
+ import { g as initialMachineMetadata, h as createSessionMetadata, j as setupOfflineReconnection, n as notifyDaemonSessionStarted, k as MessageQueue2, m as hashObject, o as startHappyServer, u as registerKillSessionHandler, B as BasePermissionHandler } from './index-DO-9Ep72.mjs';
8
8
  import 'node:child_process';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import 'axios';