@zonease/happy 0.6.38 → 0.6.39

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 (109) hide show
  1. package/dist/{types-BWpRU1VN.mjs → api-CIZKyfSn.mjs} +136 -1725
  2. package/dist/{types-DvTsFaqR.cjs → api-CwOCQeLH.cjs} +317 -1961
  3. package/dist/auth-0VnlXT9f.mjs +181 -0
  4. package/dist/auth-BE8dD20T.mjs +270 -0
  5. package/dist/auth-BIleVCw0.cjs +274 -0
  6. package/dist/auth-Cas9DR3b.cjs +183 -0
  7. package/dist/browser-Cc08m7of.cjs +22 -0
  8. package/dist/browser-Dll5hxOV.mjs +20 -0
  9. package/dist/claudeLocal-CPT3zcIU.mjs +344 -0
  10. package/dist/claudeLocal-WkgLbl1a.cjs +352 -0
  11. package/dist/codex/happyMcpStdioBridge.cjs +4 -1
  12. package/dist/codex/happyMcpStdioBridge.mjs +4 -1
  13. package/dist/common-BdNgghUJ.mjs +75 -0
  14. package/dist/common-CZvSRU4p.cjs +83 -0
  15. package/dist/{config-0Xnc8ARM.cjs → config-Draa-5XC.cjs} +21 -34
  16. package/dist/{config-_GW4YBCP.mjs → config-rTT4qTU5.mjs} +7 -20
  17. package/dist/connect-BfC1tYP_.mjs +679 -0
  18. package/dist/connect-CJZ73syK.cjs +681 -0
  19. package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
  20. package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
  21. package/dist/controlClient-CM_QcJnB.mjs +159 -0
  22. package/dist/controlClient-DZMIsKsT.cjs +168 -0
  23. package/dist/deviceExec-CU1R9CRb.cjs +84 -0
  24. package/dist/deviceExec-D93AOVV4.mjs +81 -0
  25. package/dist/diagnose-B3D1a_v0.cjs +60 -0
  26. package/dist/diagnose-CuRKXxt1.mjs +58 -0
  27. package/dist/doctor-C08m5Z9f.cjs +78 -0
  28. package/dist/doctor-Cya9YSA0.cjs +214 -0
  29. package/dist/doctor-DZYB6IVc.mjs +211 -0
  30. package/dist/doctor-ON7pN5G5.mjs +74 -0
  31. package/dist/downloadImage-DfSFLb-h.mjs +887 -0
  32. package/dist/downloadImage-MQMrUUxS.cjs +897 -0
  33. package/dist/enroll-BxlbVftP.cjs +115 -0
  34. package/dist/enroll-C52x8oly.mjs +113 -0
  35. package/dist/evidence-Ccd9iYcj.mjs +127 -0
  36. package/dist/evidence-CjWBG5rX.cjs +129 -0
  37. package/dist/formatImageMessage-B4lSxlk2.mjs +43 -0
  38. package/dist/formatImageMessage-DbyG8c2j.cjs +46 -0
  39. package/dist/github-DKcyF1zp.mjs +57 -0
  40. package/dist/github-h9GhJJ_C.cjs +59 -0
  41. package/dist/index-CDTNgfUf.cjs +578 -0
  42. package/dist/index-CPKdvi7J.mjs +554 -0
  43. package/dist/index.cjs +812 -40
  44. package/dist/index.mjs +813 -40
  45. package/dist/install-BokHRgFW.cjs +98 -0
  46. package/dist/install-C_cp8b2-.cjs +96 -0
  47. package/dist/install-DAeCn8ae.mjs +94 -0
  48. package/dist/install-oRAjVLjP.mjs +95 -0
  49. package/dist/lib.cjs +18 -12
  50. package/dist/lib.mjs +13 -7
  51. package/dist/{loadableToolOutput-va3RLplh.mjs → loadableToolOutput-Cuygmd4K.mjs} +4 -2
  52. package/dist/{loadableToolOutput-5vdQB98M.cjs → loadableToolOutput-DDCJjywt.cjs} +63 -61
  53. package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
  54. package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
  55. package/dist/persistence-BFjvtUad.mjs +944 -0
  56. package/dist/persistence-JLUn_Nou.cjs +996 -0
  57. package/dist/projectPath-BTBIMB57.mjs +10 -0
  58. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  59. package/dist/prompt-BTWDZZO0.cjs +25 -0
  60. package/dist/prompt-BsAZsPbo.mjs +22 -0
  61. package/dist/release-BMEzNONE.mjs +168 -0
  62. package/dist/release-EhOJeAZe.cjs +171 -0
  63. package/dist/run-BDn2aE3Q.cjs +2152 -0
  64. package/dist/run-DBZqN6Kn.mjs +2128 -0
  65. package/dist/runClaude-DNHvERX0.mjs +3763 -0
  66. package/dist/runClaude-uaKxPZPm.cjs +3766 -0
  67. package/dist/{runCodex-skq0j05d.cjs → runCodex-82UrZLme.cjs} +169 -153
  68. package/dist/{runCodex-DiKzBf9q.mjs → runCodex-C-RTM0wA.mjs} +36 -20
  69. package/dist/{runGemini-BeqrVq8R.mjs → runGemini-Bm6-4Xm2.mjs} +34 -17
  70. package/dist/{runGemini-CVp-CEIh.cjs → runGemini-CXMr4Jwb.cjs} +212 -195
  71. package/dist/{runOneShot-BQNkNW61.cjs → runOneShot-1B6RMYT7.cjs} +31 -55
  72. package/dist/{runOneShot-ltCpHbBB.mjs → runOneShot-ioARRHrx.mjs} +14 -38
  73. package/dist/{serve-BECdtivY.cjs → serve-BtdAhkIE.cjs} +35 -58
  74. package/dist/{serve-BNdoEDVx.mjs → serve-CwibwU6N.mjs} +12 -35
  75. package/dist/session-2sU4Bu1R.mjs +108 -0
  76. package/dist/session-D--F87zL.cjs +110 -0
  77. package/dist/sessionKeyCache-4Tp8pJ3N.cjs +36 -0
  78. package/dist/sessionKeyCache-D2BwO91C.mjs +33 -0
  79. package/dist/sessionList-BCSw3qYt.mjs +51 -0
  80. package/dist/sessionList-ICJsjNtJ.cjs +54 -0
  81. package/dist/sessionSummary-Bkz52BPh.cjs +393 -0
  82. package/dist/sessionSummary-lHGBWQSb.mjs +387 -0
  83. package/dist/spawnHappyCLI-CoORX0YW.mjs +66 -0
  84. package/dist/spawnHappyCLI-D_V-AAoP.cjs +70 -0
  85. package/dist/ssh-BTOlhhxi.cjs +333 -0
  86. package/dist/ssh-DL_AkRAD.mjs +328 -0
  87. package/dist/systemdScope-BeOtAkhk.mjs +25 -0
  88. package/dist/systemdScope-DW3-UoKB.cjs +29 -0
  89. package/dist/task-CQzrHRZe.mjs +165 -0
  90. package/dist/task-CSpaC9HX.cjs +167 -0
  91. package/dist/time-Bj2HRkKH.cjs +33 -0
  92. package/dist/time-DgsBLJA_.mjs +29 -0
  93. package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
  94. package/dist/trimIdent-BAjmv_TG.mjs +20 -0
  95. package/dist/types-BoJh4Qfv.mjs +57 -0
  96. package/dist/types-DWeB7aqh.cjs +59 -0
  97. package/dist/uninstall-BiKyRAb1.cjs +77 -0
  98. package/dist/uninstall-nFMFGUVM.mjs +75 -0
  99. package/dist/update-BcqRHXf2.mjs +109 -0
  100. package/dist/update-Dw8pgsAT.cjs +111 -0
  101. package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
  102. package/dist/workflowCommon-Bl568-rt.cjs +62 -0
  103. package/dist/workflowReports-BEWuQesA.cjs +113 -0
  104. package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
  105. package/package.json +1 -1
  106. package/dist/index-BbCDDvoB.cjs +0 -11376
  107. package/dist/index-vvZXdswy.mjs +0 -11336
  108. package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
  109. package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ var chalk = require('chalk');
4
+ var logger = require('./persistence-JLUn_Nou.cjs');
5
+ var sessionSummary = require('./sessionSummary-Bkz52BPh.cjs');
6
+ require('node:fs/promises');
7
+ require('node:fs');
8
+ require('node:os');
9
+ require('node:path');
10
+ require('zod');
11
+ require('node:crypto');
12
+ require('tweetnacl');
13
+ require('fs');
14
+ require('axios');
15
+ require('./auth-BIleVCw0.cjs');
16
+ require('qrcode-terminal');
17
+ require('./time-Bj2HRkKH.cjs');
18
+ require('./browser-Cc08m7of.cjs');
19
+ require('open');
20
+ require('react');
21
+ require('ink');
22
+ require('./sessionKeyCache-4Tp8pJ3N.cjs');
23
+
24
+ function printSessionHelp() {
25
+ console.log(`
26
+ ${chalk.bold("happy session")} - Read Happy sessions
27
+
28
+ ${chalk.bold("Usage:")}
29
+ happy session summary <sessionId> [options]
30
+ happy session summarize <sessionId> [options]
31
+
32
+ ${chalk.bold("Options:")}
33
+ --limit <n> Raw messages to compact (1-500, default 200)
34
+ --before-seq <n> Page backwards: messages with seq < n
35
+ --after-seq <n> Page forwards: messages with seq > n
36
+ --max-turns <n> Compact turns to print (1-200, default 80)
37
+ --text-limit <n> Max chars per text block (80-4000, default 800)
38
+ --max-tools <n> Max tool names per turn (0-50, default 12)
39
+ --no-tools Hide tool-use names
40
+ --json Print full JSON instead of markdown
41
+ -h, --help Show this help
42
+ `);
43
+ }
44
+ function readNumberFlag(flag, value) {
45
+ if (!value || value.startsWith("-")) {
46
+ throw new Error(`${flag} requires a number value.`);
47
+ }
48
+ const parsed = Number(value);
49
+ if (!Number.isInteger(parsed)) {
50
+ throw new Error(`${flag} must be an integer.`);
51
+ }
52
+ return parsed;
53
+ }
54
+ async function handleSessionCommand(args) {
55
+ const action = args[0];
56
+ if (!action || action === "-h" || action === "--help") {
57
+ printSessionHelp();
58
+ return;
59
+ }
60
+ if (action !== "summary" && action !== "summarize") {
61
+ throw new Error(`Unknown session command: ${action}. Use "happy session summary <sessionId>".`);
62
+ }
63
+ const sessionId = args[1];
64
+ if (!sessionId || sessionId.startsWith("-")) {
65
+ throw new Error('Session ID required. Use "happy session summary <sessionId>".');
66
+ }
67
+ const input = { sessionId };
68
+ let printJson = false;
69
+ for (let i = 2; i < args.length; i++) {
70
+ const arg = args[i];
71
+ if (arg === "--limit") {
72
+ input.limit = readNumberFlag(arg, args[++i]);
73
+ } else if (arg === "--before-seq") {
74
+ input.beforeSeq = readNumberFlag(arg, args[++i]);
75
+ } else if (arg === "--after-seq") {
76
+ input.afterSeq = readNumberFlag(arg, args[++i]);
77
+ } else if (arg === "--max-turns") {
78
+ input.maxTurns = readNumberFlag(arg, args[++i]);
79
+ } else if (arg === "--text-limit") {
80
+ input.textLimit = readNumberFlag(arg, args[++i]);
81
+ } else if (arg === "--max-tools") {
82
+ input.maxToolsPerTurn = readNumberFlag(arg, args[++i]);
83
+ } else if (arg === "--no-tools") {
84
+ input.includeTools = false;
85
+ } else if (arg === "--json") {
86
+ printJson = true;
87
+ } else if (arg === "-h" || arg === "--help") {
88
+ printSessionHelp();
89
+ return;
90
+ } else {
91
+ throw new Error(`Unknown option: ${arg}`);
92
+ }
93
+ }
94
+ const credentials = await logger.readCredentials();
95
+ if (!credentials) {
96
+ throw new Error('No happy credentials found. Run "happy auth login" first.');
97
+ }
98
+ const result = await sessionSummary.runSessionSummary(credentials, input);
99
+ if (printJson) {
100
+ console.log(JSON.stringify(result, null, 2));
101
+ } else {
102
+ console.log(result.summaryText);
103
+ if (result.messageWindow.hasMore) {
104
+ console.log("");
105
+ console.log(chalk.gray(`More messages available. Continue with --before-seq ${result.messageWindow.nextBeforeSeq}`));
106
+ }
107
+ }
108
+ }
109
+
110
+ exports.handleSessionCommand = handleSessionCommand;
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ var fs = require('node:fs');
4
+ var promises = require('node:fs/promises');
5
+ var path = require('node:path');
6
+ var logger = require('./persistence-JLUn_Nou.cjs');
7
+
8
+ function keyPath(sessionId) {
9
+ return path.join(logger.configuration.sessionKeysDir, `${sessionId}.key`);
10
+ }
11
+ async function cacheSessionKey(sessionId, sessionAESKey, machineKey) {
12
+ if (!fs.existsSync(logger.configuration.sessionKeysDir)) {
13
+ fs.mkdirSync(logger.configuration.sessionKeysDir, { recursive: true });
14
+ }
15
+ const encrypted = logger.encryptWithDataKey(logger.encodeBase64(sessionAESKey), machineKey);
16
+ await promises.writeFile(keyPath(sessionId), encrypted);
17
+ }
18
+ function readCachedSessionKey(sessionId, machineKey) {
19
+ const p = keyPath(sessionId);
20
+ if (!fs.existsSync(p)) {
21
+ return null;
22
+ }
23
+ try {
24
+ const blob = new Uint8Array(fs.readFileSync(p));
25
+ const b64 = logger.decryptWithDataKey(blob, machineKey);
26
+ if (typeof b64 !== "string") {
27
+ return null;
28
+ }
29
+ return logger.decodeBase64(b64);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ exports.cacheSessionKey = cacheSessionKey;
36
+ exports.readCachedSessionKey = readCachedSessionKey;
@@ -0,0 +1,33 @@
1
+ import { existsSync, mkdirSync, readFileSync } from 'node:fs';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { c as configuration, k as encryptWithDataKey, f as encodeBase64, m as decryptWithDataKey, e as decodeBase64 } from './persistence-BFjvtUad.mjs';
5
+
6
+ function keyPath(sessionId) {
7
+ return join(configuration.sessionKeysDir, `${sessionId}.key`);
8
+ }
9
+ async function cacheSessionKey(sessionId, sessionAESKey, machineKey) {
10
+ if (!existsSync(configuration.sessionKeysDir)) {
11
+ mkdirSync(configuration.sessionKeysDir, { recursive: true });
12
+ }
13
+ const encrypted = encryptWithDataKey(encodeBase64(sessionAESKey), machineKey);
14
+ await writeFile(keyPath(sessionId), encrypted);
15
+ }
16
+ function readCachedSessionKey(sessionId, machineKey) {
17
+ const p = keyPath(sessionId);
18
+ if (!existsSync(p)) {
19
+ return null;
20
+ }
21
+ try {
22
+ const blob = new Uint8Array(readFileSync(p));
23
+ const b64 = decryptWithDataKey(blob, machineKey);
24
+ if (typeof b64 !== "string") {
25
+ return null;
26
+ }
27
+ return decodeBase64(b64);
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ export { cacheSessionKey as c, readCachedSessionKey as r };
@@ -0,0 +1,51 @@
1
+ import axios from 'axios';
2
+ import { z } from 'zod';
3
+ import { c as configuration } from './persistence-BFjvtUad.mjs';
4
+ import { d as decryptSessionRow } from './sessionSummary-lHGBWQSb.mjs';
5
+
6
+ const sessionListInputSchema = {
7
+ status: z.enum(["active", "archived", "all"]).optional().describe("Filter by session state. Default: all."),
8
+ since: z.number().int().nonnegative().optional().describe("Only return sessions updated after this epoch-millis timestamp."),
9
+ limit: z.number().int().min(1).max(150).optional().describe("Max sessions to return. Default: 50.")
10
+ };
11
+ async function runSessionList(credentials, input) {
12
+ const limit = input.limit ?? 50;
13
+ const params = {};
14
+ if (input.since) params.since = String(input.since);
15
+ const response = await axios.get(
16
+ `${configuration.serverUrl}/v1/sessions`,
17
+ {
18
+ params,
19
+ headers: { Authorization: `Bearer ${credentials.token}` },
20
+ timeout: 1e4
21
+ }
22
+ );
23
+ const filtered = response.data.sessions.map((row) => decryptSessionRow(credentials, row)).filter((row) => row !== null).filter((row) => {
24
+ if (input.status === "active") return row.active;
25
+ if (input.status === "archived") return !row.active;
26
+ return true;
27
+ }).slice(0, limit).map((row) => {
28
+ const meta = row.metadata ?? {};
29
+ const summary = typeof meta.summary?.text === "string" ? meta.summary.text : null;
30
+ return {
31
+ sessionId: row.id,
32
+ active: row.active,
33
+ activeAt: row.activeAt,
34
+ createdAt: row.createdAt,
35
+ updatedAt: row.updatedAt,
36
+ tag: typeof meta.tag === "string" ? meta.tag : null,
37
+ summary,
38
+ agentName: typeof meta.agentName === "string" ? meta.agentName : null,
39
+ machineId: typeof meta.machineId === "string" ? meta.machineId : null,
40
+ machineName: typeof meta.machineName === "string" ? meta.machineName : null,
41
+ path: typeof meta.path === "string" ? meta.path : null,
42
+ flavor: typeof meta.flavor === "string" ? meta.flavor : null
43
+ };
44
+ });
45
+ return {
46
+ sessions: filtered,
47
+ truncated: response.data.sessions.length >= 150
48
+ };
49
+ }
50
+
51
+ export { runSessionList as r, sessionListInputSchema as s };
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+ var z = require('zod');
5
+ var logger = require('./persistence-JLUn_Nou.cjs');
6
+ var sessionSummary = require('./sessionSummary-Bkz52BPh.cjs');
7
+
8
+ const sessionListInputSchema = {
9
+ status: z.z.enum(["active", "archived", "all"]).optional().describe("Filter by session state. Default: all."),
10
+ since: z.z.number().int().nonnegative().optional().describe("Only return sessions updated after this epoch-millis timestamp."),
11
+ limit: z.z.number().int().min(1).max(150).optional().describe("Max sessions to return. Default: 50.")
12
+ };
13
+ async function runSessionList(credentials, input) {
14
+ const limit = input.limit ?? 50;
15
+ const params = {};
16
+ if (input.since) params.since = String(input.since);
17
+ const response = await axios.get(
18
+ `${logger.configuration.serverUrl}/v1/sessions`,
19
+ {
20
+ params,
21
+ headers: { Authorization: `Bearer ${credentials.token}` },
22
+ timeout: 1e4
23
+ }
24
+ );
25
+ const filtered = response.data.sessions.map((row) => sessionSummary.decryptSessionRow(credentials, row)).filter((row) => row !== null).filter((row) => {
26
+ if (input.status === "active") return row.active;
27
+ if (input.status === "archived") return !row.active;
28
+ return true;
29
+ }).slice(0, limit).map((row) => {
30
+ const meta = row.metadata ?? {};
31
+ const summary = typeof meta.summary?.text === "string" ? meta.summary.text : null;
32
+ return {
33
+ sessionId: row.id,
34
+ active: row.active,
35
+ activeAt: row.activeAt,
36
+ createdAt: row.createdAt,
37
+ updatedAt: row.updatedAt,
38
+ tag: typeof meta.tag === "string" ? meta.tag : null,
39
+ summary,
40
+ agentName: typeof meta.agentName === "string" ? meta.agentName : null,
41
+ machineId: typeof meta.machineId === "string" ? meta.machineId : null,
42
+ machineName: typeof meta.machineName === "string" ? meta.machineName : null,
43
+ path: typeof meta.path === "string" ? meta.path : null,
44
+ flavor: typeof meta.flavor === "string" ? meta.flavor : null
45
+ };
46
+ });
47
+ return {
48
+ sessions: filtered,
49
+ truncated: response.data.sessions.length >= 150
50
+ };
51
+ }
52
+
53
+ exports.runSessionList = runSessionList;
54
+ exports.sessionListInputSchema = sessionListInputSchema;
@@ -0,0 +1,393 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+ var z = require('zod');
5
+ var logger = require('./persistence-JLUn_Nou.cjs');
6
+ var auth = require('./auth-BIleVCw0.cjs');
7
+ var sessionKeyCache = require('./sessionKeyCache-4Tp8pJ3N.cjs');
8
+
9
+ function resolveSessionKey(credentials, dataEncryptionKey, sessionId) {
10
+ if (credentials.encryption.type === "legacy") {
11
+ return { key: credentials.encryption.secret, variant: "legacy" };
12
+ }
13
+ if (sessionId) {
14
+ const cached = sessionKeyCache.readCachedSessionKey(sessionId, credentials.encryption.machineKey);
15
+ if (cached) {
16
+ return { key: cached, variant: "dataKey" };
17
+ }
18
+ }
19
+ if (!dataEncryptionKey) {
20
+ return null;
21
+ }
22
+ const blob = logger.decodeBase64(dataEncryptionKey);
23
+ if (blob.length < 1 || blob[0] !== 0) {
24
+ return null;
25
+ }
26
+ const opened = auth.decryptWithEphemeralKey(blob.slice(1), credentials.encryption.machineKey);
27
+ if (!opened) {
28
+ return null;
29
+ }
30
+ return { key: opened, variant: "dataKey" };
31
+ }
32
+ function decryptSessionRow(credentials, row) {
33
+ const resolved = resolveSessionKey(credentials, row.dataEncryptionKey, row.id);
34
+ if (!resolved) {
35
+ return null;
36
+ }
37
+ let metadata = null;
38
+ if (row.metadata) {
39
+ try {
40
+ metadata = resolved.variant === "legacy" ? logger.decryptLegacy(logger.decodeBase64(row.metadata), resolved.key) : logger.decryptWithDataKey(logger.decodeBase64(row.metadata), resolved.key);
41
+ } catch {
42
+ metadata = null;
43
+ }
44
+ }
45
+ let agentState = null;
46
+ if (row.agentState) {
47
+ try {
48
+ agentState = resolved.variant === "legacy" ? logger.decryptLegacy(logger.decodeBase64(row.agentState), resolved.key) : logger.decryptWithDataKey(logger.decodeBase64(row.agentState), resolved.key);
49
+ } catch {
50
+ agentState = null;
51
+ }
52
+ }
53
+ return {
54
+ id: row.id,
55
+ seq: row.seq,
56
+ createdAt: row.createdAt,
57
+ updatedAt: row.updatedAt,
58
+ activeAt: row.activeAt,
59
+ active: row.active,
60
+ metadata,
61
+ agentState,
62
+ isShared: row.isShared,
63
+ encryptionKey: resolved.key,
64
+ encryptionVariant: resolved.variant
65
+ };
66
+ }
67
+
68
+ async function fetchSessionById(credentials, sessionId) {
69
+ const response = await axios.get(
70
+ `${logger.configuration.serverUrl}/v1/sessions`,
71
+ {
72
+ headers: { Authorization: `Bearer ${credentials.token}` },
73
+ timeout: 1e4
74
+ }
75
+ );
76
+ const row = response.data.sessions.find((s) => s.id === sessionId);
77
+ if (!row) return null;
78
+ return decryptSessionRow(credentials, row);
79
+ }
80
+
81
+ function extractEncryptedBase64(content) {
82
+ if (typeof content === "string") return content;
83
+ if (content && typeof content === "object" && content.t === "encrypted" && typeof content.c === "string") {
84
+ return content.c;
85
+ }
86
+ return null;
87
+ }
88
+ function buildPreview(content) {
89
+ if (!content) return "";
90
+ if (content.type === "text" && typeof content.text === "string") {
91
+ return content.text;
92
+ }
93
+ if (content.type === "mixed" && typeof content.text === "string") {
94
+ return content.text;
95
+ }
96
+ if (content.type === "output") {
97
+ const data = content.data;
98
+ if (typeof data === "string") return data;
99
+ if (data && typeof data === "object") {
100
+ if (data.type === "message" && typeof data.message === "string") return data.message;
101
+ if (data.type === "reasoning" && typeof data.message === "string") return data.message;
102
+ if (typeof data.text === "string") return data.text;
103
+ }
104
+ }
105
+ return "";
106
+ }
107
+ function decryptMessage(raw, encryptionKey, encryptionVariant) {
108
+ const base64 = extractEncryptedBase64(raw.content);
109
+ let decrypted = null;
110
+ if (base64) {
111
+ try {
112
+ const bytes = logger.decodeBase64(base64);
113
+ decrypted = encryptionVariant === "legacy" ? logger.decryptLegacy(bytes, encryptionKey) : logger.decryptWithDataKey(bytes, encryptionKey);
114
+ } catch {
115
+ decrypted = null;
116
+ }
117
+ }
118
+ const role = decrypted?.role === "user" || decrypted?.role === "agent" ? decrypted.role : "unknown";
119
+ const content = decrypted?.content ?? null;
120
+ return {
121
+ id: raw.id,
122
+ seq: raw.seq,
123
+ role,
124
+ content,
125
+ meta: decrypted?.meta ?? null,
126
+ textPreview: buildPreview(content),
127
+ sentBy: raw.sentBy,
128
+ sentByName: raw.sentByName,
129
+ createdAt: raw.createdAt,
130
+ updatedAt: raw.updatedAt,
131
+ deliveryIssue: raw.deliveryIssue ?? void 0
132
+ };
133
+ }
134
+
135
+ const DEFAULT_LIMIT = 200;
136
+ const DEFAULT_TEXT_LIMIT = 800;
137
+ const DEFAULT_MAX_TURNS = 80;
138
+ const DEFAULT_MAX_TOOLS_PER_TURN = 12;
139
+ const sessionSummaryInputSchema = {
140
+ sessionId: z.z.string().describe("Target session ID (from happy_session_list)."),
141
+ limit: z.z.number().int().min(1).max(500).optional().describe(`Max raw messages to compact in this call. Default: ${DEFAULT_LIMIT}.`),
142
+ beforeSeq: z.z.number().int().min(1).optional().describe("Return messages with seq < this value (paginate backwards). Mutually exclusive with afterSeq."),
143
+ afterSeq: z.z.number().int().min(0).optional().describe("Return messages with seq > this value (paginate forwards). Mutually exclusive with beforeSeq."),
144
+ maxTurns: z.z.number().int().min(1).max(200).optional().describe(`Max compact turns to return. Default: ${DEFAULT_MAX_TURNS}.`),
145
+ textLimit: z.z.number().int().min(80).max(4e3).optional().describe(`Max chars per user/assistant text block. Default: ${DEFAULT_TEXT_LIMIT}.`),
146
+ includeTools: z.z.boolean().optional().describe("Include compact tool-use names. Default: true."),
147
+ maxToolsPerTurn: z.z.number().int().min(0).max(50).optional().describe(`Max tool names per turn when includeTools=true. Default: ${DEFAULT_MAX_TOOLS_PER_TURN}.`)
148
+ };
149
+ function trimText(text, limit) {
150
+ const normalized = text.replace(/\r\n/g, "\n").trim();
151
+ if (normalized.length <= limit) return normalized;
152
+ return `${normalized.slice(0, limit)}\u2026`;
153
+ }
154
+ function oneLine(text) {
155
+ return text.replace(/\s+/g, " ").trim();
156
+ }
157
+ function imageCount(content) {
158
+ return Array.isArray(content?.images) ? content.images.length : 0;
159
+ }
160
+ function isHumanOnlyMessage(message) {
161
+ const meta = message.meta;
162
+ if (!meta || typeof meta !== "object") return false;
163
+ const collaboration = meta.collaboration;
164
+ return meta.humanOnly === true || meta.skipAiContext === true || collaboration?.kind === "mention";
165
+ }
166
+ function assistantContentItems(messageData) {
167
+ if (Array.isArray(messageData?.message?.content)) {
168
+ return messageData.message.content;
169
+ }
170
+ if (Array.isArray(messageData?.content)) {
171
+ return messageData.content;
172
+ }
173
+ return [];
174
+ }
175
+ function extractAssistantTexts(message, textLimit) {
176
+ const content = message.content;
177
+ if (content?.type !== "output") return [];
178
+ const data = content.data;
179
+ if (typeof data === "string") {
180
+ return [trimText(data, textLimit)].filter(Boolean);
181
+ }
182
+ if (!data || typeof data !== "object") return [];
183
+ const directMessage = typeof data.message === "string" ? data.message : null;
184
+ if (directMessage) {
185
+ return [trimText(directMessage, textLimit)].filter(Boolean);
186
+ }
187
+ const directText = typeof data.text === "string" ? data.text : null;
188
+ if (directText) {
189
+ return [trimText(directText, textLimit)].filter(Boolean);
190
+ }
191
+ return assistantContentItems(data).filter((item) => item?.type === "text" && typeof item.text === "string").map((item) => trimText(item.text, textLimit)).filter(Boolean);
192
+ }
193
+ function extractToolNames(message) {
194
+ const content = message.content;
195
+ if (content?.type !== "output") return [];
196
+ const data = content.data;
197
+ if (!data || typeof data !== "object") return [];
198
+ const names = [];
199
+ for (const item of assistantContentItems(data)) {
200
+ if (item?.type === "tool_use" && typeof item.name === "string") {
201
+ names.push(item.name);
202
+ }
203
+ if (item?.type === "tool_call" && typeof item.name === "string") {
204
+ names.push(item.name);
205
+ }
206
+ }
207
+ const directToolName = typeof data.toolName === "string" ? data.toolName : typeof data.name === "string" && (data.type === "tool_call" || data.type === "tool_use") ? data.name : null;
208
+ if (directToolName) {
209
+ names.push(directToolName);
210
+ }
211
+ return names;
212
+ }
213
+ function pushTurn(turns, turn) {
214
+ if (!turn) return;
215
+ if (!turn.user && turn.assistantTexts.length === 0 && turn.tools.length === 0) return;
216
+ turns.push(turn);
217
+ }
218
+ function compactSessionMessages(messages, options) {
219
+ const chronological = [...messages].sort((a, b) => a.seq - b.seq);
220
+ const turns = [];
221
+ let current = null;
222
+ let compactedCount = 0;
223
+ for (const message of chronological) {
224
+ if (isHumanOnlyMessage(message)) {
225
+ continue;
226
+ }
227
+ if (message.role === "user") {
228
+ const content = message.content;
229
+ const text = typeof content?.text === "string" ? trimText(content.text, options.textLimit) : "";
230
+ if (!text && imageCount(content) === 0) continue;
231
+ pushTurn(turns, current);
232
+ current = {
233
+ startSeq: message.seq,
234
+ endSeq: message.seq,
235
+ startedAt: message.createdAt,
236
+ user: {
237
+ seq: message.seq,
238
+ createdAt: message.createdAt,
239
+ text,
240
+ imageCount: imageCount(content)
241
+ },
242
+ assistantTexts: [],
243
+ tools: [],
244
+ omittedToolCount: 0
245
+ };
246
+ compactedCount += 1;
247
+ continue;
248
+ }
249
+ const texts = extractAssistantTexts(message, options.textLimit);
250
+ const toolNames = options.includeTools ? extractToolNames(message) : [];
251
+ if (texts.length === 0 && toolNames.length === 0) continue;
252
+ if (!current) {
253
+ current = {
254
+ startSeq: message.seq,
255
+ endSeq: message.seq,
256
+ startedAt: message.createdAt,
257
+ user: null,
258
+ assistantTexts: [],
259
+ tools: [],
260
+ omittedToolCount: 0
261
+ };
262
+ }
263
+ current.endSeq = message.seq;
264
+ for (const text of texts) {
265
+ current.assistantTexts.push({ seq: message.seq, createdAt: message.createdAt, text });
266
+ compactedCount += 1;
267
+ }
268
+ for (const name of toolNames) {
269
+ if (current.tools.length < options.maxToolsPerTurn) {
270
+ current.tools.push({ seq: message.seq, createdAt: message.createdAt, name });
271
+ } else {
272
+ current.omittedToolCount += 1;
273
+ }
274
+ compactedCount += 1;
275
+ }
276
+ }
277
+ pushTurn(turns, current);
278
+ return {
279
+ turns: turns.slice(-options.maxTurns),
280
+ compactedCount
281
+ };
282
+ }
283
+ function formatTimestamp(ms) {
284
+ return new Date(ms).toISOString();
285
+ }
286
+ function buildSummaryText(result) {
287
+ const lines = [];
288
+ lines.push(`# Happy session ${result.sessionId}`);
289
+ lines.push("");
290
+ lines.push(`- Active: ${result.active ? "yes" : "no"}`);
291
+ lines.push(`- Agent: ${[result.agent.name, result.agent.model].filter(Boolean).join(" / ") || "unknown"}`);
292
+ lines.push(`- Path: ${result.path ?? "unknown"}`);
293
+ lines.push(`- Updated: ${formatTimestamp(result.updatedAt)}`);
294
+ if (result.storedSummary) {
295
+ lines.push(`- Stored summary: ${oneLine(result.storedSummary)}`);
296
+ }
297
+ lines.push(`- Window: ${result.messageWindow.rawCount} raw messages, ${result.messageWindow.compactedCount} compact items, hasMore=${result.messageWindow.hasMore}`);
298
+ lines.push("");
299
+ if (result.turns.length === 0) {
300
+ lines.push("No user/assistant text found in this window.");
301
+ return lines.join("\n");
302
+ }
303
+ result.turns.forEach((turn, index) => {
304
+ lines.push(`## Turn ${index + 1} (seq ${turn.startSeq}-${turn.endSeq})`);
305
+ if (turn.user) {
306
+ const imageSuffix = turn.user.imageCount > 0 ? ` [${turn.user.imageCount} image${turn.user.imageCount === 1 ? "" : "s"}]` : "";
307
+ lines.push(`User: ${oneLine(turn.user.text) || "(no text)"}${imageSuffix}`);
308
+ } else {
309
+ lines.push("User: (continuation before the first user message in this window)");
310
+ }
311
+ for (const text of turn.assistantTexts) {
312
+ lines.push(`Assistant: ${oneLine(text.text)}`);
313
+ }
314
+ if (turn.tools.length > 0) {
315
+ const omitted = turn.omittedToolCount > 0 ? `, +${turn.omittedToolCount} more` : "";
316
+ lines.push(`Tools: ${turn.tools.map((tool) => tool.name).join(", ")}${omitted}`);
317
+ }
318
+ lines.push("");
319
+ });
320
+ return lines.join("\n").trimEnd();
321
+ }
322
+ async function runSessionSummary(credentials, input) {
323
+ if (input.beforeSeq !== void 0 && input.afterSeq !== void 0) {
324
+ throw new Error("Specify only one of beforeSeq / afterSeq, not both.");
325
+ }
326
+ const session = await fetchSessionById(credentials, input.sessionId);
327
+ if (!session) {
328
+ throw new Error(`Session ${input.sessionId} not found or not decryptable.`);
329
+ }
330
+ const params = {
331
+ limit: String(input.limit ?? DEFAULT_LIMIT)
332
+ };
333
+ if (input.beforeSeq !== void 0) params.before_seq = String(input.beforeSeq);
334
+ if (input.afterSeq !== void 0) params.after_seq = String(input.afterSeq);
335
+ const response = await axios.get(
336
+ `${logger.configuration.serverUrl}/v3/sessions/${encodeURIComponent(input.sessionId)}/messages`,
337
+ {
338
+ params,
339
+ headers: { Authorization: `Bearer ${credentials.token}` },
340
+ timeout: 1e4
341
+ }
342
+ );
343
+ const decrypted = response.data.messages.map(
344
+ (raw) => decryptMessage(raw, session.encryptionKey, session.encryptionVariant)
345
+ );
346
+ const compacted = compactSessionMessages(decrypted, {
347
+ textLimit: input.textLimit ?? DEFAULT_TEXT_LIMIT,
348
+ maxTurns: input.maxTurns ?? DEFAULT_MAX_TURNS,
349
+ includeTools: input.includeTools ?? true,
350
+ maxToolsPerTurn: input.maxToolsPerTurn ?? DEFAULT_MAX_TOOLS_PER_TURN
351
+ });
352
+ const seqs = decrypted.map((message) => message.seq);
353
+ const minSeq = seqs.length > 0 ? Math.min(...seqs) : null;
354
+ const maxSeq = seqs.length > 0 ? Math.max(...seqs) : null;
355
+ const meta = session.metadata ?? {};
356
+ const resultWithoutText = {
357
+ sessionId: session.id,
358
+ active: session.active,
359
+ activeAt: session.activeAt,
360
+ createdAt: session.createdAt,
361
+ updatedAt: session.updatedAt,
362
+ agent: {
363
+ name: typeof meta.agentName === "string" ? meta.agentName : null,
364
+ model: typeof meta.model === "string" ? meta.model : null,
365
+ mode: typeof meta.permissionMode === "string" ? meta.permissionMode : null,
366
+ flavor: typeof meta.flavor === "string" ? meta.flavor : null
367
+ },
368
+ machine: {
369
+ id: typeof meta.machineId === "string" ? meta.machineId : null,
370
+ name: typeof meta.machineName === "string" ? meta.machineName : null
371
+ },
372
+ path: typeof meta.path === "string" ? meta.path : null,
373
+ storedSummary: typeof meta.summary?.text === "string" ? meta.summary.text : null,
374
+ messageWindow: {
375
+ rawCount: decrypted.length,
376
+ compactedCount: compacted.compactedCount,
377
+ hasMore: response.data.hasMore,
378
+ nextBeforeSeq: minSeq,
379
+ nextAfterSeq: maxSeq
380
+ },
381
+ turns: compacted.turns
382
+ };
383
+ return {
384
+ ...resultWithoutText,
385
+ summaryText: buildSummaryText(resultWithoutText)
386
+ };
387
+ }
388
+
389
+ exports.decryptMessage = decryptMessage;
390
+ exports.decryptSessionRow = decryptSessionRow;
391
+ exports.fetchSessionById = fetchSessionById;
392
+ exports.runSessionSummary = runSessionSummary;
393
+ exports.sessionSummaryInputSchema = sessionSummaryInputSchema;