@hydra-acp/cli 0.1.8 → 0.1.10

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/index.d.ts CHANGED
@@ -1245,11 +1245,13 @@ interface JsonRpcError {
1245
1245
  data?: unknown;
1246
1246
  }
1247
1247
  type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
1248
- declare const HistoryPolicy: z.ZodEnum<["full", "pending_only", "none"]>;
1248
+ declare const HistoryPolicy: z.ZodEnum<["full", "pending_only", "none", "after_message"]>;
1249
1249
  type HistoryPolicy = z.infer<typeof HistoryPolicy>;
1250
1250
  declare const SessionAttachParams: z.ZodObject<{
1251
1251
  sessionId: z.ZodString;
1252
- historyPolicy: z.ZodDefault<z.ZodEnum<["full", "pending_only", "none"]>>;
1252
+ historyPolicy: z.ZodDefault<z.ZodEnum<["full", "pending_only", "none", "after_message"]>>;
1253
+ afterMessageId: z.ZodOptional<z.ZodString>;
1254
+ clientId: z.ZodOptional<z.ZodString>;
1253
1255
  clientInfo: z.ZodOptional<z.ZodObject<{
1254
1256
  name: z.ZodString;
1255
1257
  version: z.ZodOptional<z.ZodString>;
@@ -1263,11 +1265,13 @@ declare const SessionAttachParams: z.ZodObject<{
1263
1265
  _meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1264
1266
  }, "strip", z.ZodTypeAny, {
1265
1267
  sessionId: string;
1266
- historyPolicy: "full" | "pending_only" | "none";
1268
+ historyPolicy: "full" | "pending_only" | "none" | "after_message";
1267
1269
  clientInfo?: {
1268
1270
  name: string;
1269
1271
  version?: string | undefined;
1270
1272
  } | undefined;
1273
+ afterMessageId?: string | undefined;
1274
+ clientId?: string | undefined;
1271
1275
  _meta?: Record<string, unknown> | undefined;
1272
1276
  }, {
1273
1277
  sessionId: string;
@@ -1275,7 +1279,9 @@ declare const SessionAttachParams: z.ZodObject<{
1275
1279
  name: string;
1276
1280
  version?: string | undefined;
1277
1281
  } | undefined;
1278
- historyPolicy?: "full" | "pending_only" | "none" | undefined;
1282
+ historyPolicy?: "full" | "pending_only" | "none" | "after_message" | undefined;
1283
+ afterMessageId?: string | undefined;
1284
+ clientId?: string | undefined;
1279
1285
  _meta?: Record<string, unknown> | undefined;
1280
1286
  }>;
1281
1287
  type SessionAttachParams = z.infer<typeof SessionAttachParams>;
@@ -1691,12 +1697,24 @@ declare class Session {
1691
1697
  upstreamSessionId: string;
1692
1698
  }) => void): void;
1693
1699
  get attachedCount(): number;
1700
+ connectedClients(excludeClientId?: string): Array<{
1701
+ clientId: string;
1702
+ name?: string;
1703
+ version?: string;
1704
+ }>;
1694
1705
  get turnStartedAt(): number | undefined;
1695
1706
  getHistorySnapshot(): Promise<CachedNotification[]>;
1696
1707
  onBroadcast(handler: (entry: CachedNotification) => void): () => void;
1697
- attach(client: AttachedClient, historyPolicy: HistoryPolicy): Promise<CachedNotification[]>;
1708
+ attach(client: AttachedClient, historyPolicy: HistoryPolicy, opts?: {
1709
+ afterMessageId?: string;
1710
+ }): Promise<{
1711
+ entries: CachedNotification[];
1712
+ appliedPolicy: HistoryPolicy;
1713
+ }>;
1714
+ private loadReplay;
1698
1715
  replayPendingPermissions(client: AttachedClient): void;
1699
1716
  detach(clientId: string): void;
1717
+ private broadcastClientDisconnected;
1700
1718
  prompt(clientId: string, params: unknown): Promise<unknown>;
1701
1719
  private broadcastPromptReceived;
1702
1720
  private broadcastTurnComplete;
package/dist/index.js CHANGED
@@ -854,11 +854,22 @@ var JsonRpcErrorCodes = {
854
854
  MethodNotFound: -32601,
855
855
  InvalidParams: -32602,
856
856
  InternalError: -32603,
857
+ // -32001…-32003 reserved for RFD #533 attach semantics:
858
+ // -32001 Session not found
859
+ // -32002 Not authorised to attach
860
+ // -32003 Session does not support multi-client attach
861
+ // We emit -32001 (matching); the other two are reserved for spec
862
+ // alignment even though we don't currently emit them (we bearer-auth
863
+ // at WS upgrade time and always support multi-client attach).
857
864
  SessionNotFound: -32001,
858
- PermissionDenied: -32002,
859
- AlreadyAttached: -32003,
865
+ NotAuthorisedToAttach: -32002,
866
+ MultiClientNotSupported: -32003,
860
867
  AgentNotInstalled: -32005,
861
- BundleAlreadyImported: -32010
868
+ // Hydra-internal codes — outside the RFD's reserved range so they
869
+ // can't collide with future spec assignments.
870
+ BundleAlreadyImported: -32010,
871
+ PermissionDenied: -32011,
872
+ AlreadyAttached: -32012
862
873
  };
863
874
  var InitializeParams = z3.object({
864
875
  protocolVersion: z3.number().optional(),
@@ -868,7 +879,12 @@ var InitializeParams = z3.object({
868
879
  version: z3.string().optional()
869
880
  }).optional()
870
881
  });
871
- var HistoryPolicy = z3.enum(["full", "pending_only", "none"]);
882
+ var HistoryPolicy = z3.enum([
883
+ "full",
884
+ "pending_only",
885
+ "none",
886
+ "after_message"
887
+ ]);
872
888
  var SessionNewParams = z3.object({
873
889
  cwd: z3.string(),
874
890
  agentId: z3.string().optional(),
@@ -884,6 +900,18 @@ var SessionResumeHints = z3.object({
884
900
  var SessionAttachParams = z3.object({
885
901
  sessionId: z3.string(),
886
902
  historyPolicy: HistoryPolicy.default("full"),
903
+ // Required when historyPolicy is "after_message"; ignored otherwise.
904
+ // The proxy replays history entries strictly after the entry whose
905
+ // messageId matches this value. If the id isn't found in the buffer,
906
+ // the response.historyPolicy field surfaces "full" so the caller
907
+ // knows we fell back. Per RFD #533.
908
+ afterMessageId: z3.string().optional(),
909
+ // Caller-assigned opaque id (e.g. a UUID). When provided, the proxy
910
+ // echoes it in resolvedBy/sentBy and lifecycle events so other
911
+ // clients can disambiguate multiple instances of the same
912
+ // clientInfo.name. When omitted, the proxy assigns one and returns
913
+ // it in the response. Per RFD #533.
914
+ clientId: z3.string().optional(),
887
915
  clientInfo: z3.object({
888
916
  name: z3.string(),
889
917
  version: z3.string().optional()
@@ -1385,6 +1413,9 @@ function hydraCommandsAsAdvertised() {
1385
1413
  var HYDRA_ID_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1386
1414
  var generateHydraId = customAlphabet(HYDRA_ID_ALPHABET, 16);
1387
1415
  var HYDRA_SESSION_PREFIX = "hydra_session_";
1416
+ function generateMessageId() {
1417
+ return `m_${generateHydraId()}`;
1418
+ }
1388
1419
  var DEFAULT_HISTORY_MAX_ENTRIES = 1e3;
1389
1420
  var Session = class {
1390
1421
  sessionId;
@@ -1559,6 +1590,30 @@ var Session = class {
1559
1590
  get attachedCount() {
1560
1591
  return this.clients.size;
1561
1592
  }
1593
+ // Roster of currently-attached clients, optionally excluding one
1594
+ // clientId. Used by the daemon to populate connectedClients on the
1595
+ // session/attach response (per RFD #533) — the freshly-attaching
1596
+ // client wants to see who else is on the session but not itself in
1597
+ // the list.
1598
+ connectedClients(excludeClientId) {
1599
+ const out = [];
1600
+ for (const client of this.clients.values()) {
1601
+ if (excludeClientId && client.clientId === excludeClientId) {
1602
+ continue;
1603
+ }
1604
+ const entry = {
1605
+ clientId: client.clientId
1606
+ };
1607
+ if (client.clientInfo?.name) {
1608
+ entry.name = client.clientInfo.name;
1609
+ }
1610
+ if (client.clientInfo?.version) {
1611
+ entry.version = client.clientInfo.version;
1612
+ }
1613
+ out.push(entry);
1614
+ }
1615
+ return out;
1616
+ }
1562
1617
  // Wall-clock when the in-flight agent turn began, or undefined when
1563
1618
  // idle. Tracked in-memory by broadcastPromptReceived/broadcastTurnComplete
1564
1619
  // so the daemon can hand a fresh attacher mid-turn the right elapsed
@@ -1590,10 +1645,12 @@ var Session = class {
1590
1645
  };
1591
1646
  }
1592
1647
  // Register a client and (asynchronously) load the replay slice it
1593
- // should receive. Validation errors throw synchronously so callers
1594
- // can rely on either the registration being in effect or having
1595
- // thrown; the disk-load is the only async work.
1596
- attach(client, historyPolicy) {
1648
+ // should receive. Returns both the slice to replay and the actual
1649
+ // historyPolicy applied (which may differ from the requested one
1650
+ // when after_message falls back to full). Validation errors throw
1651
+ // synchronously so callers can rely on either the registration being
1652
+ // in effect or having thrown; the disk-load is the only async work.
1653
+ attach(client, historyPolicy, opts = {}) {
1597
1654
  if (this.closed) {
1598
1655
  throw withCode(
1599
1656
  new Error("session is closed"),
@@ -1609,9 +1666,20 @@ var Session = class {
1609
1666
  this.clients.set(client.clientId, client);
1610
1667
  this.updatedAt = Date.now();
1611
1668
  if (historyPolicy === "none" || historyPolicy === "pending_only") {
1612
- return Promise.resolve([]);
1669
+ return Promise.resolve({ entries: [], appliedPolicy: historyPolicy });
1670
+ }
1671
+ return this.loadReplay(historyPolicy, opts);
1672
+ }
1673
+ async loadReplay(historyPolicy, opts) {
1674
+ const all = await this.getHistorySnapshot();
1675
+ if (historyPolicy === "after_message") {
1676
+ const cutoff = opts.afterMessageId ? findMessageIdIndex(all, opts.afterMessageId) : -1;
1677
+ if (cutoff < 0) {
1678
+ return { entries: all, appliedPolicy: "full" };
1679
+ }
1680
+ return { entries: all.slice(cutoff + 1), appliedPolicy: "after_message" };
1613
1681
  }
1614
- return this.getHistorySnapshot();
1682
+ return { entries: all, appliedPolicy: "full" };
1615
1683
  }
1616
1684
  // Dispatch in-flight permission requests to a freshly-attached
1617
1685
  // client. Called by the daemon's WS handler *after* it finishes
@@ -1623,8 +1691,39 @@ var Session = class {
1623
1691
  }
1624
1692
  }
1625
1693
  detach(clientId) {
1626
- if (this.clients.delete(clientId)) {
1627
- this.updatedAt = Date.now();
1694
+ const leaving = this.clients.get(clientId);
1695
+ if (!leaving) {
1696
+ return;
1697
+ }
1698
+ this.clients.delete(clientId);
1699
+ this.updatedAt = Date.now();
1700
+ this.broadcastClientDisconnected(leaving);
1701
+ }
1702
+ // Notify remaining attached clients that a peer just left, per
1703
+ // RFD #533. Fires for both explicit session/detach and ws-close
1704
+ // teardown (acp-ws calls Session.detach() in both paths). The
1705
+ // notification is broadcast (not recorded) — peer presence is
1706
+ // transient, not part of conversation history.
1707
+ broadcastClientDisconnected(client) {
1708
+ const info = {
1709
+ clientId: client.clientId
1710
+ };
1711
+ if (client.clientInfo?.name) {
1712
+ info.name = client.clientInfo.name;
1713
+ }
1714
+ if (client.clientInfo?.version) {
1715
+ info.version = client.clientInfo.version;
1716
+ }
1717
+ const update = {
1718
+ sessionUpdate: "client_disconnected",
1719
+ client: info,
1720
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1721
+ };
1722
+ for (const peer of this.clients.values()) {
1723
+ void peer.connection.notify("session/update", {
1724
+ sessionId: this.sessionId,
1725
+ update
1726
+ }).catch(() => void 0);
1628
1727
  }
1629
1728
  }
1630
1729
  async prompt(clientId, params) {
@@ -1677,6 +1776,7 @@ var Session = class {
1677
1776
  sessionId: this.sessionId,
1678
1777
  update: {
1679
1778
  sessionUpdate: "prompt_received",
1779
+ messageId: generateMessageId(),
1680
1780
  prompt: promptParams.prompt,
1681
1781
  sentBy
1682
1782
  }
@@ -1702,7 +1802,8 @@ var Session = class {
1702
1802
  broadcastTurnComplete(originatorClientId, response) {
1703
1803
  const stopReason = response && typeof response === "object" && "stopReason" in response && typeof response.stopReason === "string" ? response.stopReason : void 0;
1704
1804
  const update = {
1705
- sessionUpdate: "turn_complete"
1805
+ sessionUpdate: "turn_complete",
1806
+ messageId: generateMessageId()
1706
1807
  };
1707
1808
  if (stopReason !== void 0) {
1708
1809
  update.stopReason = stopReason;
@@ -2319,10 +2420,11 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
2319
2420
  recordAndBroadcast(method, params, excludeClientId) {
2320
2421
  const rewritten = this.rewriteForClient(params);
2321
2422
  const recordable = !isStateUpdate(method, rewritten);
2423
+ const broadcast = recordable ? ensureMessageIdOnUpdate(method, rewritten) : rewritten;
2322
2424
  if (recordable) {
2323
2425
  const entry = {
2324
2426
  method,
2325
- params: rewritten,
2427
+ params: broadcast,
2326
2428
  recordedAt: Date.now()
2327
2429
  };
2328
2430
  this.lastRecordedAt = entry.recordedAt;
@@ -2350,7 +2452,7 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
2350
2452
  if (excludeClientId && client.clientId === excludeClientId) {
2351
2453
  continue;
2352
2454
  }
2353
- void client.connection.notify(method, rewritten).catch(() => void 0);
2455
+ void client.connection.notify(method, broadcast).catch(() => void 0);
2354
2456
  }
2355
2457
  }
2356
2458
  async handlePermissionRequest(params) {
@@ -2362,11 +2464,13 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
2362
2464
  );
2363
2465
  }
2364
2466
  const clientParams = this.rewriteForClient(params);
2467
+ const toolCallId = extractToolCallId(clientParams);
2365
2468
  return new Promise((resolve3, reject) => {
2366
2469
  let settled = false;
2367
2470
  const outbound = [];
2368
2471
  const entry = { addClient: sendTo };
2369
2472
  this.inFlightPermissions.add(entry);
2473
+ const sessionId = this.sessionId;
2370
2474
  const settle = (fn) => {
2371
2475
  if (settled) {
2372
2476
  return;
@@ -2379,22 +2483,25 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
2379
2483
  if (settled) {
2380
2484
  return;
2381
2485
  }
2382
- const { id, response } = client.connection.requestWithId(
2486
+ const response = client.connection.request(
2383
2487
  "session/request_permission",
2384
2488
  clientParams
2385
2489
  );
2386
- outbound.push({ client, id });
2490
+ outbound.push({ client });
2387
2491
  void response.then((result) => {
2388
2492
  settle(() => {
2493
+ const update = buildPermissionResolvedUpdate({
2494
+ toolCallId,
2495
+ result,
2496
+ resolver: client
2497
+ });
2389
2498
  for (const o of outbound) {
2390
2499
  if (o.client.clientId === client.clientId) {
2391
2500
  continue;
2392
2501
  }
2393
- void o.client.connection.notify("session/permission_resolved", {
2394
- ...clientParams,
2395
- requestId: o.id,
2396
- resolvedBy: client.clientId,
2397
- result
2502
+ void o.client.connection.notify("session/update", {
2503
+ sessionId,
2504
+ update
2398
2505
  }).catch(() => void 0);
2399
2506
  }
2400
2507
  resolve3(result);
@@ -2507,6 +2614,97 @@ function extractAdvertisedCommands(params) {
2507
2614
  }
2508
2615
  return out;
2509
2616
  }
2617
+ function ensureMessageIdOnUpdate(method, params) {
2618
+ if (method !== "session/update" || !params || typeof params !== "object") {
2619
+ return params;
2620
+ }
2621
+ const p = params;
2622
+ if (!p.update || typeof p.update !== "object" || Array.isArray(p.update)) {
2623
+ return params;
2624
+ }
2625
+ const u = p.update;
2626
+ if (typeof u.messageId === "string") {
2627
+ return params;
2628
+ }
2629
+ return {
2630
+ ...params,
2631
+ update: { ...p.update, messageId: generateMessageId() }
2632
+ };
2633
+ }
2634
+ function findMessageIdIndex(history, target) {
2635
+ for (let i = 0; i < history.length; i++) {
2636
+ const entry = history[i];
2637
+ if (!entry || entry.method !== "session/update") {
2638
+ continue;
2639
+ }
2640
+ const params = entry.params;
2641
+ if (params?.update?.messageId === target) {
2642
+ return i;
2643
+ }
2644
+ }
2645
+ return -1;
2646
+ }
2647
+ function extractToolCallId(params) {
2648
+ if (!params || typeof params !== "object") {
2649
+ return void 0;
2650
+ }
2651
+ const toolCall = params.toolCall;
2652
+ if (!toolCall || typeof toolCall !== "object") {
2653
+ return void 0;
2654
+ }
2655
+ const id = toolCall.toolCallId;
2656
+ return typeof id === "string" ? id : void 0;
2657
+ }
2658
+ function buildPermissionResolvedUpdate(args) {
2659
+ const outcome = extractOutcome(args.result);
2660
+ const update = {
2661
+ sessionUpdate: "permission_resolved"
2662
+ };
2663
+ if (args.toolCallId !== void 0) {
2664
+ update.toolCallId = args.toolCallId;
2665
+ }
2666
+ if (outcome) {
2667
+ update.outcome = outcome;
2668
+ if (outcome.kind === "selected" && typeof outcome.optionId === "string") {
2669
+ update.chosenOptionId = outcome.optionId;
2670
+ }
2671
+ }
2672
+ update.resolvedBy = buildResolvedBy(args.resolver);
2673
+ return update;
2674
+ }
2675
+ function extractOutcome(result) {
2676
+ if (!result || typeof result !== "object") {
2677
+ return void 0;
2678
+ }
2679
+ const raw = result.outcome;
2680
+ if (!raw || typeof raw !== "object") {
2681
+ return void 0;
2682
+ }
2683
+ const kind = raw.kind;
2684
+ if (typeof kind !== "string") {
2685
+ return void 0;
2686
+ }
2687
+ const out = { kind };
2688
+ const optionId = raw.optionId;
2689
+ if (typeof optionId === "string") {
2690
+ out.optionId = optionId;
2691
+ }
2692
+ const reason = raw.reason;
2693
+ if (typeof reason === "string") {
2694
+ out.reason = reason;
2695
+ }
2696
+ return out;
2697
+ }
2698
+ function buildResolvedBy(client) {
2699
+ const out = { clientId: client.clientId };
2700
+ if (client.clientInfo?.name) {
2701
+ out.name = client.clientInfo.name;
2702
+ }
2703
+ if (client.clientInfo?.version) {
2704
+ out.version = client.clientInfo.version;
2705
+ }
2706
+ return out;
2707
+ }
2510
2708
  function extractPromptText(prompt) {
2511
2709
  if (typeof prompt === "string") {
2512
2710
  return prompt;
@@ -4283,7 +4481,7 @@ function registerSessionRoutes(app, manager, defaults) {
4283
4481
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4284
4482
  reply.header(
4285
4483
  "Content-Disposition",
4286
- `attachment; filename="hydra-${id}-${stamp}.hydra"`
4484
+ `attachment; filename="${id}-${stamp}.hydra"`
4287
4485
  );
4288
4486
  reply.code(200).send(bundle);
4289
4487
  });
@@ -4718,15 +4916,20 @@ function registerAcpWsEndpoint(app, deps) {
4718
4916
  connection,
4719
4917
  session,
4720
4918
  state,
4721
- params.clientInfo
4919
+ params.clientInfo,
4920
+ params.clientId
4921
+ );
4922
+ const { entries: replay, appliedPolicy } = await session.attach(
4923
+ client,
4924
+ params.historyPolicy,
4925
+ { afterMessageId: params.afterMessageId }
4722
4926
  );
4723
- const replay = await session.attach(client, params.historyPolicy);
4724
4927
  state.attached.set(session.sessionId, {
4725
4928
  sessionId: session.sessionId,
4726
4929
  clientId: client.clientId
4727
4930
  });
4728
4931
  app.log.info(
4729
- `session/attach OK sessionId=${session.sessionId} clientId=${client.clientId} attachedCount=${state.attached.size} replayed=${replay.length}`
4932
+ `session/attach OK sessionId=${session.sessionId} clientId=${client.clientId} attachedCount=${state.attached.size} requestedPolicy=${params.historyPolicy} appliedPolicy=${appliedPolicy} replayed=${replay.length}`
4730
4933
  );
4731
4934
  for (const note of replay) {
4732
4935
  await connection.notify(note.method, note.params);
@@ -4734,6 +4937,13 @@ function registerAcpWsEndpoint(app, deps) {
4734
4937
  session.replayPendingPermissions(client);
4735
4938
  return {
4736
4939
  sessionId: session.sessionId,
4940
+ clientId: client.clientId,
4941
+ connectedClients: session.connectedClients(client.clientId),
4942
+ // appliedPolicy surfaces whether after_message fell back to full
4943
+ // (because afterMessageId wasn't found in history) — RFD #533
4944
+ // says the response.historyPolicy should reflect what actually
4945
+ // ran, not what was asked for.
4946
+ historyPolicy: appliedPolicy,
4737
4947
  replayed: replay.length,
4738
4948
  _meta: buildResponseMeta(session)
4739
4949
  };
@@ -4749,7 +4959,7 @@ function registerAcpWsEndpoint(app, deps) {
4749
4959
  const session = deps.manager.get(params.sessionId);
4750
4960
  session?.detach(att.clientId);
4751
4961
  state.attached.delete(params.sessionId);
4752
- return { detached: true };
4962
+ return { sessionId: params.sessionId, status: "detached" };
4753
4963
  });
4754
4964
  connection.onRequest("session/list", async (raw) => {
4755
4965
  const params = SessionListParams.parse(raw ?? {});
@@ -4822,7 +5032,7 @@ function registerAcpWsEndpoint(app, deps) {
4822
5032
  session = await deps.manager.resurrect(fromDisk);
4823
5033
  }
4824
5034
  const client = bindClientToSession(connection, session, state);
4825
- const replay = await session.attach(client, "pending_only");
5035
+ const { entries: replay } = await session.attach(client, "pending_only");
4826
5036
  state.attached.set(session.sessionId, {
4827
5037
  sessionId: session.sessionId,
4828
5038
  clientId: client.clientId
@@ -4916,11 +5126,11 @@ function buildInitializeResult() {
4916
5126
  ]
4917
5127
  };
4918
5128
  }
4919
- function bindClientToSession(connection, session, state, clientInfo) {
5129
+ function bindClientToSession(connection, session, state, clientInfo, callerClientId) {
4920
5130
  void state;
4921
5131
  void session;
4922
5132
  return {
4923
- clientId: `cli_${nanoid2(8)}`,
5133
+ clientId: callerClientId ?? `cli_${nanoid2(8)}`,
4924
5134
  connection,
4925
5135
  clientInfo
4926
5136
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydra-acp/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Multi-client ACP session daemon: spawn agents, attach over WSS, multiplex sessions across editors.",
5
5
  "license": "MIT",
6
6
  "type": "module",