very-happy-cli 0.2.107 → 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-B3WfHjyi.cjs → AcpBackend-Cc4Ilypg.cjs} +1 -1
  2. package/dist/{AcpBackend-leZGKCNL.mjs → AcpBackend-DOzPxcth.mjs} +1 -1
  3. package/dist/{AcpSessionManager-Cza1nTaQ.cjs → AcpSessionManager-CLfzatC_.cjs} +1 -1
  4. package/dist/{AcpSessionManager-CI_-4TvB.mjs → AcpSessionManager-DDN-Yir4.mjs} +1 -1
  5. package/dist/{config-CpVncJmR.cjs → config-DiVKfYr0.cjs} +2 -2
  6. package/dist/{config-TEae6YCF.mjs → config-DnvzvFHR.mjs} +2 -2
  7. package/dist/{index-RyvQwH4I.cjs → index-B2U15moC.cjs} +2 -2
  8. package/dist/{index-Dg78JF0h.mjs → index-CGKxZKlo.mjs} +2 -2
  9. package/dist/{index-b_E_olpx.cjs → index-D2qU5HP4.cjs} +4 -4
  10. package/dist/{index-CIfhQ_X6.mjs → index-DO-9Ep72.mjs} +181 -89
  11. package/dist/{index-D9H-dsUT.mjs → index-DaQIcHvY.mjs} +5 -5
  12. package/dist/{index-CF9-9B70.cjs → index-dd2R8uSE.cjs} +191 -92
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-tPNg8EgD.mjs → installTerminalHooks-BDagPTQW.mjs} +2 -2
  16. package/dist/{installTerminalHooks-DMSUDPG3.cjs → installTerminalHooks-BoUmuYdq.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-DUXABUpc.mjs → mcp-Ck_y0mCB.mjs} +2 -2
  20. package/dist/{mcp-B13ZiAxa.cjs → mcp-Du1QEnIf.cjs} +2 -2
  21. package/dist/{runGemini-aCMjZ4lm.mjs → runGemini-D20S6Lc3.mjs} +4 -4
  22. package/dist/{runGemini-Dvf_ajZd.cjs → runGemini-DV2M0A_k.cjs} +4 -4
  23. package/dist/{runOpenClaw-D9v6E9th.cjs → runOpenClaw-C1Q2zl--.cjs} +3 -3
  24. package/dist/{runOpenClaw-BLdlHdZa.mjs → runOpenClaw-CuuXkfDE.mjs} +3 -3
  25. package/dist/{send-DUoz9am9.cjs → send-CtgnnJic.cjs} +2 -2
  26. package/dist/{send-DAnYf1wR.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-Gr4_bY2m.mjs → spawn-BKBY9P3J.mjs} +16 -3
  30. package/dist/{spawn-4FlGcOQK.cjs → spawn-CTO_hR8v.cjs} +16 -3
  31. package/dist/{types-DOJq8cyH.mjs → types-BKCvo4Cv.mjs} +8 -8
  32. package/dist/{types-4iIbK7lA.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-4iIbK7lA.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-DOJq8cyH.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-4iIbK7lA.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-DOJq8cyH.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-4iIbK7lA.cjs');
8
- var index = require('./index-CF9-9B70.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-DOJq8cyH.mjs';
6
- import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './index-CIfhQ_X6.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-CF9-9B70.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-4iIbK7lA.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-CIfhQ_X6.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-DOJq8cyH.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-B3WfHjyi.cjs');
4
- var persistence = require('./types-4iIbK7lA.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-Cza1nTaQ.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-CF9-9B70.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-DOJq8cyH.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
  }
@@ -9077,6 +9084,12 @@ async function startDaemon() {
9077
9084
  });
9078
9085
  claudeAuthService.start();
9079
9086
  apiMachine.connect();
9087
+ if (identityConflict?.confidence === "weak") {
9088
+ void apiMachine.updateMachineMetadata((current) => ({
9089
+ ...current ?? initialMachineMetadata,
9090
+ host: initialMachineMetadata.host
9091
+ })).then(() => logger.debug(`[DAEMON RUN] claimed the machine record for host ${initialMachineMetadata.host}`)).catch((error) => logger.debug("[DAEMON RUN] could not refresh the recorded host:", error));
9092
+ }
9080
9093
  let cliUpdateCheckRunning = false;
9081
9094
  const refreshCliUpdate = async () => {
9082
9095
  if (cliUpdateCheckRunning) return;
@@ -9291,6 +9304,109 @@ function formatTranscript(bodies) {
9291
9304
  return lines.join("\n");
9292
9305
  }
9293
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
+
9294
9410
  const PERSONAL_MEMORY_SOFT_LIMIT_CHARS = 2e3;
9295
9411
  function headingTitle(line) {
9296
9412
  const m = /^##(?!#)\s*(.*?)\s*$/.exec(line);
@@ -9387,19 +9503,24 @@ function fail(text) {
9387
9503
  function describeSession(id, persisted, extra) {
9388
9504
  const meta = persisted?.metadata;
9389
9505
  const parts = [id];
9390
- if (extra?.live !== void 0) parts.push(extra.live ? "[running]" : "[not running]");
9391
9506
  if (meta?.summary?.text) parts.push(`title="${meta.summary.text}"`);
9392
9507
  if (meta?.path) parts.push(`cwd=${meta.path}`);
9393
9508
  if (meta?.flavor) parts.push(`agent=${meta.flavor}`);
9509
+ if (meta?.tags?.length) parts.push(`tags=${meta.tags.join(",")}`);
9394
9510
  if (meta?.variant === "assistant") parts.push("(assistant)");
9395
- if (extra?.pid) parts.push(`pid=${extra.pid}`);
9396
9511
  parts.push(sessionWebUrl(id));
9397
9512
  return parts.join(" ");
9398
9513
  }
9399
- async function bearerToken() {
9400
- const credentials = await readCredentialsForConfiguredRelay();
9401
- if (!credentials) throw new Error("CLI is not authenticated (no ~/.happy/access.key)");
9402
- 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(" ");
9403
9524
  }
9404
9525
  function registerAssistantTools(mcp) {
9405
9526
  mcp.registerTool("sessions_list", {
@@ -9408,22 +9529,10 @@ function registerAssistantTools(mcp) {
9408
9529
  inputSchema: {}
9409
9530
  }, async () => {
9410
9531
  try {
9411
- const live = await listDaemonSessions();
9412
- const persisted = readPersistedSessions();
9413
- const lines = [];
9414
- const seen = /* @__PURE__ */ new Set();
9415
- for (const child of live) {
9416
- const id = child.happySessionId;
9417
- seen.add(id);
9418
- lines.push(describeSession(id, persisted[id], { pid: child.pid, live: true }));
9419
- }
9420
- 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);
9421
- for (const [id, entry] of rest) {
9422
- lines.push(describeSession(id, entry, { live: false }));
9423
- }
9424
- if (lines.length === 0) return ok("No sessions found on this machine.");
9425
- return ok(`${lines.length} session(s):
9426
- ${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")}`);
9427
9536
  } catch (error) {
9428
9537
  return fail(`Failed to list sessions: ${error instanceof Error ? error.message : String(error)}`);
9429
9538
  }
@@ -9437,37 +9546,12 @@ ${lines.join("\n")}`);
9437
9546
  }
9438
9547
  }, async (args) => {
9439
9548
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9440
- const limit = Math.max(1, Math.min(100, Math.floor(args.limit ?? 20)));
9441
9549
  try {
9442
- const persisted = readPersistedSessions()[args.sessionId];
9443
- if (!persisted) {
9444
- 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).`);
9445
- }
9446
- const token = await bearerToken();
9447
- const response = await axios.get(
9448
- `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(args.sessionId)}/messages`,
9449
- {
9450
- params: { before_seq: 2147483647, limit },
9451
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${configuration.currentCliVersion}` },
9452
- timeout: 15e3
9453
- }
9454
- );
9455
- const messages = Array.isArray(response.data?.messages) ? response.data.messages : [];
9456
- messages.reverse();
9457
- const key = decodeBase64(persisted.encryptionKey);
9458
- const bodies = messages.map((m) => {
9459
- if (m.content?.t !== "encrypted") return null;
9460
- try {
9461
- return decrypt(key, persisted.encryptionVariant, decodeBase64(m.content.c));
9462
- } catch {
9463
- return null;
9464
- }
9465
- });
9466
- const transcript = formatTranscript(bodies);
9467
- const header = describeSession(args.sessionId, persisted);
9468
- return ok(`${header}
9469
- --- last ${messages.length} message(s) ---
9470
- ${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}`);
9471
9555
  } catch (error) {
9472
9556
  return fail(`Failed to read session: ${error instanceof Error ? error.message : String(error)}`);
9473
9557
  }
@@ -9532,7 +9616,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9532
9616
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9533
9617
  try {
9534
9618
  const persisted = readPersistedSessions()[args.sessionId];
9535
- const success = await stopDaemonSession(args.sessionId);
9619
+ const success = await stopSession(args.sessionId);
9536
9620
  if (!success) return fail(`Session ${args.sessionId} was not found among the daemon's running sessions.`);
9537
9621
  return ok(`Stopped ${describeSession(args.sessionId, persisted)}`);
9538
9622
  } catch (error) {
@@ -9548,15 +9632,7 @@ ${transcript.length > 0 ? transcript : "(no readable conversation content in thi
9548
9632
  }, async (args) => {
9549
9633
  if (!isValidSessionId(args.sessionId)) return fail("Invalid sessionId");
9550
9634
  try {
9551
- const token = await bearerToken();
9552
- await axios.post(
9553
- `${configuration.serverUrl}/v1/sessions/${encodeURIComponent(args.sessionId)}/archive`,
9554
- {},
9555
- {
9556
- headers: { "Authorization": `Bearer ${token}`, "X-Happy-Client": `${MCP_CLIENT_TAG}/${configuration.currentCliVersion}` },
9557
- timeout: 1e4
9558
- }
9559
- );
9635
+ await archiveSession(args.sessionId);
9560
9636
  const persisted = readPersistedSessions()[args.sessionId];
9561
9637
  return ok(`Archived ${describeSession(args.sessionId, persisted)}`);
9562
9638
  } catch (error) {
@@ -10232,7 +10308,7 @@ function registerSideQuestionHandler(rpc, deps) {
10232
10308
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
10233
10309
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
10234
10310
  const run = deps.run ?? (async (input) => {
10235
- const { query } = await import('./index-Dg78JF0h.mjs');
10311
+ const { query } = await import('./index-CGKxZKlo.mjs');
10236
10312
  return runSideQuestion(query, input);
10237
10313
  });
10238
10314
  const slots = /* @__PURE__ */ new Map();
@@ -10772,6 +10848,7 @@ async function runClaude(credentials, options = {}) {
10772
10848
  const forkedFromMessageId = process.env.HAPPY_FORKED_FROM_MESSAGE_ID;
10773
10849
  const importedFromClaudeSessionId = process.env.HAPPY_IMPORTED_FROM_CLAUDE_SESSION_ID;
10774
10850
  const importTitle = process.env.HAPPY_IMPORT_TITLE;
10851
+ const originTags = spawnOriginTags();
10775
10852
  let metadata = {
10776
10853
  path: workingDirectory,
10777
10854
  host: os.hostname(),
@@ -10804,12 +10881,13 @@ async function runClaude(credentials, options = {}) {
10804
10881
  ...importedFromClaudeSessionId ? { importedFromClaudeSessionId } : {},
10805
10882
  ...importTitle ? { summary: { text: importTitle, updatedAt: Date.now() } } : {},
10806
10883
  ...isAssistantVariant ? { variant: "assistant" } : {},
10807
- // B-091: sessions the assistant dispatched (daemon spawn RPC exports
10808
- // HAPPY_SPAWNED_BY, B-069) are born with the 'assistant' tag so every
10809
- // list shows their origin. NOT on the meta-agent itself — that one is
10810
- // the variant above and never joins the lists at all. (Shared helper:
10811
- // the other flavors get the same tag via createSessionMetadata.)
10812
- ...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 } : {}
10813
10891
  };
10814
10892
  const reconnectSessionId = process.env.HAPPY_RECONNECT_SESSION_ID;
10815
10893
  const reconnectKeyBase64 = process.env.HAPPY_RECONNECT_ENCRYPTION_KEY;
@@ -15160,7 +15238,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15160
15238
  process.exit(0);
15161
15239
  } else if (subcommand === "install-terminal-hooks") {
15162
15240
  try {
15163
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-tPNg8EgD.mjs');
15241
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-BDagPTQW.mjs');
15164
15242
  const command = parseTerminalHooksArgs(args.slice(1));
15165
15243
  if (command.action === "help") {
15166
15244
  console.log(TERMINAL_HOOKS_HELP);
@@ -15177,7 +15255,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15177
15255
  }
15178
15256
  } else if (subcommand === "spawn") {
15179
15257
  try {
15180
- const { handleSpawnCommand } = await import('./spawn-Gr4_bY2m.mjs');
15258
+ const { handleSpawnCommand } = await import('./spawn-BKBY9P3J.mjs');
15181
15259
  await handleSpawnCommand(args.slice(1));
15182
15260
  } catch (error) {
15183
15261
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15187,9 +15265,21 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15187
15265
  process.exit(1);
15188
15266
  }
15189
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;
15190
15280
  } else if (subcommand === "send") {
15191
15281
  try {
15192
- const { handleSendCommand } = await import('./send-DAnYf1wR.mjs');
15282
+ const { handleSendCommand } = await import('./send-yUiBqHHL.mjs');
15193
15283
  await handleSendCommand(args.slice(1));
15194
15284
  } catch (error) {
15195
15285
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -15295,9 +15385,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15295
15385
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
15296
15386
  const projectId = args[3];
15297
15387
  try {
15298
- const { saveGoogleCloudProjectToConfig } = await import('./config-TEae6YCF.mjs');
15299
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./types-DOJq8cyH.mjs').then(function (n) { return n.ap; });
15300
- const { ApiClient: ApiClient2 } = await import('./types-DOJq8cyH.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; });
15301
15391
  let userEmail = void 0;
15302
15392
  try {
15303
15393
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -15328,7 +15418,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15328
15418
  }
15329
15419
  if (geminiSubcommand === "project" && args[2] === "get") {
15330
15420
  try {
15331
- const { readGeminiLocalConfig } = await import('./config-TEae6YCF.mjs');
15421
+ const { readGeminiLocalConfig } = await import('./config-DnvzvFHR.mjs');
15332
15422
  const config = readGeminiLocalConfig();
15333
15423
  if (config.googleCloudProject) {
15334
15424
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -15368,7 +15458,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15368
15458
  process.exit(0);
15369
15459
  }
15370
15460
  try {
15371
- const { runGemini } = await import('./runGemini-aCMjZ4lm.mjs');
15461
+ const { runGemini } = await import('./runGemini-D20S6Lc3.mjs');
15372
15462
  let startedBy = void 0;
15373
15463
  for (let i = 1; i < args.length; i++) {
15374
15464
  if (args[i] === "--started-by") {
@@ -15390,7 +15480,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15390
15480
  return;
15391
15481
  } else if (subcommand === "acp") {
15392
15482
  try {
15393
- const { runAcp, resolveAcpAgentConfig } = await import('./index-D9H-dsUT.mjs');
15483
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-DaQIcHvY.mjs');
15394
15484
  let startedBy = void 0;
15395
15485
  let verbose = false;
15396
15486
  const acpArgs = [];
@@ -15430,7 +15520,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15430
15520
  return;
15431
15521
  } else if (subcommand === "openclaw") {
15432
15522
  try {
15433
- const { runOpenClaw } = await import('./runOpenClaw-BLdlHdZa.mjs');
15523
+ const { runOpenClaw } = await import('./runOpenClaw-CuuXkfDE.mjs');
15434
15524
  let startedBy = void 0;
15435
15525
  let verbose = false;
15436
15526
  let gatewayUrl;
@@ -15481,7 +15571,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
15481
15571
  return;
15482
15572
  } else if (subcommand === "mcp" && args.length === 1) {
15483
15573
  try {
15484
- const { handleMcpCommand } = await import('./mcp-DUXABUpc.mjs');
15574
+ const { handleMcpCommand } = await import('./mcp-Ck_y0mCB.mjs');
15485
15575
  await handleMcpCommand();
15486
15576
  } catch (error) {
15487
15577
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -15696,6 +15786,8 @@ ${chalk.bold("Usage:")}
15696
15786
  print its web URL (for automation; see spawn --help)
15697
15787
  very-happy send Send a message into an existing session
15698
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)
15699
15791
  very-happy codex Start Codex mode
15700
15792
  very-happy gemini Start Gemini mode (ACP)
15701
15793
  very-happy acp Start a generic ACP-compatible agent
@@ -15837,4 +15929,4 @@ ${chalk.bold("Examples:")}
15837
15929
  }
15838
15930
  }
15839
15931
 
15840
- 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-leZGKCNL.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-leZGKCNL.mjs';
3
- import { h as connectionState, A as ApiClient, i as readSettings, j as encodeBase64, l as logger, p as projectPath } from './types-DOJq8cyH.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-CI_-4TvB.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-CIfhQ_X6.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';