very-happy-cli 0.2.119 → 0.2.121

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 (35) hide show
  1. package/dist/{AcpBackend-DpsLyp2y.mjs → AcpBackend-BxvQ6O4k.mjs} +1 -1
  2. package/dist/{AcpBackend-BpwOAcgu.cjs → AcpBackend-Cdj56ZfU.cjs} +1 -1
  3. package/dist/{AcpSessionManager-Ck6heffK.cjs → AcpSessionManager-DahCU78z.cjs} +51 -11
  4. package/dist/{AcpSessionManager-CM0PcgwO.mjs → AcpSessionManager-DlWlQVhw.mjs} +51 -11
  5. package/dist/{config-CtQJKe8h.cjs → config-DxOhdFEx.cjs} +2 -2
  6. package/dist/{config-DpE-aSHW.mjs → config-osO37dR1.mjs} +2 -2
  7. package/dist/{index-BITXLEZo.mjs → index-BcwAnM9k.mjs} +25 -11
  8. package/dist/{index-CK-yge8m.cjs → index-Bv53Zygv.cjs} +24 -10
  9. package/dist/{index-CRhOD3CQ.cjs → index-DObePs_K.cjs} +176 -54
  10. package/dist/{index-CGpkGfDj.mjs → index-DtZFY8aS.mjs} +2 -2
  11. package/dist/{index-DNXCQz_A.mjs → index-Iz-M3qbz.mjs} +172 -51
  12. package/dist/{index-DY7g3UWq.cjs → index-Vjo1jIJH.cjs} +2 -2
  13. package/dist/index.cjs +2 -2
  14. package/dist/index.mjs +2 -2
  15. package/dist/{installTerminalHooks-Dmr3JUJR.mjs → installTerminalHooks-Bq11vOqF.mjs} +2 -2
  16. package/dist/{installTerminalHooks-BOOg7NNs.cjs → installTerminalHooks-DehsXLmV.cjs} +2 -2
  17. package/dist/lib.cjs +1 -1
  18. package/dist/lib.d.cts +25 -1
  19. package/dist/lib.d.mts +25 -1
  20. package/dist/lib.mjs +1 -1
  21. package/dist/{mcp-D4N7yn98.mjs → mcp-Bx1B3uny.mjs} +2 -2
  22. package/dist/{mcp-CxIk4Mes.cjs → mcp-Cpx9Qlch.cjs} +2 -2
  23. package/dist/{runGemini-Bexbg8pA.mjs → runGemini-CMgudbJs.mjs} +9 -5
  24. package/dist/{runGemini-TbGYRy0V.cjs → runGemini-DI8DipUx.cjs} +9 -5
  25. package/dist/{runOpenClaw-B-TV4EkO.cjs → runOpenClaw-CetdjTl-.cjs} +3 -3
  26. package/dist/{runOpenClaw-BpoIQWbe.mjs → runOpenClaw-DR3fLtVf.mjs} +3 -3
  27. package/dist/{send-U9UJvqNA.cjs → send-Cq0NZCn8.cjs} +2 -2
  28. package/dist/{send-z5SeDtKA.mjs → send-M4b_hD7t.mjs} +2 -2
  29. package/dist/{sessions-DNALvNe8.mjs → sessions-CelkWhqX.mjs} +11 -4
  30. package/dist/{sessions-BAZGALtl.cjs → sessions-DW4lrLOp.cjs} +11 -4
  31. package/dist/{spawn-CsV0SKkw.mjs → spawn-C4fF0q3Z.mjs} +2 -2
  32. package/dist/{spawn-dBxu-RAv.cjs → spawn-D3cwBlWI.cjs} +2 -2
  33. package/dist/{types-Bw_6p8NP.cjs → types-COOYpI8_.cjs} +176 -33
  34. package/dist/{types-DdyNuB7D.mjs → types-P2cJWD0E.mjs} +174 -33
  35. package/package.json +7 -7
@@ -29,7 +29,7 @@ import { SandboxManager } from '@anthropic-ai/sandbox-runtime';
29
29
  import { Expo } from 'expo-server-sdk';
30
30
 
31
31
  var name = "very-happy-cli";
32
- var version = "0.2.119";
32
+ var version = "0.2.121";
33
33
  var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
34
34
  var author = "Kirill Dubovitskiy";
35
35
  var contributors = [
@@ -142,12 +142,12 @@ var dependencies = {
142
142
  zod: "^4.0.0"
143
143
  };
144
144
  var optionalDependencies = {
145
- "very-happy-tools-arm64-darwin": "0.2.119",
146
- "very-happy-tools-x64-darwin": "0.2.119",
147
- "very-happy-tools-arm64-linux": "0.2.119",
148
- "very-happy-tools-x64-linux": "0.2.119",
149
- "very-happy-tools-arm64-win32": "0.2.119",
150
- "very-happy-tools-x64-win32": "0.2.119"
145
+ "very-happy-tools-arm64-darwin": "0.2.121",
146
+ "very-happy-tools-x64-darwin": "0.2.121",
147
+ "very-happy-tools-arm64-linux": "0.2.121",
148
+ "very-happy-tools-x64-linux": "0.2.121",
149
+ "very-happy-tools-arm64-win32": "0.2.121",
150
+ "very-happy-tools-x64-win32": "0.2.121"
151
151
  };
152
152
  var devDependencies = {
153
153
  "@slopus/happy-wire": "workspace:*",
@@ -720,6 +720,11 @@ const sessionProgressEventSchema = z.object({
720
720
  durationMs: z.number().optional(),
721
721
  summary: z.string().optional()
722
722
  });
723
+ const sessionQueueCancelEventSchema = z.object({
724
+ t: z.literal("queue-cancel"),
725
+ targetLocalKeys: z.array(z.string().min(1)).min(1),
726
+ reason: z.string().min(1).optional()
727
+ });
723
728
  const sessionEventSchema = z.discriminatedUnion("t", [
724
729
  sessionTextEventSchema,
725
730
  sessionServiceMessageEventSchema,
@@ -730,7 +735,8 @@ const sessionEventSchema = z.discriminatedUnion("t", [
730
735
  sessionStartEventSchema,
731
736
  sessionTurnEndEventSchema,
732
737
  sessionStopEventSchema,
733
- sessionProgressEventSchema
738
+ sessionProgressEventSchema,
739
+ sessionQueueCancelEventSchema
734
740
  ]);
735
741
  const sessionEnvelopeSchema = z.object({
736
742
  id: z.string(),
@@ -3113,6 +3119,48 @@ class InvalidateSync {
3113
3119
  };
3114
3120
  }
3115
3121
 
3122
+ function sessionEnvelopeOf(body) {
3123
+ if (!body || typeof body !== "object") return null;
3124
+ const record = body;
3125
+ if (record.role !== "session" || !record.content || typeof record.content !== "object") return null;
3126
+ const content = record.content;
3127
+ if (content.type === "session" && content.data && typeof content.data === "object") {
3128
+ return content.data;
3129
+ }
3130
+ if (content.ev !== void 0) return content;
3131
+ return null;
3132
+ }
3133
+ function isQueuedUserText(body) {
3134
+ if (!body || typeof body !== "object") return false;
3135
+ const record = body;
3136
+ return record.role === "user" && record.content?.type === "text" && typeof record.meta?.queuedAt === "number";
3137
+ }
3138
+ function scanUndeliveredQueuedInputs(records, prior) {
3139
+ const canceled = prior?.canceled ?? /* @__PURE__ */ new Set();
3140
+ const undelivered = [];
3141
+ let reachedBoundary = false;
3142
+ for (const record of records) {
3143
+ const envelope = sessionEnvelopeOf(record.body);
3144
+ if (envelope) {
3145
+ const t = envelope.ev?.t;
3146
+ if (t === "turn-end") {
3147
+ reachedBoundary = true;
3148
+ break;
3149
+ }
3150
+ if (t === "queue-cancel" && Array.isArray(envelope.ev?.targetLocalKeys)) {
3151
+ for (const key of envelope.ev.targetLocalKeys) {
3152
+ if (typeof key === "string") canceled.add(key);
3153
+ }
3154
+ }
3155
+ continue;
3156
+ }
3157
+ if (isQueuedUserText(record.body) && record.localId && !canceled.has(record.localId)) {
3158
+ undelivered.push(record.localId);
3159
+ }
3160
+ }
3161
+ return { undeliveredLocalKeys: undelivered, reachedBoundary, canceled };
3162
+ }
3163
+
3116
3164
  function record(value) {
3117
3165
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
3118
3166
  }
@@ -3756,8 +3804,75 @@ class ApiSessionClient extends EventEmitter {
3756
3804
  this.sendSync.invalidate();
3757
3805
  }
3758
3806
  }
3807
+ /** B-332: emit a queue-cancel tombstone, mirroring the web's own signal. */
3808
+ sendQueueCancelReason(localKeys, reason) {
3809
+ if (localKeys.length === 0) return;
3810
+ const envelope = createEnvelope("user", { t: "queue-cancel", targetLocalKeys: localKeys, reason });
3811
+ this.sendSessionProtocolMessage(envelope);
3812
+ }
3759
3813
  /**
3760
- * Send message to session
3814
+ * B-332 site ③: find queued user input this wrapper destroyed (it was still
3815
+ * in the previous wrapper's in-memory queue when it died) and tell the web.
3816
+ *
3817
+ * Walks NEWEST → OLDEST via `before_seq`, decrypting each page, and stops at
3818
+ * the first turn-end. The runner calls this on EVERY reconnect (runClaude /
3819
+ * runCodex), seeded or not: a cursor seeded at the server's latest seq skips
3820
+ * the undelivered tail just as thoroughly as skipExistingMessages does —
3821
+ * and the daemon's resume/restart always seeds, so gating on the skip path
3822
+ * would miss the common case. Best-effort: a failed scan must not take a
3823
+ * session down. Needs no socket; POSTs through the same session-message
3824
+ * channel so the web receives it identically to a cancel-button tombstone.
3825
+ */
3826
+ async cancelUndeliveredQueuedInputs() {
3827
+ const MAX_SCAN_PAGES = 10;
3828
+ const canceled = /* @__PURE__ */ new Set();
3829
+ const undelivered = [];
3830
+ let beforeSeq = 2147483647;
3831
+ let reachedBoundary = false;
3832
+ let pages = 0;
3833
+ try {
3834
+ while (!reachedBoundary && pages < MAX_SCAN_PAGES) {
3835
+ pages += 1;
3836
+ const response = await axios.get(
3837
+ `${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
3838
+ {
3839
+ params: { before_seq: beforeSeq, limit: 100 },
3840
+ headers: this.authHeaders(),
3841
+ timeout: 6e4
3842
+ }
3843
+ );
3844
+ const messages = Array.isArray(response.data.messages) ? response.data.messages : [];
3845
+ if (messages.length === 0) break;
3846
+ const records = messages.map((message) => {
3847
+ let body = null;
3848
+ try {
3849
+ body = message.content?.t === "encrypted" ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(message.content.c)) : null;
3850
+ } catch (error) {
3851
+ logger.debug("[API] Failed to decrypt during undelivered scan:", error);
3852
+ }
3853
+ return { seq: message.seq, localId: message.localId, body };
3854
+ });
3855
+ const scan = scanUndeliveredQueuedInputs(records, { canceled });
3856
+ for (const key of scan.canceled) canceled.add(key);
3857
+ undelivered.push(...scan.undeliveredLocalKeys);
3858
+ reachedBoundary = scan.reachedBoundary;
3859
+ const oldest = records.at(-1)?.seq;
3860
+ if (oldest === void 0 || messages.length < 100) break;
3861
+ beforeSeq = oldest;
3862
+ }
3863
+ } catch (error) {
3864
+ logger.debug("[API] cancelUndeliveredQueuedInputs scan failed:", error);
3865
+ return;
3866
+ }
3867
+ if (!reachedBoundary) {
3868
+ logger.debug("[API] cancelUndeliveredQueuedInputs found no turn boundary; leaving messages as-is");
3869
+ return;
3870
+ }
3871
+ undelivered.reverse();
3872
+ this.sendQueueCancelReason(undelivered, "restarted");
3873
+ }
3874
+ /**
3875
+ * Send a message to session
3761
3876
  * @param body - Message body (can be MessageContent or raw content for agent messages)
3762
3877
  */
3763
3878
  sendClaudeSessionMessage(body, opts) {
@@ -4450,34 +4565,44 @@ function migrateSettings(raw, fromVersion) {
4450
4565
  let migrated = { ...raw };
4451
4566
  return migrated;
4452
4567
  }
4453
- async function readSettings() {
4568
+ async function readSettingsOutcome() {
4454
4569
  if (!existsSync(configuration.settingsFile)) {
4455
- return { ...defaultSettings };
4570
+ return { kind: "absent" };
4456
4571
  }
4457
4572
  try {
4458
- const content = await readFile$1(configuration.settingsFile, "utf8");
4459
- const raw = JSON.parse(content);
4460
- const schemaVersion = raw.schemaVersion ?? 1;
4461
- if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
4462
- logger.warn(
4463
- `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
4464
- );
4465
- }
4466
- const migrated = migrateSettings(raw, schemaVersion);
4467
- if (migrated.sandboxConfig !== void 0) {
4468
- try {
4469
- migrated.sandboxConfig = SandboxConfigSchema.parse(migrated.sandboxConfig);
4470
- } catch (error) {
4471
- logger.warn(`\u26A0\uFE0F Invalid sandbox config - skipping. Error: ${error.message}`);
4472
- migrated.sandboxConfig = void 0;
4473
- }
4474
- }
4475
- return { ...defaultSettings, ...migrated };
4573
+ return { kind: "ok", settings: await parseSettingsFile() };
4476
4574
  } catch (error) {
4477
- logger.warn(`Failed to read settings: ${error.message}`);
4478
- return { ...defaultSettings };
4575
+ return { kind: "unreadable", error: error?.message ?? String(error) };
4479
4576
  }
4480
4577
  }
4578
+ async function readSettings() {
4579
+ const outcome = await readSettingsOutcome();
4580
+ if (outcome.kind === "ok") return outcome.settings;
4581
+ if (outcome.kind === "unreadable") {
4582
+ logger.warn(`Failed to read settings: ${outcome.error}`);
4583
+ }
4584
+ return { ...defaultSettings };
4585
+ }
4586
+ async function parseSettingsFile() {
4587
+ const content = await readFile$1(configuration.settingsFile, "utf8");
4588
+ const raw = JSON.parse(content);
4589
+ const schemaVersion = raw.schemaVersion ?? 1;
4590
+ if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
4591
+ logger.warn(
4592
+ `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
4593
+ );
4594
+ }
4595
+ const migrated = migrateSettings(raw);
4596
+ if (migrated.sandboxConfig !== void 0) {
4597
+ try {
4598
+ migrated.sandboxConfig = SandboxConfigSchema.parse(migrated.sandboxConfig);
4599
+ } catch (error) {
4600
+ logger.warn(`\u26A0\uFE0F Invalid sandbox config - skipping. Error: ${error.message}`);
4601
+ migrated.sandboxConfig = void 0;
4602
+ }
4603
+ }
4604
+ return { ...defaultSettings, ...migrated };
4605
+ }
4481
4606
  async function writeSettings(settings) {
4482
4607
  await ensurePrivateDirectory(configuration.happyHomeDir);
4483
4608
  const settingsWithVersion = {
@@ -4519,7 +4644,13 @@ async function updateSettings(updater) {
4519
4644
  throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
4520
4645
  }
4521
4646
  try {
4522
- const current = await readSettings() || { ...defaultSettings };
4647
+ const outcome = await readSettingsOutcome();
4648
+ if (outcome.kind === "unreadable") {
4649
+ throw new Error(
4650
+ `Settings file ${configuration.settingsFile} exists but could not be read (${outcome.error}). Refusing to overwrite it \u2014 fix or remove the file (a removed file is recreated with defaults).`
4651
+ );
4652
+ }
4653
+ const current = outcome.kind === "ok" ? outcome.settings : { ...defaultSettings };
4523
4654
  const updated = await updater(current);
4524
4655
  await ensurePrivateDirectory(configuration.happyHomeDir);
4525
4656
  await writePrivateFile(tmpFile, JSON.stringify(updated, null, 2));
@@ -4766,6 +4897,7 @@ var persistence = /*#__PURE__*/Object.freeze({
4766
4897
  readDaemonState: readDaemonState,
4767
4898
  readPersistedSessions: readPersistedSessions,
4768
4899
  readSettings: readSettings,
4900
+ readSettingsOutcome: readSettingsOutcome,
4769
4901
  readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
4770
4902
  releaseDaemonLock: releaseDaemonLock,
4771
4903
  updateSettings: updateSettings,
@@ -14513,6 +14645,7 @@ async function withCodexAppServerClient(handler) {
14513
14645
  await client.disconnect();
14514
14646
  }
14515
14647
  }
14648
+ const CLI_AVAILABILITY_RECHECK_MS = 5 * 60 * 1e3;
14516
14649
  class ApiMachineClient {
14517
14650
  constructor(token, machine) {
14518
14651
  this.token = token;
@@ -14536,6 +14669,10 @@ class ApiMachineClient {
14536
14669
  keepAliveInterval = null;
14537
14670
  lastKnownCLIAvailability = null;
14538
14671
  lastKnownResumeSupport = null;
14672
+ /** Wall-clock of the last CLI availability probe. Initialised as "already
14673
+ * stale" so the FIRST keep-alive tick always probes and populates the
14674
+ * machine metadata that a connecting/reconnecting web client reads. */
14675
+ lastCliProbeAt = Date.now() - CLI_AVAILABILITY_RECHECK_MS;
14539
14676
  cliUpdateState = null;
14540
14677
  cliUpdatePushChain = Promise.resolve();
14541
14678
  claudeAuthState = null;
@@ -15403,6 +15540,10 @@ class ApiMachineClient {
15403
15540
  }
15404
15541
  this.socket.emit("machine-alive", payload);
15405
15542
  void this.refreshRelayConnection();
15543
+ if (Date.now() - this.lastCliProbeAt < CLI_AVAILABILITY_RECHECK_MS) {
15544
+ return;
15545
+ }
15546
+ this.lastCliProbeAt = Date.now();
15406
15547
  const newAvailability = detectCLIAvailability();
15407
15548
  const prev = this.lastKnownCLIAvailability;
15408
15549
  const newResumeSupport = detectResumeSupport();
@@ -16630,4 +16771,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
16630
16771
  }).passthrough()
16631
16772
  ]);
16632
16773
 
16633
- export { MIRROR_BACKFILL_LINES_DEFAULT as $, ApiClient as A, readCredentials as B, credentialRelayProblem as C, deriveLocalCliUpdateSummary as D, updateSettings as E, encodeBase64Url as F, decodeBase64 as G, writeCredentialsLegacy as H, InvalidateSync as I, writeCredentialsDataKey as J, readCredentialsForConfiguredRelay as K, encrypt as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, isClaudeSessionId as N, getLocalHappyAgentCredentialPath as O, readLocalHappyAgentCredentials as P, decryptWithDataKey as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, decryptLegacy as T, compareExactVersions as U, extractCompleteLines as V, decideBackfill as W, parseTerminalHookPayload as X, tmuxArgs as Y, scrubTmuxClientEnv as Z, decideMirrorBinding as _, ApiSessionClient as a, persistSession as a0, mirrorLineKey as a1, mirrorLocalId as a2, detectCLIAvailability as a3, detectResumeSupport as a4, acquireDaemonLock as a5, summarizeSpawnSessionForLog as a6, deletePersistedSessions as a7, shellescape as a8, decrypt as a9, writeDaemonState as aa, writeSettings as ab, fetchCliUpdateState as ac, resolveCliUpdateCheckInterval as ad, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as ae, releaseDaemonLock as af, listVhTerminals as ag, isValidTerminalId as ah, readVhTerminal as ai, sendToVhTerminal as aj, TITLE_PROMPT_PREFIX as ak, BOARD_ANALYZER_PROMPT_PREFIX as al, startOfflineReconnection as am, clearCredentials as an, clearMachineId as ao, SandboxConfigSchema as ap, CodexAppServerClient as aq, getLatestDaemonLog as ar, persistence as as, api as at, createEnvelope as b, configuration as c, connectionState as d, readSettings as e, encodeBase64 as f, contentLogMetadata as g, errorLogMetadata as h, isValidSessionId as i, packageJson as j, delay as k, logger as l, ensurePrivateDirectorySync as m, hardenPrivateFileSync as n, readDaemonState as o, projectPath as p, clearDaemonState as q, readPersistedSessions as r, getProjectPath as s, initializeSandbox as t, wrapCommand as u, AsyncLock as v, writePrivateFileSync as w, ensurePrivateDirectory as x, hardenPrivateFile as y, commandOnPath as z };
16774
+ export { scrubTmuxClientEnv as $, ApiClient as A, commandOnPath as B, readCredentials as C, credentialRelayProblem as D, deriveLocalCliUpdateSummary as E, readSettingsOutcome as F, updateSettings as G, encodeBase64Url as H, InvalidateSync as I, decodeBase64 as J, writeCredentialsLegacy as K, writeCredentialsDataKey as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readCredentialsForConfiguredRelay as N, encrypt as O, isClaudeSessionId as P, getLocalHappyAgentCredentialPath as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, readLocalHappyAgentCredentials as T, decryptWithDataKey as U, decryptLegacy as V, compareExactVersions as W, extractCompleteLines as X, decideBackfill as Y, parseTerminalHookPayload as Z, tmuxArgs as _, ApiSessionClient as a, decideMirrorBinding as a0, MIRROR_BACKFILL_LINES_DEFAULT as a1, persistSession as a2, mirrorLineKey as a3, mirrorLocalId as a4, detectCLIAvailability as a5, detectResumeSupport as a6, acquireDaemonLock as a7, summarizeSpawnSessionForLog as a8, deletePersistedSessions as a9, shellescape as aa, decrypt as ab, writeDaemonState as ac, writeSettings as ad, fetchCliUpdateState as ae, resolveCliUpdateCheckInterval as af, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as ag, releaseDaemonLock as ah, listVhTerminals as ai, isValidTerminalId as aj, readVhTerminal as ak, sendToVhTerminal as al, TITLE_PROMPT_PREFIX as am, BOARD_ANALYZER_PROMPT_PREFIX as an, startOfflineReconnection as ao, clearCredentials as ap, clearMachineId as aq, SandboxConfigSchema as ar, CodexAppServerClient as as, getLatestDaemonLog as at, persistence as au, api as av, createEnvelope as b, configuration as c, connectionState as d, readSettings as e, encodeBase64 as f, contentLogMetadata as g, errorLogMetadata as h, isValidSessionId as i, packageJson as j, delay as k, logger as l, ensurePrivateDirectorySync as m, hardenPrivateFileSync as n, readDaemonState as o, projectPath as p, clearDaemonState as q, readPersistedSessions as r, streamKeyOf as s, getProjectPath as t, initializeSandbox as u, wrapCommand as v, writePrivateFileSync as w, AsyncLock as x, ensurePrivateDirectory as y, hardenPrivateFile as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.119",
3
+ "version": "0.2.121",
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.119",
117
- "very-happy-tools-x64-darwin": "0.2.119",
118
- "very-happy-tools-arm64-linux": "0.2.119",
119
- "very-happy-tools-x64-linux": "0.2.119",
120
- "very-happy-tools-arm64-win32": "0.2.119",
121
- "very-happy-tools-x64-win32": "0.2.119"
116
+ "very-happy-tools-arm64-darwin": "0.2.121",
117
+ "very-happy-tools-x64-darwin": "0.2.121",
118
+ "very-happy-tools-arm64-linux": "0.2.121",
119
+ "very-happy-tools-x64-linux": "0.2.121",
120
+ "very-happy-tools-arm64-win32": "0.2.121",
121
+ "very-happy-tools-x64-win32": "0.2.121"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@slopus/happy-wire": "workspace:*",