very-happy-cli 0.2.77 → 0.2.79

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 (31) hide show
  1. package/dist/{AcpBackend-CsLnh1So.cjs → AcpBackend-BuxJntPo.cjs} +1 -1
  2. package/dist/{AcpBackend-BFFC77vV.mjs → AcpBackend-DPcRh_3n.mjs} +1 -1
  3. package/dist/{AcpSessionManager--CixPN-p.cjs → AcpSessionManager-BOZPQZUQ.cjs} +1 -1
  4. package/dist/{AcpSessionManager-CU5vsxgF.mjs → AcpSessionManager-DVGWPtBC.mjs} +1 -1
  5. package/dist/{config-pv17N6Kg.cjs → config-DB0v5eoc.cjs} +2 -2
  6. package/dist/{config-CRFTYuTN.mjs → config-T7eM_8eT.mjs} +2 -2
  7. package/dist/{index-Dcv1RKe8.cjs → index-BGvGqzc1.cjs} +252 -73
  8. package/dist/{index-BkvUwyIU.mjs → index-CuLe9SB7.mjs} +5 -5
  9. package/dist/{index-C1OnwMx0.cjs → index-D_ZjCYNI.cjs} +4 -4
  10. package/dist/{index-DOb57EGb.mjs → index-HYOkniru.mjs} +249 -70
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.mjs +2 -2
  13. package/dist/{installTerminalHooks-YyT3fv6w.cjs → installTerminalHooks-CAViJyYh.cjs} +2 -2
  14. package/dist/{installTerminalHooks-IXvZMGJq.mjs → installTerminalHooks-CPKThY28.mjs} +2 -2
  15. package/dist/lib.cjs +1 -1
  16. package/dist/lib.d.cts +11 -2
  17. package/dist/lib.d.mts +11 -2
  18. package/dist/lib.mjs +1 -1
  19. package/dist/{mcp-CsKnmeE5.cjs → mcp-DhQcmKO7.cjs} +2 -2
  20. package/dist/{mcp-B-GdhlKD.mjs → mcp-DhUA22wj.mjs} +2 -2
  21. package/dist/{runGemini-D0lddF43.mjs → runGemini-BVxviS8T.mjs} +4 -4
  22. package/dist/{runGemini-DuUdmlVZ.cjs → runGemini-CfWCL9K7.cjs} +4 -4
  23. package/dist/{runOpenClaw-BiwwYnYf.mjs → runOpenClaw-DVTaZJtj.mjs} +3 -3
  24. package/dist/{runOpenClaw-DEMCYSoD.cjs → runOpenClaw-kSy7Os3C.cjs} +3 -3
  25. package/dist/{send-DM8oqJFp.mjs → send-DjjJQXJt.mjs} +2 -2
  26. package/dist/{send-DijSXAMp.cjs → send-jzwPToBN.cjs} +2 -2
  27. package/dist/{spawn-nISXbZD5.mjs → spawn-DBpgsEkS.mjs} +2 -2
  28. package/dist/{spawn-De3b5Hsu.cjs → spawn-DFnYIiKP.cjs} +2 -2
  29. package/dist/{types-D2qSKjsO.mjs → types-BRDN6zN2.mjs} +92 -14
  30. package/dist/{types-NwtcBgfS.cjs → types-Dy7P_1bh.cjs} +93 -15
  31. package/package.json +7 -7
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
51
51
  var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
52
52
 
53
53
  var name = "very-happy-cli";
54
- var version = "0.2.77";
54
+ var version = "0.2.79";
55
55
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
56
56
  var author = "Kirill Dubovitskiy";
57
57
  var contributors = [
@@ -164,12 +164,12 @@ var dependencies = {
164
164
  zod: "^4.0.0"
165
165
  };
166
166
  var optionalDependencies = {
167
- "very-happy-tools-arm64-darwin": "0.2.77",
168
- "very-happy-tools-x64-darwin": "0.2.77",
169
- "very-happy-tools-arm64-linux": "0.2.77",
170
- "very-happy-tools-x64-linux": "0.2.77",
171
- "very-happy-tools-arm64-win32": "0.2.77",
172
- "very-happy-tools-x64-win32": "0.2.77"
167
+ "very-happy-tools-arm64-darwin": "0.2.79",
168
+ "very-happy-tools-x64-darwin": "0.2.79",
169
+ "very-happy-tools-arm64-linux": "0.2.79",
170
+ "very-happy-tools-x64-linux": "0.2.79",
171
+ "very-happy-tools-arm64-win32": "0.2.79",
172
+ "very-happy-tools-x64-win32": "0.2.79"
173
173
  };
174
174
  var devDependencies = {
175
175
  "@slopus/happy-wire": "workspace:*",
@@ -694,6 +694,9 @@ const sessionTurnUsageSchema = z__namespace.object({
694
694
  const sessionTurnEndEventSchema = z__namespace.object({
695
695
  t: z__namespace.literal("turn-end"),
696
696
  status: sessionTurnEndStatusSchema,
697
+ // Human-readable SDK failure detail. Optional for backward compatibility;
698
+ // successful and cancelled turns normally omit it.
699
+ error: z__namespace.string().optional(),
697
700
  // Optional per-turn metadata sourced from the SDK result message. Present
698
701
  // only on turns ended by a result event; absent on lazily-closed turns.
699
702
  costUsd: z__namespace.number().optional(),
@@ -960,6 +963,9 @@ const CliUpdateStateSchema = z.z.object({
960
963
  const WebTerminalListItemSchema = z.z.object({
961
964
  id: z.z.string(),
962
965
  title: z.z.string().optional(),
966
+ /** New daemons always send an array (including []); absence identifies an
967
+ * old daemon that cannot persist terminal tags. */
968
+ tags: z.z.array(z.z.string()).optional(),
963
969
  cwd: z.z.string().optional(),
964
970
  createdAt: z.z.number().optional(),
965
971
  activityAt: z.z.number().optional(),
@@ -1474,7 +1480,7 @@ class RpcHandlerManager {
1474
1480
  }
1475
1481
  }
1476
1482
 
1477
- const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-NwtcBgfS.cjs', document.baseURI).href))));
1483
+ const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-Dy7P_1bh.cjs', document.baseURI).href))));
1478
1484
  function projectPath() {
1479
1485
  const path = path$1.resolve(__dirname$1, "..");
1480
1486
  return path;
@@ -2466,12 +2472,14 @@ function closeTurn(state, status, envelopes, meta) {
2466
2472
  envelopes.push(createEnvelope("agent", {
2467
2473
  t: "turn-end",
2468
2474
  status,
2475
+ ...meta?.error ? { error: meta.error } : {},
2469
2476
  ...typeof meta?.costUsd === "number" ? { costUsd: meta.costUsd } : {},
2470
2477
  ...typeof meta?.durationMs === "number" ? { durationMs: meta.durationMs } : {},
2471
2478
  ...typeof meta?.numTurns === "number" ? { numTurns: meta.numTurns } : {},
2472
2479
  ...meta?.usage ? { usage: meta.usage } : {}
2473
2480
  }, { turn: state.currentTurnId }));
2474
2481
  state.currentTurnId = null;
2482
+ state.pendingAssistantError = void 0;
2475
2483
  clearSubagentTracking(state);
2476
2484
  }
2477
2485
  function toolTitle(name, input) {
@@ -2492,9 +2500,9 @@ function toToolArgs(input) {
2492
2500
  }
2493
2501
  return { input };
2494
2502
  }
2495
- function closeClaudeTurnWithStatus(state, status) {
2503
+ function closeClaudeTurnWithStatus(state, status, meta) {
2496
2504
  const envelopes = [];
2497
- closeTurn(state, status, envelopes);
2505
+ closeTurn(state, status, envelopes, meta);
2498
2506
  return {
2499
2507
  currentTurnId: state.currentTurnId,
2500
2508
  envelopes
@@ -2535,6 +2543,10 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
2535
2543
  };
2536
2544
  }
2537
2545
  if (message.type === "assistant") {
2546
+ const assistantError = message.error;
2547
+ if (!message.isSidechain && typeof assistantError === "string" && assistantError.trim()) {
2548
+ state.pendingAssistantError = assistantError.trim();
2549
+ }
2538
2550
  const turnId = ensureTurn(state, envelopes);
2539
2551
  maybeEmitSubagentStart(state, turnId, subagent, envelopes);
2540
2552
  const usage = pickAssistantUsage(message);
@@ -2656,6 +2668,10 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
2656
2668
  if (message.type === "result") {
2657
2669
  const raw = message;
2658
2670
  const status = raw.is_error === true ? "failed" : "completed";
2671
+ const error = status === "failed" ? raw.errors?.filter((item) => typeof item === "string" && item.trim()).join("\n") || state.pendingAssistantError || raw.subtype || "Claude turn failed" : void 0;
2672
+ if (status === "failed" && !state.currentTurnId) {
2673
+ ensureTurn(state, envelopes);
2674
+ }
2659
2675
  const usage = raw.usage && typeof raw.usage.input_tokens === "number" && typeof raw.usage.output_tokens === "number" ? {
2660
2676
  input_tokens: raw.usage.input_tokens,
2661
2677
  output_tokens: raw.usage.output_tokens,
@@ -2663,6 +2679,7 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
2663
2679
  ...typeof raw.usage.cache_read_input_tokens === "number" ? { cache_read_input_tokens: raw.usage.cache_read_input_tokens } : {}
2664
2680
  } : void 0;
2665
2681
  closeTurn(state, status, envelopes, {
2682
+ error,
2666
2683
  costUsd: typeof raw.total_cost_usd === "number" ? raw.total_cost_usd : void 0,
2667
2684
  durationMs: typeof raw.duration_ms === "number" ? raw.duration_ms : void 0,
2668
2685
  numTurns: typeof raw.num_turns === "number" ? raw.num_turns : void 0,
@@ -3398,9 +3415,9 @@ class ApiSessionClient extends node_events.EventEmitter {
3398
3415
  }));
3399
3416
  }
3400
3417
  }
3401
- closeClaudeSessionTurn(status = "completed") {
3418
+ closeClaudeSessionTurn(status = "completed", meta) {
3402
3419
  this.emit("turn-ended", status);
3403
- const mapped = closeClaudeTurnWithStatus(this.claudeSessionProtocolState, status);
3420
+ const mapped = closeClaudeTurnWithStatus(this.claudeSessionProtocolState, status, meta);
3404
3421
  this.claudeSessionProtocolState.currentTurnId = mapped.currentTurnId;
3405
3422
  for (const envelope of mapped.envelopes) {
3406
3423
  this.sendSessionProtocolMessage(envelope);
@@ -9808,6 +9825,7 @@ function terminalListSignature(items) {
9808
9825
  const canon = [...items].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).map((t) => [
9809
9826
  t.id,
9810
9827
  t.title ?? "",
9828
+ t.tags ?? [],
9811
9829
  t.cwd ?? "",
9812
9830
  t.createdAt ?? 0,
9813
9831
  Math.floor((t.activityAt ?? 0) / ACTIVITY_SIGNATURE_BUCKET_MS),
@@ -9841,6 +9859,7 @@ const LIST_SESSIONS_FORMAT = [
9841
9859
  "#{pane_current_path}",
9842
9860
  "#{@vh_title}",
9843
9861
  "#{@vh_title_manual}",
9862
+ "#{@vh_tags}",
9844
9863
  // B-121: the control-mode client has no `pty.process`, so the agent-state
9845
9864
  // fast path lost its live `#{pane_current_command}` equivalent. Carry it in
9846
9865
  // the ONE list-sessions call the tracker already makes — the value goes
@@ -9855,8 +9874,8 @@ const LIST_SESSIONS_FORMAT = [
9855
9874
  function parseSessionListLine(line) {
9856
9875
  if (!line) return void 0;
9857
9876
  const parts = line.split(LIST_FIELD_SEP);
9858
- if (parts.length < 8) return void 0;
9859
- const [name, created, activity, cwd, vhTitle, manual, paneCommand] = parts;
9877
+ if (parts.length < 9) return void 0;
9878
+ const [name, created, activity, cwd, vhTitle, manual, vhTags, paneCommand] = parts;
9860
9879
  if (!name) return void 0;
9861
9880
  return {
9862
9881
  name,
@@ -9865,12 +9884,40 @@ function parseSessionListLine(line) {
9865
9884
  cwd: cwd || void 0,
9866
9885
  vhTitle: vhTitle.trim() || void 0,
9867
9886
  manual: manual.trim().length > 0,
9887
+ tags: parseTerminalTags(vhTags),
9868
9888
  paneCurrentCommand: paneCommand.trim() || void 0,
9869
9889
  // pane_title is last, so anything after field 8 is title content that
9870
9890
  // contained the separator — rejoin it rather than dropping it.
9871
- paneTitle: parts.slice(7).join(LIST_FIELD_SEP) || void 0
9891
+ paneTitle: parts.slice(8).join(LIST_FIELD_SEP) || void 0
9872
9892
  };
9873
9893
  }
9894
+ const TERMINAL_TAG_MAX_COUNT = 64;
9895
+ const TERMINAL_TAG_MAX_LENGTH = 24;
9896
+ const TERMINAL_TAGS_MAX_BYTES = 4096;
9897
+ function validateTerminalTags(value) {
9898
+ if (!Array.isArray(value) || value.length > TERMINAL_TAG_MAX_COUNT) return void 0;
9899
+ const seen = /* @__PURE__ */ new Set();
9900
+ const out = [];
9901
+ for (const valueTag of value) {
9902
+ if (typeof valueTag !== "string") return void 0;
9903
+ const tag = valueTag.trim().replace(/^#+/, "").trim().replace(/\s+/g, "-").slice(0, TERMINAL_TAG_MAX_LENGTH);
9904
+ if (!tag || tag !== valueTag) return void 0;
9905
+ const key = tag.toLowerCase();
9906
+ if (seen.has(key)) return void 0;
9907
+ seen.add(key);
9908
+ out.push(tag);
9909
+ }
9910
+ if (Buffer.byteLength(JSON.stringify(out), "utf8") > TERMINAL_TAGS_MAX_BYTES) return void 0;
9911
+ return out;
9912
+ }
9913
+ function parseTerminalTags(raw) {
9914
+ if (typeof raw !== "string" || !raw.trim()) return [];
9915
+ try {
9916
+ return validateTerminalTags(JSON.parse(raw)) ?? [];
9917
+ } catch {
9918
+ return [];
9919
+ }
9920
+ }
9874
9921
  function classifyPane(currentCommand, tail) {
9875
9922
  const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
9876
9923
  const isShell = SHELL_COMMANDS.has(cmd);
@@ -11303,6 +11350,8 @@ class WebTerminalManager {
11303
11350
  out.push({
11304
11351
  id,
11305
11352
  title,
11353
+ // Always include [] so Web can feature-detect tag support.
11354
+ tags: s.tags,
11306
11355
  cwd: s.cwd,
11307
11356
  createdAt: s.created,
11308
11357
  // tmux last-activity (epoch s) → ms; optional so old daemons
@@ -11480,6 +11529,25 @@ class WebTerminalManager {
11480
11529
  return false;
11481
11530
  }
11482
11531
  }
11532
+ /** Persist terminal tags in tmux, parallel to @vh_title. */
11533
+ setTags(terminalId, tags) {
11534
+ if (!isTmuxAvailable()) return false;
11535
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(terminalId)) return false;
11536
+ const valid = validateTerminalTags(tags);
11537
+ if (!valid) return false;
11538
+ const name = `vh-${terminalId}`;
11539
+ try {
11540
+ const r = node_child_process.spawnSync("tmux", ["set-option", "-t", name, "@vh_tags", JSON.stringify(valid)], {
11541
+ stdio: "ignore",
11542
+ env: ptyEnv()
11543
+ });
11544
+ if (r.status !== 0) return false;
11545
+ this.kickListRefresh();
11546
+ return true;
11547
+ } catch {
11548
+ return false;
11549
+ }
11550
+ }
11483
11551
  }
11484
11552
 
11485
11553
  const TMUX_TIMEOUT_MS = 3e3;
@@ -13428,6 +13496,16 @@ class ApiMachineClient {
13428
13496
  }
13429
13497
  return { type: "success" };
13430
13498
  });
13499
+ this.rpcHandlerManager.registerHandler("set-terminal-tags", async (params) => {
13500
+ const { terminalId, tags } = params || {};
13501
+ if (!terminalId || !Array.isArray(tags)) {
13502
+ throw new Error("terminalId and tags are required");
13503
+ }
13504
+ if (!this.webTerminal.setTags(terminalId, tags)) {
13505
+ throw new Error("Failed to set terminal tags (invalid tags, tmux unavailable, or session gone)");
13506
+ }
13507
+ return { type: "success" };
13508
+ });
13431
13509
  this.rpcHandlerManager.registerHandler("stop-session", (params) => {
13432
13510
  const { sessionId } = params || {};
13433
13511
  if (!sessionId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.77",
3
+ "version": "0.2.79",
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": [
@@ -113,12 +113,12 @@
113
113
  "zod": "^4.0.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "very-happy-tools-arm64-darwin": "0.2.77",
117
- "very-happy-tools-x64-darwin": "0.2.77",
118
- "very-happy-tools-arm64-linux": "0.2.77",
119
- "very-happy-tools-x64-linux": "0.2.77",
120
- "very-happy-tools-arm64-win32": "0.2.77",
121
- "very-happy-tools-x64-win32": "0.2.77"
116
+ "very-happy-tools-arm64-darwin": "0.2.79",
117
+ "very-happy-tools-x64-darwin": "0.2.79",
118
+ "very-happy-tools-arm64-linux": "0.2.79",
119
+ "very-happy-tools-x64-linux": "0.2.79",
120
+ "very-happy-tools-arm64-win32": "0.2.79",
121
+ "very-happy-tools-x64-win32": "0.2.79"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@slopus/happy-wire": "workspace:*",