hatchee 0.3.0 → 0.4.0
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/cli.mjs +193 -23
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -8018,7 +8018,7 @@ function flattenItem(item) {
|
|
|
8018
8018
|
return { text: item.text, color: item.color };
|
|
8019
8019
|
}
|
|
8020
8020
|
}
|
|
8021
|
-
var PROTOCOL_VERSION =
|
|
8021
|
+
var PROTOCOL_VERSION = 4, PROTOCOL_MIN = 1, AgentState, RuleAction, Rule, DiffStat, LineColor, TermLine, ToolDecision, TranscriptItem, SessionSnapshot, ServerHello, Encrypted, SessionUpsert, SessionRemoved, Line, Item, PermissionRequest, PermissionResolved, ErrorMsg, Pong, RulesUpdate, DirEntry, DirsUpdate, RecentSession, SessionsUpdate, DiffFile, DiffUpdate, DaemonToPhone, DaemonInner, ClientHello, PermissionReply, RulesSync, UserMessage, SpawnMode, SpawnEffort, Spawn, Stop, SetSession, Interrupt, ListDirs, ListSessions, GetDiff, Ping, PushRegister, PhoneToDaemon, PhoneInner, clip = (s, n) => s.length > n ? s.slice(0, n - 1) + "…" : s;
|
|
8022
8022
|
var init_src = __esm(() => {
|
|
8023
8023
|
init_zod();
|
|
8024
8024
|
AgentState = exports_external.enum([
|
|
@@ -8163,6 +8163,15 @@ var init_src = __esm(() => {
|
|
|
8163
8163
|
t: exports_external.literal("sessions"),
|
|
8164
8164
|
sessions: exports_external.array(RecentSession)
|
|
8165
8165
|
});
|
|
8166
|
+
DiffFile = exports_external.object({
|
|
8167
|
+
file: exports_external.string(),
|
|
8168
|
+
patch: exports_external.string()
|
|
8169
|
+
});
|
|
8170
|
+
DiffUpdate = exports_external.object({
|
|
8171
|
+
t: exports_external.literal("diff"),
|
|
8172
|
+
sessionId: exports_external.string(),
|
|
8173
|
+
files: exports_external.array(DiffFile)
|
|
8174
|
+
});
|
|
8166
8175
|
DaemonToPhone = exports_external.discriminatedUnion("t", [
|
|
8167
8176
|
ServerHello,
|
|
8168
8177
|
SessionUpsert,
|
|
@@ -8176,6 +8185,7 @@ var init_src = __esm(() => {
|
|
|
8176
8185
|
RulesUpdate,
|
|
8177
8186
|
DirsUpdate,
|
|
8178
8187
|
SessionsUpdate,
|
|
8188
|
+
DiffUpdate,
|
|
8179
8189
|
Encrypted
|
|
8180
8190
|
]);
|
|
8181
8191
|
DaemonInner = exports_external.discriminatedUnion("t", [
|
|
@@ -8189,7 +8199,8 @@ var init_src = __esm(() => {
|
|
|
8189
8199
|
Pong,
|
|
8190
8200
|
RulesUpdate,
|
|
8191
8201
|
DirsUpdate,
|
|
8192
|
-
SessionsUpdate
|
|
8202
|
+
SessionsUpdate,
|
|
8203
|
+
DiffUpdate
|
|
8193
8204
|
]);
|
|
8194
8205
|
ClientHello = exports_external.object({
|
|
8195
8206
|
t: exports_external.literal("hello"),
|
|
@@ -8202,7 +8213,8 @@ var init_src = __esm(() => {
|
|
|
8202
8213
|
t: exports_external.literal("permission_reply"),
|
|
8203
8214
|
requestId: exports_external.string(),
|
|
8204
8215
|
allow: exports_external.boolean(),
|
|
8205
|
-
always: exports_external.boolean().default(false)
|
|
8216
|
+
always: exports_external.boolean().default(false),
|
|
8217
|
+
answer: exports_external.string().nullable().default(null)
|
|
8206
8218
|
});
|
|
8207
8219
|
RulesSync = exports_external.object({
|
|
8208
8220
|
t: exports_external.literal("rules_sync"),
|
|
@@ -8240,6 +8252,7 @@ var init_src = __esm(() => {
|
|
|
8240
8252
|
});
|
|
8241
8253
|
ListDirs = exports_external.object({ t: exports_external.literal("list_dirs") });
|
|
8242
8254
|
ListSessions = exports_external.object({ t: exports_external.literal("list_sessions") });
|
|
8255
|
+
GetDiff = exports_external.object({ t: exports_external.literal("get_diff"), sessionId: exports_external.string() });
|
|
8243
8256
|
Ping = exports_external.object({ t: exports_external.literal("ping") });
|
|
8244
8257
|
PushRegister = exports_external.object({
|
|
8245
8258
|
t: exports_external.literal("push_register"),
|
|
@@ -8258,6 +8271,7 @@ var init_src = __esm(() => {
|
|
|
8258
8271
|
Interrupt,
|
|
8259
8272
|
ListDirs,
|
|
8260
8273
|
ListSessions,
|
|
8274
|
+
GetDiff,
|
|
8261
8275
|
Ping,
|
|
8262
8276
|
PushRegister,
|
|
8263
8277
|
Encrypted
|
|
@@ -8272,6 +8286,7 @@ var init_src = __esm(() => {
|
|
|
8272
8286
|
Interrupt,
|
|
8273
8287
|
ListDirs,
|
|
8274
8288
|
ListSessions,
|
|
8289
|
+
GetDiff,
|
|
8275
8290
|
Ping,
|
|
8276
8291
|
PushRegister
|
|
8277
8292
|
]);
|
|
@@ -11771,19 +11786,74 @@ class Registry {
|
|
|
11771
11786
|
});
|
|
11772
11787
|
return { branch, branches, diff };
|
|
11773
11788
|
}
|
|
11789
|
+
gitDiff(cwd) {
|
|
11790
|
+
let raw = "";
|
|
11791
|
+
try {
|
|
11792
|
+
raw = execSync("git diff", { cwd, stdio: ["ignore", "pipe", "ignore"], timeout: 3000, maxBuffer: 4 * 1024 * 1024 }).toString();
|
|
11793
|
+
} catch {
|
|
11794
|
+
return [];
|
|
11795
|
+
}
|
|
11796
|
+
if (!raw.trim())
|
|
11797
|
+
return [];
|
|
11798
|
+
const files = [];
|
|
11799
|
+
for (const chunk of raw.split(/^diff --git /m).slice(1)) {
|
|
11800
|
+
if (files.length >= 12)
|
|
11801
|
+
break;
|
|
11802
|
+
const header = chunk.split(`
|
|
11803
|
+
`, 1)[0] ?? "";
|
|
11804
|
+
const file = header.match(/ b\/(.*)$/)?.[1] ?? header.split(" ").pop() ?? "?";
|
|
11805
|
+
const body = chunk.split(`
|
|
11806
|
+
`).filter((l) => l.startsWith("@@") || l.startsWith("+") || l.startsWith("-") || l.startsWith(" ")).join(`
|
|
11807
|
+
`);
|
|
11808
|
+
const lines = body.split(`
|
|
11809
|
+
`);
|
|
11810
|
+
const clipped = lines.length > 120 ? lines.slice(0, 120).join(`
|
|
11811
|
+
`) + `
|
|
11812
|
+
… (+${lines.length - 120} more lines)` : body;
|
|
11813
|
+
files.push({ file, patch: clipped.slice(0, 6000) });
|
|
11814
|
+
}
|
|
11815
|
+
return files;
|
|
11816
|
+
}
|
|
11817
|
+
prune(now = Date.now(), keepAlive = () => false) {
|
|
11818
|
+
const ENDED_TTL = 60 * 60000;
|
|
11819
|
+
const IDLE_TTL = 12 * 60 * 60000;
|
|
11820
|
+
const removed = [];
|
|
11821
|
+
for (const [id, s] of this.sessions) {
|
|
11822
|
+
if (keepAlive(id))
|
|
11823
|
+
continue;
|
|
11824
|
+
const ttl = ["done", "canceled", "error"].includes(s.state) ? ENDED_TTL : IDLE_TTL;
|
|
11825
|
+
if (now - s.updatedAt > ttl)
|
|
11826
|
+
removed.push(id);
|
|
11827
|
+
}
|
|
11828
|
+
for (const id of removed) {
|
|
11829
|
+
this.sessions.delete(id);
|
|
11830
|
+
this.items.delete(id);
|
|
11831
|
+
}
|
|
11832
|
+
return removed;
|
|
11833
|
+
}
|
|
11774
11834
|
}
|
|
11775
11835
|
|
|
11776
11836
|
// src/transcript.ts
|
|
11777
11837
|
init_src();
|
|
11778
11838
|
import { openSync, readSync, closeSync, fstatSync, readdirSync, statSync, existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
11779
11839
|
import { homedir as homedir2 } from "node:os";
|
|
11780
|
-
import { join as join2, basename } from "node:path";
|
|
11840
|
+
import { join as join2, basename, resolve, sep } from "node:path";
|
|
11781
11841
|
var claudeDir = () => process.env.CLAUDE_CONFIG_DIR || join2(homedir2(), ".claude");
|
|
11842
|
+
function isSafeTranscriptPath(path) {
|
|
11843
|
+
if (!path.endsWith(".jsonl"))
|
|
11844
|
+
return false;
|
|
11845
|
+
const real = resolve(path);
|
|
11846
|
+
const root = resolve(claudeDir());
|
|
11847
|
+
return real === root || real.startsWith(root + sep);
|
|
11848
|
+
}
|
|
11782
11849
|
|
|
11783
11850
|
class TranscriptReader {
|
|
11784
11851
|
offsets = new Map;
|
|
11852
|
+
static MAX_READ = 256 * 1024;
|
|
11785
11853
|
read(sessionId, path) {
|
|
11786
|
-
if (typeof path !== "string" || !path)
|
|
11854
|
+
if (typeof path !== "string" || !path || !isSafeTranscriptPath(path))
|
|
11855
|
+
return [];
|
|
11856
|
+
if (basename(path) !== `${sessionId}.jsonl`)
|
|
11787
11857
|
return [];
|
|
11788
11858
|
let fd;
|
|
11789
11859
|
try {
|
|
@@ -11798,6 +11868,8 @@ class TranscriptReader {
|
|
|
11798
11868
|
off = 0;
|
|
11799
11869
|
if (size <= off)
|
|
11800
11870
|
return [];
|
|
11871
|
+
if (size - off > TranscriptReader.MAX_READ)
|
|
11872
|
+
off = size - TranscriptReader.MAX_READ;
|
|
11801
11873
|
const buf = Buffer.alloc(size - off);
|
|
11802
11874
|
readSync(fd, buf, 0, buf.length, off);
|
|
11803
11875
|
const text = buf.toString("utf8");
|
|
@@ -12028,6 +12100,11 @@ class Daemon {
|
|
|
12028
12100
|
if (p.authed)
|
|
12029
12101
|
this.sendTo(p, msg);
|
|
12030
12102
|
}
|
|
12103
|
+
broadcastV(minProto, msg) {
|
|
12104
|
+
for (const p of this.phones)
|
|
12105
|
+
if (p.authed && p.proto >= minProto)
|
|
12106
|
+
this.sendTo(p, msg);
|
|
12107
|
+
}
|
|
12031
12108
|
emit(sessionId, item) {
|
|
12032
12109
|
this.registry.appendItem(sessionId, item);
|
|
12033
12110
|
for (const p of this.phones) {
|
|
@@ -12161,6 +12238,14 @@ class Daemon {
|
|
|
12161
12238
|
this.spawnState(sessionId, "error", "couldn't start Claude Code");
|
|
12162
12239
|
this.spawned.delete(sessionId);
|
|
12163
12240
|
}
|
|
12241
|
+
pruneSessions(now = Date.now()) {
|
|
12242
|
+
const removed = this.registry.prune(now, (id) => this.spawned.has(id));
|
|
12243
|
+
for (const id of removed) {
|
|
12244
|
+
this.openTools.delete(id);
|
|
12245
|
+
this.broadcast({ t: "session_removed", sessionId: id });
|
|
12246
|
+
}
|
|
12247
|
+
return removed;
|
|
12248
|
+
}
|
|
12164
12249
|
wakePhones(server, danger) {
|
|
12165
12250
|
if (!this.relayPush)
|
|
12166
12251
|
return;
|
|
@@ -12265,17 +12350,17 @@ class Daemon {
|
|
|
12265
12350
|
return;
|
|
12266
12351
|
this.pending.delete(msg.requestId);
|
|
12267
12352
|
clearTimeout(req.timer);
|
|
12268
|
-
if (msg.always && msg.allow && !req.danger) {
|
|
12353
|
+
if (msg.always && msg.allow && !req.danger && req.kind === "tool") {
|
|
12269
12354
|
const pattern = req.command.split(/\s+/).slice(0, 2).join(" ");
|
|
12270
12355
|
if (pattern && !this.cfg.rules.some((r) => r.pattern === pattern && r.scope === this.cfg.host)) {
|
|
12271
12356
|
this.cfg.rules.unshift({ pattern, action: "allow", scope: this.cfg.host });
|
|
12272
12357
|
this.persist();
|
|
12273
12358
|
}
|
|
12274
12359
|
}
|
|
12275
|
-
req.resolve({ allow: msg.allow, always: msg.always, by: "phone" });
|
|
12360
|
+
req.resolve({ allow: msg.allow, always: msg.always, by: "phone", answer: msg.answer ?? null });
|
|
12276
12361
|
this.broadcast({ t: "permission_resolved", requestId: msg.requestId, allowed: msg.allow, by: "phone" });
|
|
12277
12362
|
const s = this.registry.sessions.get(req.sessionId);
|
|
12278
|
-
if (s) {
|
|
12363
|
+
if (s && req.kind === "tool") {
|
|
12279
12364
|
this.broadcast({
|
|
12280
12365
|
t: "session_upsert",
|
|
12281
12366
|
session: this.registry.upsert({ id: s.id, state: msg.allow ? "working" : "canceled", action: msg.allow ? "continuing…" : "denied from phone", command: "", plan: [] }, this.cfg.host)
|
|
@@ -12344,6 +12429,11 @@ class Daemon {
|
|
|
12344
12429
|
case "list_sessions":
|
|
12345
12430
|
this.sendTo(p, { t: "sessions", sessions: listRecentSessions() });
|
|
12346
12431
|
break;
|
|
12432
|
+
case "get_diff": {
|
|
12433
|
+
const cwd = this.registry.sessions.get(msg.sessionId)?.cwd;
|
|
12434
|
+
this.sendTo(p, { t: "diff", sessionId: msg.sessionId, files: cwd ? this.registry.gitDiff(cwd) : [] });
|
|
12435
|
+
break;
|
|
12436
|
+
}
|
|
12347
12437
|
case "stop": {
|
|
12348
12438
|
const owned = this.spawned.get(msg.sessionId);
|
|
12349
12439
|
if (owned) {
|
|
@@ -12442,14 +12532,18 @@ class Daemon {
|
|
|
12442
12532
|
}
|
|
12443
12533
|
}
|
|
12444
12534
|
async handlePreToolUse(body, sessionId, cwd, isSpawned = false) {
|
|
12535
|
+
if (body.tool_name === "AskUserQuestion")
|
|
12536
|
+
return this.handleQuestion(body, sessionId, cwd);
|
|
12445
12537
|
const command = toolSummary(body.tool_name, body.tool_input);
|
|
12446
12538
|
const rawCommand = body.tool_name === "Bash" ? String(body.tool_input?.command ?? "") : command;
|
|
12447
12539
|
const danger = isDangerous(rawCommand) || isDangerousTool(body.tool_name, body.tool_input);
|
|
12448
12540
|
const isPlan = body.tool_name === "ExitPlanMode";
|
|
12449
|
-
const toolUseId = isPlan
|
|
12541
|
+
const toolUseId = isPlan || isSpawned ? null : this.registerOpenTool(sessionId, body);
|
|
12450
12542
|
const mark = (d) => {
|
|
12451
12543
|
if (toolUseId) {
|
|
12452
12544
|
this.emit(sessionId, { kind: "tool_decision", id: crypto.randomUUID(), toolUseId, decision: d });
|
|
12545
|
+
} else if (isSpawned && !isPlan) {
|
|
12546
|
+
this.markStreamDecision(sessionId, body, d);
|
|
12453
12547
|
} else {
|
|
12454
12548
|
const line = flattenItem({ kind: "tool_decision", id: "", toolUseId: "", decision: d });
|
|
12455
12549
|
if (line)
|
|
@@ -12498,15 +12592,15 @@ class Daemon {
|
|
|
12498
12592
|
expiresAt: Date.now() + HOOK_WAIT_MS
|
|
12499
12593
|
});
|
|
12500
12594
|
this.wakePhones(s.server, danger);
|
|
12501
|
-
const result = await new Promise((
|
|
12595
|
+
const result = await new Promise((resolve2) => {
|
|
12502
12596
|
const timer = setTimeout(() => {
|
|
12503
12597
|
this.pending.delete(requestId);
|
|
12504
12598
|
this.broadcast({ t: "permission_resolved", requestId, allowed: false, by: "timeout" });
|
|
12505
12599
|
const back = this.registry.upsert({ id: sessionId, state: "working", action: "answer in the terminal (phone timed out)", command: "", plan: [] }, this.cfg.host);
|
|
12506
12600
|
this.broadcast({ t: "session_upsert", session: back });
|
|
12507
|
-
|
|
12601
|
+
resolve2({ allow: false, by: "timeout" });
|
|
12508
12602
|
}, HOOK_WAIT_MS);
|
|
12509
|
-
this.pending.set(requestId, { requestId, sessionId, command, danger, timer, resolve });
|
|
12603
|
+
this.pending.set(requestId, { requestId, sessionId, command, danger, kind: "tool", timer, resolve: resolve2 });
|
|
12510
12604
|
});
|
|
12511
12605
|
if (result.by === "timeout") {
|
|
12512
12606
|
mark("timeout");
|
|
@@ -12520,6 +12614,64 @@ class Daemon {
|
|
|
12520
12614
|
note(sessionId, text, color) {
|
|
12521
12615
|
this.emit(sessionId, { kind: "note", id: crypto.randomUUID(), text, color });
|
|
12522
12616
|
}
|
|
12617
|
+
async handleQuestion(body, sessionId, cwd) {
|
|
12618
|
+
const qs = Array.isArray(body.tool_input?.questions) ? body.tool_input.questions : [];
|
|
12619
|
+
const q = qs[0] ?? {};
|
|
12620
|
+
const question = String(q.question ?? "Claude has a question").slice(0, 300);
|
|
12621
|
+
const options = Array.isArray(q.options) ? q.options.map((o) => String(o?.label ?? o ?? "")).filter(Boolean).slice(0, 4) : [];
|
|
12622
|
+
const connected = [...this.phones].some((p) => p.authed && p.proto >= 4);
|
|
12623
|
+
const wakeable = [...this.pushTokens.values()].some((t) => t.apnsToken);
|
|
12624
|
+
if (!connected && !wakeable)
|
|
12625
|
+
return decision("ask", "no phone connected");
|
|
12626
|
+
const requestId = crypto.randomUUID();
|
|
12627
|
+
this.emit(sessionId, { kind: "note", id: crypto.randomUUID(), text: `? ${question}`, color: "warn" });
|
|
12628
|
+
const s = this.registry.upsert({
|
|
12629
|
+
id: sessionId,
|
|
12630
|
+
cwd,
|
|
12631
|
+
state: "waiting",
|
|
12632
|
+
action: question.slice(0, 120),
|
|
12633
|
+
answers: options,
|
|
12634
|
+
command: ""
|
|
12635
|
+
}, this.cfg.host);
|
|
12636
|
+
this.broadcast({ t: "session_upsert", session: s });
|
|
12637
|
+
this.broadcastV(4, {
|
|
12638
|
+
t: "permission_request",
|
|
12639
|
+
requestId,
|
|
12640
|
+
sessionId,
|
|
12641
|
+
toolName: "AskUserQuestion",
|
|
12642
|
+
command: question,
|
|
12643
|
+
danger: false,
|
|
12644
|
+
expiresAt: Date.now() + HOOK_WAIT_MS
|
|
12645
|
+
});
|
|
12646
|
+
this.wakePhones(s.server, false);
|
|
12647
|
+
const result = await new Promise((resolve2) => {
|
|
12648
|
+
const timer = setTimeout(() => {
|
|
12649
|
+
this.pending.delete(requestId);
|
|
12650
|
+
this.broadcast({ t: "permission_resolved", requestId, allowed: false, by: "timeout" });
|
|
12651
|
+
const back2 = this.registry.upsert({ id: sessionId, state: "working", action: "answer in the terminal (phone timed out)", answers: [] }, this.cfg.host);
|
|
12652
|
+
this.broadcast({ t: "session_upsert", session: back2 });
|
|
12653
|
+
resolve2({ allow: false, by: "timeout" });
|
|
12654
|
+
}, HOOK_WAIT_MS);
|
|
12655
|
+
this.pending.set(requestId, {
|
|
12656
|
+
requestId,
|
|
12657
|
+
sessionId,
|
|
12658
|
+
command: question,
|
|
12659
|
+
danger: false,
|
|
12660
|
+
kind: "question",
|
|
12661
|
+
timer,
|
|
12662
|
+
resolve: resolve2
|
|
12663
|
+
});
|
|
12664
|
+
});
|
|
12665
|
+
if (result.by === "timeout")
|
|
12666
|
+
return decision("ask", "phone did not answer");
|
|
12667
|
+
const answer = (result.answer ?? "").trim();
|
|
12668
|
+
const back = this.registry.upsert({ id: sessionId, state: "working", action: "continuing…", answers: [] }, this.cfg.host);
|
|
12669
|
+
this.broadcast({ t: "session_upsert", session: back });
|
|
12670
|
+
if (!answer)
|
|
12671
|
+
return decision("ask", "answer it in the terminal");
|
|
12672
|
+
this.emit(sessionId, { kind: "user", id: crypto.randomUUID(), text: answer });
|
|
12673
|
+
return decision("deny", `The user answered the question from their phone: "${answer}". Proceed with that answer — do not ask again.`);
|
|
12674
|
+
}
|
|
12523
12675
|
registerOpenTool(sessionId, body) {
|
|
12524
12676
|
const id = typeof body.tool_use_id === "string" && body.tool_use_id ? body.tool_use_id : crypto.randomUUID();
|
|
12525
12677
|
const tool = String(body.tool_name ?? "tool");
|
|
@@ -12537,20 +12689,34 @@ class Daemon {
|
|
|
12537
12689
|
this.openTools.set(sessionId, open2);
|
|
12538
12690
|
return id;
|
|
12539
12691
|
}
|
|
12540
|
-
streamToolTarget(sessionId) {
|
|
12692
|
+
streamToolTarget(sessionId, tool, detail) {
|
|
12541
12693
|
const items = this.registry.items.get(sessionId) ?? [];
|
|
12542
|
-
const
|
|
12543
|
-
for (const i of items)
|
|
12544
|
-
if (i.kind === "
|
|
12545
|
-
|
|
12546
|
-
}
|
|
12694
|
+
const decided = new Set;
|
|
12695
|
+
for (const i of items)
|
|
12696
|
+
if (i.kind === "tool_decision")
|
|
12697
|
+
decided.add(i.toolUseId);
|
|
12547
12698
|
for (let k = items.length - 1;k >= 0; k--) {
|
|
12548
12699
|
const i = items[k];
|
|
12549
|
-
if (i.kind === "tool_use" && !
|
|
12700
|
+
if (i.kind === "tool_use" && !decided.has(i.id) && i.tool === tool && i.input === detail)
|
|
12550
12701
|
return i.id;
|
|
12551
12702
|
}
|
|
12552
12703
|
return null;
|
|
12553
12704
|
}
|
|
12705
|
+
async markStreamDecision(sessionId, body, d) {
|
|
12706
|
+
const tool = String(body.tool_name ?? "");
|
|
12707
|
+
const detail = toolDetail(tool, body.tool_input);
|
|
12708
|
+
for (let attempt = 0;attempt < 6; attempt++) {
|
|
12709
|
+
const target = this.streamToolTarget(sessionId, tool, detail);
|
|
12710
|
+
if (target) {
|
|
12711
|
+
this.emit(sessionId, { kind: "tool_decision", id: crypto.randomUUID(), toolUseId: target, decision: d });
|
|
12712
|
+
return;
|
|
12713
|
+
}
|
|
12714
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
12715
|
+
}
|
|
12716
|
+
const line = flattenItem({ kind: "tool_decision", id: "", toolUseId: "", decision: d });
|
|
12717
|
+
if (line)
|
|
12718
|
+
this.note(sessionId, line.text, line.color);
|
|
12719
|
+
}
|
|
12554
12720
|
closeOpenTool(sessionId, toolUseId) {
|
|
12555
12721
|
const open2 = this.openTools.get(sessionId);
|
|
12556
12722
|
if (!open2)
|
|
@@ -12578,6 +12744,8 @@ class Daemon {
|
|
|
12578
12744
|
}
|
|
12579
12745
|
listen(onListenError) {
|
|
12580
12746
|
const self = this;
|
|
12747
|
+
const sweeper = setInterval(() => self.pruneSessions(), 600000);
|
|
12748
|
+
sweeper.unref?.();
|
|
12581
12749
|
const wss = new import_websocket_server.default({ port: this.cfg.wsPort });
|
|
12582
12750
|
if (onListenError)
|
|
12583
12751
|
wss.on("error", onListenError);
|
|
@@ -12690,8 +12858,10 @@ function extractToolOutput(resp) {
|
|
|
12690
12858
|
}
|
|
12691
12859
|
function toolSummary(toolName, input) {
|
|
12692
12860
|
switch (toolName) {
|
|
12693
|
-
case "Bash":
|
|
12694
|
-
|
|
12861
|
+
case "Bash": {
|
|
12862
|
+
const cmd = String(input?.command ?? "");
|
|
12863
|
+
return cmd.length > 500 ? cmd.slice(0, 480) + " …[truncated]" : cmd;
|
|
12864
|
+
}
|
|
12695
12865
|
case "Edit":
|
|
12696
12866
|
case "MultiEdit":
|
|
12697
12867
|
return `Edit ${input?.file_path ?? "?"}`;
|
|
@@ -12866,13 +13036,13 @@ function uninstallHooks() {
|
|
|
12866
13036
|
} catch {}
|
|
12867
13037
|
}
|
|
12868
13038
|
function readStdin() {
|
|
12869
|
-
return new Promise((
|
|
13039
|
+
return new Promise((resolve2) => {
|
|
12870
13040
|
let data = "";
|
|
12871
13041
|
let done = false;
|
|
12872
13042
|
const finish = () => {
|
|
12873
13043
|
if (!done) {
|
|
12874
13044
|
done = true;
|
|
12875
|
-
|
|
13045
|
+
resolve2(data);
|
|
12876
13046
|
}
|
|
12877
13047
|
};
|
|
12878
13048
|
process.stdin.setEncoding("utf8");
|