@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.
- package/dist/{types-BWpRU1VN.mjs → api-CIZKyfSn.mjs} +136 -1725
- package/dist/{types-DvTsFaqR.cjs → api-CwOCQeLH.cjs} +317 -1961
- package/dist/auth-0VnlXT9f.mjs +181 -0
- package/dist/auth-BE8dD20T.mjs +270 -0
- package/dist/auth-BIleVCw0.cjs +274 -0
- package/dist/auth-Cas9DR3b.cjs +183 -0
- package/dist/browser-Cc08m7of.cjs +22 -0
- package/dist/browser-Dll5hxOV.mjs +20 -0
- package/dist/claudeLocal-CPT3zcIU.mjs +344 -0
- package/dist/claudeLocal-WkgLbl1a.cjs +352 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -1
- package/dist/codex/happyMcpStdioBridge.mjs +4 -1
- package/dist/common-BdNgghUJ.mjs +75 -0
- package/dist/common-CZvSRU4p.cjs +83 -0
- package/dist/{config-0Xnc8ARM.cjs → config-Draa-5XC.cjs} +21 -34
- package/dist/{config-_GW4YBCP.mjs → config-rTT4qTU5.mjs} +7 -20
- package/dist/connect-BfC1tYP_.mjs +679 -0
- package/dist/connect-CJZ73syK.cjs +681 -0
- package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
- package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
- package/dist/controlClient-CM_QcJnB.mjs +159 -0
- package/dist/controlClient-DZMIsKsT.cjs +168 -0
- package/dist/deviceExec-CU1R9CRb.cjs +84 -0
- package/dist/deviceExec-D93AOVV4.mjs +81 -0
- package/dist/diagnose-B3D1a_v0.cjs +60 -0
- package/dist/diagnose-CuRKXxt1.mjs +58 -0
- package/dist/doctor-C08m5Z9f.cjs +78 -0
- package/dist/doctor-Cya9YSA0.cjs +214 -0
- package/dist/doctor-DZYB6IVc.mjs +211 -0
- package/dist/doctor-ON7pN5G5.mjs +74 -0
- package/dist/downloadImage-DfSFLb-h.mjs +887 -0
- package/dist/downloadImage-MQMrUUxS.cjs +897 -0
- package/dist/enroll-BxlbVftP.cjs +115 -0
- package/dist/enroll-C52x8oly.mjs +113 -0
- package/dist/evidence-Ccd9iYcj.mjs +127 -0
- package/dist/evidence-CjWBG5rX.cjs +129 -0
- package/dist/formatImageMessage-B4lSxlk2.mjs +43 -0
- package/dist/formatImageMessage-DbyG8c2j.cjs +46 -0
- package/dist/github-DKcyF1zp.mjs +57 -0
- package/dist/github-h9GhJJ_C.cjs +59 -0
- package/dist/index-CDTNgfUf.cjs +578 -0
- package/dist/index-CPKdvi7J.mjs +554 -0
- package/dist/index.cjs +812 -40
- package/dist/index.mjs +813 -40
- package/dist/install-BokHRgFW.cjs +98 -0
- package/dist/install-C_cp8b2-.cjs +96 -0
- package/dist/install-DAeCn8ae.mjs +94 -0
- package/dist/install-oRAjVLjP.mjs +95 -0
- package/dist/lib.cjs +18 -12
- package/dist/lib.mjs +13 -7
- package/dist/{loadableToolOutput-va3RLplh.mjs → loadableToolOutput-Cuygmd4K.mjs} +4 -2
- package/dist/{loadableToolOutput-5vdQB98M.cjs → loadableToolOutput-DDCJjywt.cjs} +63 -61
- package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
- package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
- package/dist/persistence-BFjvtUad.mjs +944 -0
- package/dist/persistence-JLUn_Nou.cjs +996 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/prompt-BTWDZZO0.cjs +25 -0
- package/dist/prompt-BsAZsPbo.mjs +22 -0
- package/dist/release-BMEzNONE.mjs +168 -0
- package/dist/release-EhOJeAZe.cjs +171 -0
- package/dist/run-BDn2aE3Q.cjs +2152 -0
- package/dist/run-DBZqN6Kn.mjs +2128 -0
- package/dist/runClaude-DNHvERX0.mjs +3763 -0
- package/dist/runClaude-uaKxPZPm.cjs +3766 -0
- package/dist/{runCodex-skq0j05d.cjs → runCodex-82UrZLme.cjs} +169 -153
- package/dist/{runCodex-DiKzBf9q.mjs → runCodex-C-RTM0wA.mjs} +36 -20
- package/dist/{runGemini-BeqrVq8R.mjs → runGemini-Bm6-4Xm2.mjs} +34 -17
- package/dist/{runGemini-CVp-CEIh.cjs → runGemini-CXMr4Jwb.cjs} +212 -195
- package/dist/{runOneShot-BQNkNW61.cjs → runOneShot-1B6RMYT7.cjs} +31 -55
- package/dist/{runOneShot-ltCpHbBB.mjs → runOneShot-ioARRHrx.mjs} +14 -38
- package/dist/{serve-BECdtivY.cjs → serve-BtdAhkIE.cjs} +35 -58
- package/dist/{serve-BNdoEDVx.mjs → serve-CwibwU6N.mjs} +12 -35
- package/dist/session-2sU4Bu1R.mjs +108 -0
- package/dist/session-D--F87zL.cjs +110 -0
- package/dist/sessionKeyCache-4Tp8pJ3N.cjs +36 -0
- package/dist/sessionKeyCache-D2BwO91C.mjs +33 -0
- package/dist/sessionList-BCSw3qYt.mjs +51 -0
- package/dist/sessionList-ICJsjNtJ.cjs +54 -0
- package/dist/sessionSummary-Bkz52BPh.cjs +393 -0
- package/dist/sessionSummary-lHGBWQSb.mjs +387 -0
- package/dist/spawnHappyCLI-CoORX0YW.mjs +66 -0
- package/dist/spawnHappyCLI-D_V-AAoP.cjs +70 -0
- package/dist/ssh-BTOlhhxi.cjs +333 -0
- package/dist/ssh-DL_AkRAD.mjs +328 -0
- package/dist/systemdScope-BeOtAkhk.mjs +25 -0
- package/dist/systemdScope-DW3-UoKB.cjs +29 -0
- package/dist/task-CQzrHRZe.mjs +165 -0
- package/dist/task-CSpaC9HX.cjs +167 -0
- package/dist/time-Bj2HRkKH.cjs +33 -0
- package/dist/time-DgsBLJA_.mjs +29 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-BoJh4Qfv.mjs +57 -0
- package/dist/types-DWeB7aqh.cjs +59 -0
- package/dist/uninstall-BiKyRAb1.cjs +77 -0
- package/dist/uninstall-nFMFGUVM.mjs +75 -0
- package/dist/update-BcqRHXf2.mjs +109 -0
- package/dist/update-Dw8pgsAT.cjs +111 -0
- package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
- package/dist/workflowCommon-Bl568-rt.cjs +62 -0
- package/dist/workflowReports-BEWuQesA.cjs +113 -0
- package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
- package/package.json +1 -1
- package/dist/index-BbCDDvoB.cjs +0 -11376
- package/dist/index-vvZXdswy.mjs +0 -11336
- package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
- 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;
|