very-happy-cli 0.2.133 → 0.2.135
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/{AcpBackend-DgG8o4jd.cjs → AcpBackend-C0Bwx6bc.cjs} +3 -3
- package/dist/{AcpBackend-BQm7dNza.mjs → AcpBackend-CkDpu1n_.mjs} +3 -3
- package/dist/{AcpSessionManager-C7unPEZO.mjs → AcpSessionManager-DB_mfFaH.mjs} +1 -1
- package/dist/{AcpSessionManager-DDFEmhQp.cjs → AcpSessionManager-DqvUp-UU.cjs} +1 -1
- package/dist/{agentGuidance-BaplwDcV.mjs → agentGuidance-7MByWxKj.mjs} +1 -1
- package/dist/{agentGuidance-BItqIZTf.cjs → agentGuidance-DlZM_YPV.cjs} +1 -1
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-uLywzv5t.cjs → config-BO0ZHyoB.cjs} +4 -4
- package/dist/{config-BZ8xEnOv.mjs → config-DzvbcL_x.mjs} +4 -4
- package/dist/{index-Daga0ztv.cjs → index-BEpZjXs4.cjs} +15 -9
- package/dist/{index-ClCeQ1FW.mjs → index-C33JB4xB.mjs} +16 -10
- package/dist/{index-D8i2EViA.mjs → index-CpEdOGpe.mjs} +4 -4
- package/dist/{index-CdH3ecC5.cjs → index-DHI6Y1xW.cjs} +4 -4
- package/dist/{index-6Xm0CgN_.cjs → index-DNbKbe23.cjs} +1536 -1202
- package/dist/{index-EsR8ayIg.mjs → index-gefLe1Bk.mjs} +1534 -1200
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-NH4kiB2E.mjs → installTerminalHooks-BWoHGCew.mjs} +4 -4
- package/dist/{installTerminalHooks-CKG0MYVK.cjs → installTerminalHooks-gmWKACNb.cjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +20 -60
- package/dist/lib.d.mts +20 -60
- package/dist/lib.mjs +2 -2
- package/dist/{limits-Du7S6jqA.mjs → limits-CPTgiQ9A.mjs} +8 -8
- package/dist/{limits-XRNzIlVm.cjs → limits-K17AgMHD.cjs} +8 -8
- package/dist/{mcp-CAMCuudA.cjs → mcp-CK1NCZm0.cjs} +4 -4
- package/dist/{mcp-D8zhjyZa.mjs → mcp-DhuxpwTB.mjs} +4 -4
- package/dist/{runGemini-Db4rX54F.mjs → runGemini-BVJE7LZl.mjs} +6 -6
- package/dist/{runGemini-BIVHp0ST.cjs → runGemini-n0wD2Z9R.cjs} +6 -6
- package/dist/{runOpenClaw-D2MceXPO.mjs → runOpenClaw-CJaIYpB7.mjs} +5 -5
- package/dist/{runOpenClaw-a0f2pZZp.cjs → runOpenClaw-fYx1XweW.cjs} +5 -5
- package/dist/{send-DKbumEcg.cjs → send-WlJ4r3U2.cjs} +4 -4
- package/dist/{send-1Wjb0lIh.mjs → send-fWHOFJV9.mjs} +4 -4
- package/dist/{sessions-DXCCRDb5.mjs → sessions-5fJxQ2xj.mjs} +4 -4
- package/dist/{sessions-SIFOOlGp.cjs → sessions-Cx_3cvN6.cjs} +4 -4
- package/dist/{spawn-Bj3W6HR4.mjs → spawn-CF1ZV-YH.mjs} +4 -4
- package/dist/{spawn-C-XpSPLX.cjs → spawn-DVJtNwWN.cjs} +4 -4
- package/dist/{teams-ChFzXAqr.cjs → teams-BbYi0fWG.cjs} +4 -4
- package/dist/{teams-CqhFQLVC.mjs → teams-gzvKrMfn.mjs} +4 -4
- package/dist/{todo-B0gbQHH8.mjs → todo-BAK2DVLV.mjs} +3 -3
- package/dist/{todo-o0clp-_I.cjs → todo-DHY20tpD.cjs} +3 -3
- package/dist/{types-6DrEgHVx.mjs → types-BEXGahmj.mjs} +128 -35
- package/dist/{types-CcjVmS2i.cjs → types-DjAHWb4J.cjs} +129 -35
- package/package.json +8 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var axios = require('axios');
|
|
4
|
-
var persistence = require('./limits-
|
|
4
|
+
var persistence = require('./limits-K17AgMHD.cjs');
|
|
5
5
|
var node_events = require('node:events');
|
|
6
6
|
var socket_ioClient = require('socket.io-client');
|
|
7
7
|
var z = require('zod');
|
|
@@ -53,7 +53,8 @@ const sessionRoleSchema = z__namespace.enum(["user", "agent"]);
|
|
|
53
53
|
const sessionTextEventSchema = z__namespace.object({
|
|
54
54
|
t: z__namespace.literal("text"),
|
|
55
55
|
text: z__namespace.string(),
|
|
56
|
-
thinking: z__namespace.boolean().optional()
|
|
56
|
+
thinking: z__namespace.boolean().optional(),
|
|
57
|
+
actualModel: z__namespace.string().optional()
|
|
57
58
|
});
|
|
58
59
|
const sessionServiceMessageEventSchema = z__namespace.object({
|
|
59
60
|
t: z__namespace.literal("service"),
|
|
@@ -800,6 +801,25 @@ const ContextUsageSchema = z.z.object({
|
|
|
800
801
|
contextWindow: z.z.number().finite().positive(),
|
|
801
802
|
updatedAt: z.z.number().finite().nonnegative()
|
|
802
803
|
});
|
|
804
|
+
const AgentVersionsSchema = z.z.object({
|
|
805
|
+
checkedAt: z.z.number(),
|
|
806
|
+
daemonPid: z.z.number(),
|
|
807
|
+
agents: z.z.array(z.z.object({
|
|
808
|
+
id: z.z.string(),
|
|
809
|
+
installed: z.z.string().nullable(),
|
|
810
|
+
latest: z.z.string().nullable(),
|
|
811
|
+
status: z.z.string()
|
|
812
|
+
}))
|
|
813
|
+
});
|
|
814
|
+
const AGENT_VERSION_SOURCES = [
|
|
815
|
+
{ id: "claude", label: "Claude Code", command: "claude", package: "@anthropic-ai/claude-code", docs: "https://code.claude.com/docs/en/setup" },
|
|
816
|
+
{ id: "codex", label: "Codex", command: "codex", package: "@openai/codex", docs: "https://github.com/openai/codex#installation-and-usage" },
|
|
817
|
+
{ id: "pi", label: "Pi", command: "pi", package: "@mariozechner/pi-coding-agent", docs: "https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent" },
|
|
818
|
+
{ id: "gemini", label: "Gemini CLI", command: "gemini", package: "@google/gemini-cli", docs: "https://github.com/google-gemini/gemini-cli#installation" },
|
|
819
|
+
{ id: "opencode", label: "OpenCode", command: "opencode", package: "opencode-ai", docs: "https://opencode.ai/docs/" },
|
|
820
|
+
{ id: "openclaw", label: "OpenClaw", command: "openclaw", package: "openclaw", docs: "https://docs.openclaw.ai/install/updating" },
|
|
821
|
+
{ id: "claude-sdk", label: "Claude Agent SDK", command: null, package: "@anthropic-ai/claude-agent-sdk", docs: "https://github.com/anthropics/claude-agent-sdk-typescript" }
|
|
822
|
+
];
|
|
803
823
|
|
|
804
824
|
z.z.object({
|
|
805
825
|
teamsVersion: z.z.number().int().positive().optional(),
|
|
@@ -879,6 +899,8 @@ const WebTerminalListItemSchema = z.z.object({
|
|
|
879
899
|
createdAt: z.z.number().optional(),
|
|
880
900
|
activityAt: z.z.number().optional(),
|
|
881
901
|
agentState: z.z.enum(["working", "needs_input", "idle", "shell"]).optional(),
|
|
902
|
+
agentKind: z.z.string().optional(),
|
|
903
|
+
agentObservedAt: z.z.number().optional(),
|
|
882
904
|
/** B-105: mirror session of the claude running inside the terminal. */
|
|
883
905
|
mirrorSessionId: z.z.string().optional(),
|
|
884
906
|
/** B-150: auto-restored after a restart (ms epoch) — the directory and the
|
|
@@ -909,6 +931,8 @@ const ClosedTerminalRecordSchema = z.z.object({
|
|
|
909
931
|
closedAt: z.z.number()
|
|
910
932
|
});
|
|
911
933
|
z.z.object({
|
|
934
|
+
agentVersions: AgentVersionsSchema.optional(),
|
|
935
|
+
agentVersionEpoch: z.z.number().optional(),
|
|
912
936
|
status: z.z.union([
|
|
913
937
|
z.z.enum(["running", "shutting-down"]),
|
|
914
938
|
z.z.string()
|
|
@@ -1345,7 +1369,7 @@ class RpcHandlerManager {
|
|
|
1345
1369
|
}
|
|
1346
1370
|
}
|
|
1347
1371
|
|
|
1348
|
-
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1372
|
+
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-DjAHWb4J.cjs', document.baseURI).href))));
|
|
1349
1373
|
function projectPath() {
|
|
1350
1374
|
const path$1 = path.resolve(__dirname$1, "..");
|
|
1351
1375
|
return path$1;
|
|
@@ -2446,7 +2470,12 @@ function mapTaskLifecycleSystemMessage(message, state, envelopes) {
|
|
|
2446
2470
|
if (!subtype.startsWith("task_")) return;
|
|
2447
2471
|
if (raw.skip_transcript === true) return;
|
|
2448
2472
|
const toolUseId = typeof raw.tool_use_id === "string" && raw.tool_use_id.length > 0 ? raw.tool_use_id : null;
|
|
2449
|
-
if (!toolUseId
|
|
2473
|
+
if (!toolUseId) return;
|
|
2474
|
+
if (subtype === "task_started" && raw.task_type === "local_bash" && state.bashToolCalls?.has(toolUseId)) {
|
|
2475
|
+
(state.backgroundBashCalls ??= /* @__PURE__ */ new Set()).add(toolUseId);
|
|
2476
|
+
}
|
|
2477
|
+
const backgroundBash = state.backgroundBashCalls?.has(toolUseId) === true;
|
|
2478
|
+
if (!getAgentToolCalls(state).has(toolUseId) && !backgroundBash) return;
|
|
2450
2479
|
const subagent = ensureSessionSubagentIdForProviderSubagent(state, toolUseId);
|
|
2451
2480
|
const usage = raw.usage && typeof raw.usage === "object" ? {
|
|
2452
2481
|
...typeof raw.usage.tool_uses === "number" ? { toolUses: raw.usage.tool_uses } : {},
|
|
@@ -2454,7 +2483,7 @@ function mapTaskLifecycleSystemMessage(message, state, envelopes) {
|
|
|
2454
2483
|
...typeof raw.usage.duration_ms === "number" ? { durationMs: raw.usage.duration_ms } : {}
|
|
2455
2484
|
} : void 0;
|
|
2456
2485
|
if (subtype === "task_started") {
|
|
2457
|
-
rememberSubagentMeta(state, subagent, { description: raw.description, subagentType: raw.subagent_type });
|
|
2486
|
+
rememberSubagentMeta(state, subagent, { description: raw.description, subagentType: backgroundBash ? "background-command" : raw.subagent_type });
|
|
2458
2487
|
const turn = ensureTurn(state, envelopes);
|
|
2459
2488
|
getStartedSubagents(state).delete(subagent);
|
|
2460
2489
|
maybeEmitSubagentStart(state, turn, subagent, envelopes);
|
|
@@ -2543,14 +2572,15 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
|
|
|
2543
2572
|
blockCursor = state.streamBlockCursor ?? 0;
|
|
2544
2573
|
state.streamBlockCursor = blockCursor + blocks.length;
|
|
2545
2574
|
}
|
|
2575
|
+
const actualModel = subagent && typeof message.message?.model === "string" && message.message.model.trim() ? { actualModel: message.message.model.trim() } : {};
|
|
2546
2576
|
const draftKey = (index) => apiMessageId ? streamKeyOf(apiMessageId, blockCursor + index) : void 0;
|
|
2547
2577
|
for (const [blockIndex, block] of blocks.entries()) {
|
|
2548
2578
|
if (block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
|
|
2549
|
-
envelopes.push(createEnvelope("agent", { t: "text", text: block.text }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2579
|
+
envelopes.push(createEnvelope("agent", { t: "text", text: block.text, ...actualModel }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2550
2580
|
continue;
|
|
2551
2581
|
}
|
|
2552
2582
|
if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim().length > 0) {
|
|
2553
|
-
envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2583
|
+
envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true, ...actualModel }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2554
2584
|
continue;
|
|
2555
2585
|
}
|
|
2556
2586
|
if (block.type === "tool_use") {
|
|
@@ -2578,7 +2608,8 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
|
|
|
2578
2608
|
}
|
|
2579
2609
|
continue;
|
|
2580
2610
|
}
|
|
2581
|
-
|
|
2611
|
+
if (name === "Bash") (state.bashToolCalls ??= /* @__PURE__ */ new Set()).add(call);
|
|
2612
|
+
const args = isSubagentTool(name) || name === "Bash" ? { ...baseArgs, sessionSubagent: sessionSubagentForCall } : baseArgs;
|
|
2582
2613
|
envelopes.push(createEnvelope("agent", {
|
|
2583
2614
|
t: "tool-call-start",
|
|
2584
2615
|
call,
|
|
@@ -2679,7 +2710,7 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
|
|
|
2679
2710
|
continue;
|
|
2680
2711
|
}
|
|
2681
2712
|
}
|
|
2682
|
-
if (sessionSubagentForToolResult) {
|
|
2713
|
+
if (sessionSubagentForToolResult && !state.backgroundBashCalls?.has(block.tool_use_id)) {
|
|
2683
2714
|
maybeEmitSubagentStop(state, turnId, sessionSubagentForToolResult, envelopes);
|
|
2684
2715
|
}
|
|
2685
2716
|
}
|
|
@@ -4359,6 +4390,58 @@ function registerTodoHandlers(manager) {
|
|
|
4359
4390
|
});
|
|
4360
4391
|
}
|
|
4361
4392
|
|
|
4393
|
+
function codingAgentFromProcessTree(panePid, snapshot) {
|
|
4394
|
+
const rows = snapshot.split("\n").flatMap((line) => {
|
|
4395
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
4396
|
+
return m ? [{ pid: Number(m[1]), parent: Number(m[2]), command: m[3] }] : [];
|
|
4397
|
+
});
|
|
4398
|
+
const visit = (pid, seen = /* @__PURE__ */ new Set()) => {
|
|
4399
|
+
if (seen.has(pid)) return void 0;
|
|
4400
|
+
seen.add(pid);
|
|
4401
|
+
const row = rows.find((r) => r.pid === pid);
|
|
4402
|
+
if (!row) return void 0;
|
|
4403
|
+
for (const child of rows.filter((r) => r.parent === pid)) {
|
|
4404
|
+
const kind = visit(child.pid, seen);
|
|
4405
|
+
if (kind) return kind;
|
|
4406
|
+
}
|
|
4407
|
+
const words = row.command.split(/\s+/);
|
|
4408
|
+
const executable = words[0].split("/").pop();
|
|
4409
|
+
const candidate = executable === "node" ? words[1]?.split("/").pop() : executable;
|
|
4410
|
+
if (candidate === "pi" || candidate === "pi-coding-agent") return "pi";
|
|
4411
|
+
if (candidate === "codex") return "codex";
|
|
4412
|
+
if (candidate === "claude" || /^\d+\.\d+\.\d+$/.test(candidate ?? "")) return "claude";
|
|
4413
|
+
return void 0;
|
|
4414
|
+
};
|
|
4415
|
+
return visit(panePid);
|
|
4416
|
+
}
|
|
4417
|
+
|
|
4418
|
+
const shells = /* @__PURE__ */ new Set(["zsh", "bash", "fish", "sh", "dash", "ksh", "tcsh", "csh"]);
|
|
4419
|
+
function classifyAgentPane(command, tail) {
|
|
4420
|
+
const cmd = command.trim().replace(/^-/, "").split(/[\\/]/).pop().toLowerCase();
|
|
4421
|
+
const lines = tail.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\r/g, "").trimEnd().split("\n");
|
|
4422
|
+
const screen = lines.join("\n");
|
|
4423
|
+
const footer = lines.slice(-20).join("\n");
|
|
4424
|
+
if (shells.has(cmd)) return { agentState: "shell" };
|
|
4425
|
+
if (!["node", "claude", "codex", "pi"].includes(cmd) && !/^\d+\.\d+\.\d+$/.test(cmd)) return {};
|
|
4426
|
+
const agentKind = cmd === "pi" ? "pi" : cmd === "codex" ? "codex" : cmd === "claude" || /^\d+\.\d+\.\d+$/.test(cmd) ? "claude" : /OpenAI Codex/.test(screen) ? "codex" : /(?:^|\n)\s*pi v\d|Pi can explain its own features/.test(screen) ? "pi" : /Claude Code|tell Claude what to do|bypass permissions on|⏵⏵|[·✻✽✶✳] [^\n]+… \(esc to interrupt\)/.test(footer) ? "claude" : void 0;
|
|
4427
|
+
if (!agentKind) return {};
|
|
4428
|
+
const result = (agentState) => ({ agentKind, agentState });
|
|
4429
|
+
if (agentKind === "pi") {
|
|
4430
|
+
if (/^\s*→ \S/m.test(screen) && /(?:↑↓ navigate.*(?:enter|return) select.*esc cancel|Enter to select.*Esc to cancel)/i.test(footer)) return result("needs_input");
|
|
4431
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s+(?:Working|Thinking|Retrying|Compacting context|Auto-compacting|Summarizing branch).*$/m.test(footer)) return result("working");
|
|
4432
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s/m.test(footer)) return result();
|
|
4433
|
+
const editorRules = footer.match(/^\s*─{8,}\s*$/gm) ?? [];
|
|
4434
|
+
if (editorRules.length >= 2 && /(?:\d+(?:\.\d+)?|\?)%\/\d+(?:\.\d+)?[km]?.* • (?:thinking off|minimal|low|medium|high|xhigh)/i.test(footer)) return result("idle");
|
|
4435
|
+
return result();
|
|
4436
|
+
}
|
|
4437
|
+
const choice = /^[\s│]*[❯›>]\s*\d+\.\s/m.test(footer);
|
|
4438
|
+
if (choice && /Do you want|Would you like|approve|Yes,|Yes\b|Enter to select.*navigate.*Esc to cancel/i.test(footer)) return result("needs_input");
|
|
4439
|
+
if (/esc to interrupt/i.test(footer)) return result("working");
|
|
4440
|
+
if (agentKind === "codex" && /^\s*gpt-[\w.-]+ (?:minimal|low|medium|high|xhigh|fast)[^\n]* · (?:~|\/|[A-Z]:[\\/])/m.test(footer) && /^› /m.test(footer)) return result("idle");
|
|
4441
|
+
if (/\? for shortcuts|bypass permissions on|⏵⏵|\d+% context left/.test(footer)) return result("idle");
|
|
4442
|
+
return result();
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4362
4445
|
function sanitizeGeometry(cols, rows) {
|
|
4363
4446
|
const ok = (v) => typeof v === "number" && Number.isInteger(v) && v >= 2 && v <= 1e4;
|
|
4364
4447
|
return ok(cols) && ok(rows) ? { cols, rows } : void 0;
|
|
@@ -9568,10 +9651,12 @@ class TerminalNotifyTracker {
|
|
|
9568
9651
|
* Feed one observation for a terminal. Returns the notification event this
|
|
9569
9652
|
* observation triggers, or null. Call once per terminal per tick.
|
|
9570
9653
|
*/
|
|
9571
|
-
observe(terminalId, state, now) {
|
|
9654
|
+
observe(terminalId, state, now, agentKind) {
|
|
9572
9655
|
let t = this.tracks.get(terminalId);
|
|
9573
|
-
if (!t) {
|
|
9656
|
+
if (!t || t.agentKind !== agentKind || now - t.lastObservedAt > 45e3) {
|
|
9574
9657
|
t = {
|
|
9658
|
+
agentKind,
|
|
9659
|
+
lastObservedAt: now,
|
|
9575
9660
|
armed: state === "working",
|
|
9576
9661
|
confirmed: state,
|
|
9577
9662
|
candidate: state,
|
|
@@ -9582,6 +9667,7 @@ class TerminalNotifyTracker {
|
|
|
9582
9667
|
this.tracks.set(terminalId, t);
|
|
9583
9668
|
return null;
|
|
9584
9669
|
}
|
|
9670
|
+
t.lastObservedAt = now;
|
|
9585
9671
|
if (state === "working") t.armed = true;
|
|
9586
9672
|
if (state === t.confirmed) {
|
|
9587
9673
|
t.candidate = state;
|
|
@@ -9914,6 +10000,8 @@ function terminalListSignature(items) {
|
|
|
9914
10000
|
t.createdAt ?? 0,
|
|
9915
10001
|
Math.floor((t.activityAt ?? 0) / ACTIVITY_SIGNATURE_BUCKET_MS),
|
|
9916
10002
|
t.agentState ?? "",
|
|
10003
|
+
t.agentKind ?? "",
|
|
10004
|
+
Math.floor((t.agentObservedAt ?? 0) / 1e4),
|
|
9917
10005
|
// B-105: a mirror binding appearing/disappearing MUST push the
|
|
9918
10006
|
// list, or the web never learns the toggle became available.
|
|
9919
10007
|
t.mirrorSessionId ?? "",
|
|
@@ -9962,7 +10050,7 @@ const LIST_SESSIONS_FORMAT = [
|
|
|
9962
10050
|
// command only separates shell/idle). MUST stay before pane_title: that
|
|
9963
10051
|
// field is deliberately last so a pathological 0x1f inside a title can only
|
|
9964
10052
|
// garble the title, never shift the fields.
|
|
9965
|
-
"#{pane_current_command}",
|
|
10053
|
+
"#{pane_current_command} #{pane_pid}",
|
|
9966
10054
|
"#{pane_title}"
|
|
9967
10055
|
].join(LIST_FIELD_SEP);
|
|
9968
10056
|
function parseSessionListLine(line) {
|
|
@@ -9980,7 +10068,8 @@ function parseSessionListLine(line) {
|
|
|
9980
10068
|
vhTitle: vhTitle.trim() || void 0,
|
|
9981
10069
|
manual: manual.trim().length > 0,
|
|
9982
10070
|
tags: parseTerminalTags(vhTags),
|
|
9983
|
-
paneCurrentCommand: paneCommand.trim() || void 0,
|
|
10071
|
+
paneCurrentCommand: paneCommand.trim().replace(/ \d+$/, "") || void 0,
|
|
10072
|
+
.../ (\d+)$/.test(paneCommand) ? { panePid: Number(paneCommand.match(/ (\d+)$/)[1]) } : {},
|
|
9984
10073
|
// Verbatim (no trim): tmux allows edge spaces in a session name and the
|
|
9985
10074
|
// restore lookup / attach echo compare it exactly.
|
|
9986
10075
|
attachTmux: isSafeTmuxSessionName(vhAttach) ? vhAttach : void 0,
|
|
@@ -10022,22 +10111,6 @@ function parseTerminalTags(raw) {
|
|
|
10022
10111
|
return [];
|
|
10023
10112
|
}
|
|
10024
10113
|
}
|
|
10025
|
-
function classifyPane(currentCommand, tail) {
|
|
10026
|
-
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10027
|
-
const isShell = SHELL_COMMANDS.has(cmd);
|
|
10028
|
-
const lines = tail.replace(/\r/g, "").split("\n").map((l) => l.trimEnd());
|
|
10029
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
10030
|
-
const text = lines.join("\n");
|
|
10031
|
-
const last15 = lines.slice(-15).join("\n");
|
|
10032
|
-
const hasDialog = last15.includes("Do you want") || last15.includes("Would you like to proceed") || /^[\s│]*[❯>]\s*1\.\s/m.test(last15) || /\(y\/n\)/i.test(last15);
|
|
10033
|
-
if (hasDialog) return "needs_input";
|
|
10034
|
-
if (text.includes("esc to interrupt")) return "working";
|
|
10035
|
-
const looksLikeClaude = looksLikeClaudeCommand(cmd);
|
|
10036
|
-
const hasIdleFooter = text.includes("? for shortcuts") || text.includes("bypass permissions on") || text.includes("\u23F5\u23F5");
|
|
10037
|
-
if (looksLikeClaude || hasIdleFooter) return "idle";
|
|
10038
|
-
if (isShell) return "shell";
|
|
10039
|
-
return void 0;
|
|
10040
|
-
}
|
|
10041
10114
|
function looksLikeClaudeCommand(currentCommand) {
|
|
10042
10115
|
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10043
10116
|
return cmd === "claude" || cmd === "node" || /^\d+\.\d+(\.\d+)?$/.test(cmd);
|
|
@@ -11048,7 +11121,7 @@ class WebTerminalManager {
|
|
|
11048
11121
|
const now = Date.now();
|
|
11049
11122
|
this.notifyTracker.prune(list.map((t) => t.id));
|
|
11050
11123
|
for (const item of list) {
|
|
11051
|
-
const event = this.notifyTracker.observe(item.id, item.agentState, now);
|
|
11124
|
+
const event = this.notifyTracker.observe(item.id, item.agentState, now, item.agentKind);
|
|
11052
11125
|
if (!event) continue;
|
|
11053
11126
|
try {
|
|
11054
11127
|
this.onNotify({ terminalId: item.id, title: item.title || "Terminal", event });
|
|
@@ -11780,6 +11853,7 @@ class WebTerminalManager {
|
|
|
11780
11853
|
if (r.status !== 0 || !r.stdout) return [];
|
|
11781
11854
|
const hostname = os$1.hostname();
|
|
11782
11855
|
const out = [];
|
|
11856
|
+
let processSnapshot;
|
|
11783
11857
|
for (const line of r.stdout.split("\n")) {
|
|
11784
11858
|
const s = parseSessionListLine(line);
|
|
11785
11859
|
if (!s || !s.name.startsWith("vh-")) continue;
|
|
@@ -11797,7 +11871,15 @@ class WebTerminalManager {
|
|
|
11797
11871
|
} catch {
|
|
11798
11872
|
}
|
|
11799
11873
|
}
|
|
11800
|
-
|
|
11874
|
+
let identity = s.paneCurrentCommand;
|
|
11875
|
+
if (identity === "node" && s.panePid) {
|
|
11876
|
+
if (processSnapshot === void 0) {
|
|
11877
|
+
const ps = node_child_process.spawnSync("ps", ["-axo", "pid=,ppid=,args="], { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS });
|
|
11878
|
+
processSnapshot = ps.status === 0 ? ps.stdout : "";
|
|
11879
|
+
}
|
|
11880
|
+
identity = codingAgentFromProcessTree(s.panePid, processSnapshot) ?? identity;
|
|
11881
|
+
}
|
|
11882
|
+
const probe = this.probeAgentState(s.name, identity);
|
|
11801
11883
|
out.push({
|
|
11802
11884
|
id,
|
|
11803
11885
|
title,
|
|
@@ -11809,6 +11891,8 @@ class WebTerminalManager {
|
|
|
11809
11891
|
// simply omit it and web clients fall back to createdAt.
|
|
11810
11892
|
activityAt: s.activity,
|
|
11811
11893
|
agentState: probe.agentState,
|
|
11894
|
+
agentKind: probe.agentKind,
|
|
11895
|
+
agentObservedAt: probe.agentObservedAt,
|
|
11812
11896
|
// Daemon-internal (not pushed to web / not in the signature):
|
|
11813
11897
|
// the mirror reconciler's stricter "claude is really here" gate.
|
|
11814
11898
|
claudeConfident: probe.claudeConfident,
|
|
@@ -11835,16 +11919,17 @@ class WebTerminalManager {
|
|
|
11835
11919
|
if (live) {
|
|
11836
11920
|
try {
|
|
11837
11921
|
const { command, tail } = live.agentProbeInput();
|
|
11838
|
-
|
|
11922
|
+
const observation = classifyAgentPane(command === "node" ? polledCommand || command : command || polledCommand || "", tail);
|
|
11923
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(tail) };
|
|
11839
11924
|
} catch {
|
|
11840
11925
|
}
|
|
11841
11926
|
}
|
|
11842
|
-
return this.probeAgentStateViaTmux(sessionName);
|
|
11927
|
+
return this.probeAgentStateViaTmux(sessionName, polledCommand);
|
|
11843
11928
|
}
|
|
11844
11929
|
/** Fallback probe for sessions with no live headless: 2 short tmux calls
|
|
11845
11930
|
* (foreground command + pane tail) fed into classifyPane. Any failure or
|
|
11846
11931
|
* timeout → undefined (the field is omitted), never an error. */
|
|
11847
|
-
probeAgentStateViaTmux(sessionName) {
|
|
11932
|
+
probeAgentStateViaTmux(sessionName, polledCommand) {
|
|
11848
11933
|
try {
|
|
11849
11934
|
const cmd = node_child_process.spawnSync(
|
|
11850
11935
|
"tmux",
|
|
@@ -11858,7 +11943,8 @@ class WebTerminalManager {
|
|
|
11858
11943
|
{ encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
|
|
11859
11944
|
);
|
|
11860
11945
|
if (cap.status !== 0 || typeof cap.stdout !== "string") return { agentState: void 0, claudeConfident: false };
|
|
11861
|
-
|
|
11946
|
+
const observation = classifyAgentPane(cmd.stdout.trim() === "node" ? polledCommand || "node" : cmd.stdout.trim(), cap.stdout);
|
|
11947
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(cap.stdout) };
|
|
11862
11948
|
} catch {
|
|
11863
11949
|
return { agentState: void 0, claudeConfident: false };
|
|
11864
11950
|
}
|
|
@@ -12994,6 +13080,10 @@ class CodexAppServerClient {
|
|
|
12994
13080
|
if (!item || typeof item !== "object") {
|
|
12995
13081
|
return method.startsWith("item/");
|
|
12996
13082
|
}
|
|
13083
|
+
if ((method === "item/started" || method === "item/completed") && (item.type === "collabAgentToolCall" || item.type === "subAgentActivity")) {
|
|
13084
|
+
this.eventHandler?.({ type: "collab_agent_item", item });
|
|
13085
|
+
return true;
|
|
13086
|
+
}
|
|
12997
13087
|
if (method === "item/started" && item.type === "commandExecution") {
|
|
12998
13088
|
const callId = typeof item.id === "string" ? item.id : "";
|
|
12999
13089
|
this.eventHandler?.({
|
|
@@ -14104,6 +14194,8 @@ class ApiMachineClient {
|
|
|
14104
14194
|
* stale" so the FIRST keep-alive tick always probes and populates the
|
|
14105
14195
|
* machine metadata that a connecting/reconnecting web client reads. */
|
|
14106
14196
|
lastCliProbeAt = Date.now() - CLI_AVAILABILITY_RECHECK_MS;
|
|
14197
|
+
/** Stable across socket reconnects; a new client/run invalidates old version checks. */
|
|
14198
|
+
agentVersionEpoch = Date.now();
|
|
14107
14199
|
cliUpdateState = null;
|
|
14108
14200
|
cliUpdatePushChain = Promise.resolve();
|
|
14109
14201
|
claudeAuthState = null;
|
|
@@ -14864,6 +14956,7 @@ class ApiMachineClient {
|
|
|
14864
14956
|
pid: process.pid,
|
|
14865
14957
|
httpPort: this.machine.daemonState?.httpPort,
|
|
14866
14958
|
startedAt: now,
|
|
14959
|
+
agentVersionEpoch: this.agentVersionEpoch,
|
|
14867
14960
|
webTerminals: { updatedAt: now, terminals: initialTerminals },
|
|
14868
14961
|
// B-265 capability flag; restamped every connect so the
|
|
14869
14962
|
// web's `detectedAt >= startedAt` trust rule holds.
|
|
@@ -16228,6 +16321,7 @@ const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
|
16228
16321
|
}).passthrough()
|
|
16229
16322
|
]);
|
|
16230
16323
|
|
|
16324
|
+
exports.AGENT_VERSION_SOURCES = AGENT_VERSION_SOURCES;
|
|
16231
16325
|
exports.ApiClient = ApiClient;
|
|
16232
16326
|
exports.ApiSessionClient = ApiSessionClient;
|
|
16233
16327
|
exports.AsyncLock = AsyncLock;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "very-happy-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.135",
|
|
4
4
|
"description": "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"contributors": [
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@agentclientprotocol/sdk": "^0.14.1",
|
|
82
|
-
"@anthropic-ai/claude-agent-sdk": "
|
|
82
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.267",
|
|
83
83
|
"@anthropic-ai/sandbox-runtime": "^0.0.37",
|
|
84
84
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
85
85
|
"@noble/ed25519": "^3.0.0",
|
|
@@ -113,12 +113,12 @@
|
|
|
113
113
|
"zod": "^4.0.0"
|
|
114
114
|
},
|
|
115
115
|
"optionalDependencies": {
|
|
116
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
117
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
118
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
119
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
120
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
121
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
116
|
+
"very-happy-tools-arm64-darwin": "0.2.135",
|
|
117
|
+
"very-happy-tools-x64-darwin": "0.2.135",
|
|
118
|
+
"very-happy-tools-arm64-linux": "0.2.135",
|
|
119
|
+
"very-happy-tools-x64-linux": "0.2.135",
|
|
120
|
+
"very-happy-tools-arm64-win32": "0.2.135",
|
|
121
|
+
"very-happy-tools-x64-win32": "0.2.135"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@slopus/happy-wire": "workspace:*",
|