@mattstack/rt-client 0.9.0 → 0.10.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/README.md CHANGED
@@ -90,7 +90,7 @@ await chatPost({ handle: 'matt', room: 'build', body: 'on it' });
90
90
 
91
91
  const health = await daemonHealth(); // { reachable, error? }, never throws
92
92
  const stop = createRelay({ // one daemon subscription, republished
93
- match: t => t.startsWith('chat/'), // `chat/<room>/msg`, `chat/wake/<handle>`
93
+ match: t => t.startsWith('chat/'), // `chat/<room>/msg`
94
94
  topic: 'chat',
95
95
  publish: (topic, data) => server.publish(topic, data),
96
96
  });
@@ -98,20 +98,18 @@ const stop = createRelay({ // one daemon sub
98
98
 
99
99
  | Function | Daemon verb |
100
100
  | --- | --- |
101
- | `chatSignIn` / `chatSignOut` / `chatAway` / `chatBack` / `chatPulse` | presence: the buddy-list row and its heartbeats |
101
+ | `chatSignIn` / `chatSignOut` / `chatAway` / `chatBack` | presence: the buddy-list row and its session heartbeat |
102
102
  | `chatBuddies` / `chatWho` / `chatRooms` | the roster, one room's members, a handle's rooms |
103
103
  | `chatJoin` / `chatLeave` / `chatArchive` | membership (`wakeOn: mention \| all \| none`); archive parks a room for everyone until a post revives it |
104
104
  | `chatPost` / `chatDm` / `chatDmOpen` / `chatRead` / `chatMessages` / `chatMark` | messages: post, DM, open a DM room without posting, read-and-advance, page, advance the cursor |
105
- | `chatArm` / `chatTouch` / `chatDisarm` / `chatUnreadWaking` | the tail's wake protocol; the CLI's `rt chat tail` uses these |
106
105
  | `paneList` / `panePeek` / `paneSpawn` / `paneAccounts` / `paneDirectories` | herdr panes: list with presence joined, peek a screen, start claude in a tab, cswap accounts, directory suggestions |
107
106
  | `chatInvite` | type `/chat:join <room>` into a pane; `accepted`, `queued` or `refused` |
108
107
  | `createRelay` / `subscribe` | the event stream; `daemonHealth` the reachability probe |
109
108
 
110
109
  Pass `{ sockPath }` as the trailing options to reach a non-default daemon
111
- socket. The verbs, their payloads and the wake protocol are specified in
112
- repo-tools `docs/superpowers/specs/2026-08-23-rt-chat-design.md` and
113
- `2026-08-24-rt-chat-presence-design.md`; the agent-facing rules are
114
- `skills/rt-chat/SKILL.md`.
110
+ socket. The verbs and their payloads are specified in repo-tools
111
+ `docs/superpowers/specs/2026-08-28-rt-chat-delivery-v2-design.md`; the
112
+ agent-facing rules are `skills/rt-chat/SKILL.md`.
115
113
 
116
114
  ## License
117
115
 
package/dist/client.d.ts CHANGED
@@ -96,35 +96,8 @@ export declare function chatMessages(a: {
96
96
  }, o?: RtClientOptions): Promise<RtResponse<{
97
97
  messages: ChatMessage[];
98
98
  }>>;
99
- export declare function chatArm(a: {
100
- handle: string;
101
- room?: string;
102
- sessionId?: string;
103
- }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
104
- export declare function chatTouch(a: {
105
- handle: string;
106
- room?: string;
107
- sessionId?: string;
108
- }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
109
- export declare function chatDisarm(a: {
110
- handle: string;
111
- sessionId?: string;
112
- }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
113
- export declare function chatUnreadWaking(a: {
114
- handle: string;
115
- room?: string;
116
- }, o?: RtClientOptions): Promise<RtResponse<{
117
- rooms: {
118
- room: string;
119
- count: number;
120
- mentions: number;
121
- maxId: number;
122
- }[];
123
- }>>;
124
99
  export declare function chatSignIn(a: Commands["chat:sign-in"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["chat:sign-in"]["data"]>>;
125
- export declare function chatSignOut(a: {
126
- sessionId: string;
127
- }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
100
+ export declare function chatSignOut(a: Commands["chat:sign-out"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["chat:sign-out"]["data"]>>;
128
101
  export declare function chatAway(a: {
129
102
  sessionId: string;
130
103
  text: string;
@@ -137,20 +110,6 @@ export declare function chatBuddies(o?: RtClientOptions): Promise<RtResponse<{
137
110
  status: BuddyStatus;
138
111
  }>;
139
112
  }>>;
140
- export declare function chatPulse(a: {
141
- sessionId: string;
142
- cwd?: string;
143
- repo?: string;
144
- branch?: string;
145
- pane?: string;
146
- }, o?: RtClientOptions): Promise<RtResponse<{
147
- unread: {
148
- dms: number;
149
- mentions: number;
150
- rooms: number;
151
- };
152
- status: BuddyStatus;
153
- }>>;
154
113
  export declare function chatDm(a: {
155
114
  from: string;
156
115
  to: string;
@@ -99,8 +99,6 @@ export interface ChatMember {
99
99
  joinedAt: number;
100
100
  lastReadId: number;
101
101
  wakeOn: WakeMode;
102
- lastSeenAt?: number;
103
- armedAt?: number;
104
102
  cwd?: string;
105
103
  pane?: string;
106
104
  /** Presence-joined by chat:who's handler — the only place this type is ever returned, and it always attaches one. */
@@ -137,7 +135,7 @@ export interface RoomSummary {
137
135
  * above: mirrors lib/state/presence-store.ts's types, which rt-client
138
136
  * cannot import.
139
137
  */
140
- export type BuddyStatus = "live" | "idle" | "deaf" | "offline";
138
+ export type BuddyStatus = "live" | "idle" | "offline";
141
139
  export interface PresenceRow {
142
140
  sessionId: string;
143
141
  handle: string;
@@ -149,8 +147,6 @@ export interface PresenceRow {
149
147
  statusText?: string;
150
148
  signedInAt: number;
151
149
  lastSeenAt: number;
152
- tailSeenAt?: number;
153
- armedAt?: number;
154
150
  signedOutAt?: number;
155
151
  }
156
152
  export type AgentStatus = "idle" | "working" | "blocked" | "done" | "unknown";
@@ -278,6 +274,7 @@ export interface AgentRecord {
278
274
  account?: string;
279
275
  label?: string;
280
276
  caller?: string;
277
+ handle?: string;
281
278
  paneId?: string;
282
279
  tabId?: string;
283
280
  workspaceId?: string;
@@ -511,65 +508,56 @@ export interface Commands {
511
508
  messages: ChatMessage[];
512
509
  };
513
510
  };
514
- "chat:arm": {
515
- payload: {
516
- handle: string;
517
- room?: string;
518
- sessionId?: string;
519
- };
520
- data: Record<string, never>;
521
- };
522
- "chat:touch": {
523
- payload: {
524
- handle: string;
525
- room?: string;
526
- sessionId?: string;
527
- };
528
- data: Record<string, never>;
529
- };
530
- "chat:disarm": {
531
- payload: {
532
- handle: string;
533
- sessionId?: string;
534
- };
535
- data: Record<string, never>;
536
- };
537
- "chat:unread-waking": {
538
- payload: {
539
- handle: string;
540
- room?: string;
541
- };
542
- data: {
543
- rooms: {
544
- room: string;
545
- count: number;
546
- mentions: number;
547
- maxId: number;
548
- }[];
549
- };
550
- };
551
- /** No `baseHandle` means "draw me a first name": the daemon picks the least recently used pool name no live session holds. */
511
+ /**
512
+ * No `baseHandle` means "draw me a first name": the daemon picks the least
513
+ * recently used pool name no live session holds. `viaPane` resolves
514
+ * `sessionId` daemon-side from herdr's `session.snapshot` for the pane id
515
+ * in `pane`, rather than trusting a caller-supplied id -- the caller may
516
+ * have none of its own (a human or another agent signing a target pane in
517
+ * on its behalf); the response's `sessionId` and `room` are then the only
518
+ * way that caller learns what got signed in and joined, since it has
519
+ * nothing local to derive either from.
520
+ */
552
521
  "chat:sign-in": {
553
522
  payload: {
554
- sessionId: string;
523
+ sessionId?: string;
555
524
  baseHandle?: string;
556
525
  cwd?: string;
557
526
  repo?: string;
558
527
  branch?: string;
559
528
  pane?: string;
560
529
  statusText?: string;
530
+ viaPane?: boolean;
531
+ /** `viaPane` only: overrides the pane-cwd-derived room outright. */
532
+ room?: string;
533
+ /** `viaPane` only: skip room derivation/join entirely, same as --no-room on the non-pane path. */
534
+ noRoom?: boolean;
561
535
  };
562
536
  data: {
563
537
  handle: string;
564
538
  baseHandle: string;
565
539
  reclaimed: boolean;
540
+ sessionId: string;
541
+ room: string | null;
566
542
  };
567
543
  };
544
+ /**
545
+ * `viaPane` mirrors `chat:sign-in`'s: the daemon resolves `pane` to a
546
+ * session id via herdr's `session.snapshot` rather than trusting a
547
+ * caller-supplied one, so a process signing another pane out (herdr-chat,
548
+ * or a human invoking a target pane) needs neither that pane's session id
549
+ * nor its own. The response's `sessionId` is the RESOLVED id, the only way
550
+ * such a caller learns which session file to delete locally.
551
+ */
568
552
  "chat:sign-out": {
569
553
  payload: {
554
+ sessionId?: string;
555
+ pane?: string;
556
+ viaPane?: boolean;
557
+ };
558
+ data: {
570
559
  sessionId: string;
571
560
  };
572
- data: Record<string, never>;
573
561
  };
574
562
  "chat:away": {
575
563
  payload: {
@@ -592,24 +580,6 @@ export interface Commands {
592
580
  }>;
593
581
  };
594
582
  };
595
- /** `unread`'s three fields are disjoint and sum to the true total: `dms` is DM-room waking count; `mentions` is non-DM waking mentions; `rooms` is non-DM waking count minus those mentions (never negative). */
596
- "chat:pulse": {
597
- payload: {
598
- sessionId: string;
599
- cwd?: string;
600
- repo?: string;
601
- branch?: string;
602
- pane?: string;
603
- };
604
- data: {
605
- unread: {
606
- dms: number;
607
- mentions: number;
608
- rooms: number;
609
- };
610
- status: BuddyStatus;
611
- };
612
- };
613
583
  "chat:dm": {
614
584
  payload: {
615
585
  from: string;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { rtCommand, DEFAULT_SOCK } from "./transport.ts";
2
2
  export type { RtResponse, RtClientOptions } from "./transport.ts";
3
- export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatArm, chatTouch, chatDisarm, chatUnreadWaking, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatPulse, chatDm, chatArchive, chatDmOpen, eventsHead, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, } from "./client.ts";
3
+ export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatDm, chatArchive, chatDmOpen, eventsHead, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, } from "./client.ts";
4
4
  export { COMMAND_NAMES } from "./commands.ts";
5
5
  export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, BranchEnrichment, Commands, CommandName, ForgeSlug, ForgeTokenData, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, AgentSurface, AgentStatus, ChatPane, PaneAccount, PaneDirectory, InviteResult, PaneDelivery, PaneSendResult, } from "./commands.ts";
6
6
  export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
package/dist/index.js CHANGED
@@ -117,36 +117,10 @@ function chatMessages(a, o = {}) {
117
117
  payload.limit = a.limit;
118
118
  return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
119
119
  }
120
- function chatArm(a, o = {}) {
121
- const payload = { handle: a.handle };
122
- if (a.room !== undefined)
123
- payload.room = a.room;
124
- if (a.sessionId !== undefined)
125
- payload.sessionId = a.sessionId;
126
- return rtCommand("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
127
- }
128
- function chatTouch(a, o = {}) {
129
- const payload = { handle: a.handle };
130
- if (a.room !== undefined)
131
- payload.room = a.room;
132
- if (a.sessionId !== undefined)
133
- payload.sessionId = a.sessionId;
134
- return rtCommand("chat:touch", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
135
- }
136
- function chatDisarm(a, o = {}) {
137
- const payload = { handle: a.handle };
120
+ function chatSignIn(a, o = {}) {
121
+ const payload = {};
138
122
  if (a.sessionId !== undefined)
139
123
  payload.sessionId = a.sessionId;
140
- return rtCommand("chat:disarm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
141
- }
142
- function chatUnreadWaking(a, o = {}) {
143
- const payload = { handle: a.handle };
144
- if (a.room !== undefined)
145
- payload.room = a.room;
146
- return rtCommand("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
147
- }
148
- function chatSignIn(a, o = {}) {
149
- const payload = { sessionId: a.sessionId };
150
124
  if (a.baseHandle !== undefined)
151
125
  payload.baseHandle = a.baseHandle;
152
126
  if (a.cwd !== undefined)
@@ -159,10 +133,23 @@ function chatSignIn(a, o = {}) {
159
133
  payload.pane = a.pane;
160
134
  if (a.statusText !== undefined)
161
135
  payload.statusText = a.statusText;
136
+ if (a.viaPane !== undefined)
137
+ payload.viaPane = a.viaPane;
138
+ if (a.room !== undefined)
139
+ payload.room = a.room;
140
+ if (a.noRoom !== undefined)
141
+ payload.noRoom = a.noRoom;
162
142
  return rtCommand("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
163
143
  }
164
144
  function chatSignOut(a, o = {}) {
165
- return rtCommand("chat:sign-out", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
145
+ const payload = {};
146
+ if (a.sessionId !== undefined)
147
+ payload.sessionId = a.sessionId;
148
+ if (a.pane !== undefined)
149
+ payload.pane = a.pane;
150
+ if (a.viaPane !== undefined)
151
+ payload.viaPane = a.viaPane;
152
+ return rtCommand("chat:sign-out", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
166
153
  }
167
154
  function chatAway(a, o = {}) {
168
155
  return rtCommand("chat:away", { sessionId: a.sessionId, text: a.text }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
@@ -173,18 +160,6 @@ function chatBack(a, o = {}) {
173
160
  function chatBuddies(o = {}) {
174
161
  return rtCommand("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
175
162
  }
176
- function chatPulse(a, o = {}) {
177
- const payload = { sessionId: a.sessionId };
178
- if (a.cwd !== undefined)
179
- payload.cwd = a.cwd;
180
- if (a.repo !== undefined)
181
- payload.repo = a.repo;
182
- if (a.branch !== undefined)
183
- payload.branch = a.branch;
184
- if (a.pane !== undefined)
185
- payload.pane = a.pane;
186
- return rtCommand("chat:pulse", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
187
- }
188
163
  function chatDm(a, o = {}) {
189
164
  const payload = { from: a.from, to: a.to, body: a.body };
190
165
  if (a.sessionId !== undefined)
@@ -289,16 +264,11 @@ var COMMAND_NAMES = [
289
264
  "chat:who",
290
265
  "chat:mark",
291
266
  "chat:messages",
292
- "chat:arm",
293
- "chat:touch",
294
- "chat:disarm",
295
- "chat:unread-waking",
296
267
  "chat:sign-in",
297
268
  "chat:sign-out",
298
269
  "chat:away",
299
270
  "chat:back",
300
271
  "chat:buddies",
301
- "chat:pulse",
302
272
  "chat:dm",
303
273
  "chat:archive",
304
274
  "chat:dm-open",
@@ -1783,13 +1753,10 @@ export {
1783
1753
  createRelay,
1784
1754
  clearIdentityMemo,
1785
1755
  chatWho,
1786
- chatUnreadWaking,
1787
- chatTouch,
1788
1756
  chatSignOut,
1789
1757
  chatSignIn,
1790
1758
  chatRooms,
1791
1759
  chatRead,
1792
- chatPulse,
1793
1760
  chatPost,
1794
1761
  chatMessages,
1795
1762
  chatMark,
@@ -1798,11 +1765,9 @@ export {
1798
1765
  chatInvite,
1799
1766
  chatDmOpen,
1800
1767
  chatDm,
1801
- chatDisarm,
1802
1768
  chatBuddies,
1803
1769
  chatBack,
1804
1770
  chatAway,
1805
- chatArm,
1806
1771
  chatArchive,
1807
1772
  allDefs,
1808
1773
  agentStart,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/client.ts CHANGED
@@ -220,65 +220,35 @@ export function chatMessages(
220
220
  return rtCommand<{ messages: ChatMessage[] }>("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
221
221
  }
222
222
 
223
- export function chatArm(
224
- a: { handle: string; room?: string; sessionId?: string },
225
- o: RtClientOptions = {},
226
- ): Promise<RtResponse<Record<string, never>>> {
227
- const payload: Record<string, unknown> = { handle: a.handle };
228
- if (a.room !== undefined) payload.room = a.room;
229
- if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
230
- return rtCommand<Record<string, never>>("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
231
- }
232
-
233
- export function chatTouch(
234
- a: { handle: string; room?: string; sessionId?: string },
235
- o: RtClientOptions = {},
236
- ): Promise<RtResponse<Record<string, never>>> {
237
- const payload: Record<string, unknown> = { handle: a.handle };
238
- if (a.room !== undefined) payload.room = a.room;
239
- if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
240
- return rtCommand<Record<string, never>>("chat:touch", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
241
- }
242
-
243
- export function chatDisarm(
244
- a: { handle: string; sessionId?: string },
245
- o: RtClientOptions = {},
246
- ): Promise<RtResponse<Record<string, never>>> {
247
- const payload: Record<string, unknown> = { handle: a.handle };
248
- if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
249
- return rtCommand<Record<string, never>>("chat:disarm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
250
- }
251
-
252
- export function chatUnreadWaking(
253
- a: { handle: string; room?: string },
254
- o: RtClientOptions = {},
255
- ): Promise<RtResponse<{ rooms: { room: string; count: number; mentions: number; maxId: number }[] }>> {
256
- const payload: Record<string, unknown> = { handle: a.handle };
257
- if (a.room !== undefined) payload.room = a.room;
258
- return rtCommand<{ rooms: { room: string; count: number; mentions: number; maxId: number }[] }>("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
259
- }
260
-
261
223
  // ─── Presence ──────────────────────────────────────────────────────────
262
224
 
263
225
  export function chatSignIn(
264
226
  a: Commands["chat:sign-in"]["payload"],
265
227
  o: RtClientOptions = {},
266
228
  ): Promise<RtResponse<Commands["chat:sign-in"]["data"]>> {
267
- const payload: Record<string, unknown> = { sessionId: a.sessionId };
229
+ const payload: Record<string, unknown> = {};
230
+ if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
268
231
  if (a.baseHandle !== undefined) payload.baseHandle = a.baseHandle;
269
232
  if (a.cwd !== undefined) payload.cwd = a.cwd;
270
233
  if (a.repo !== undefined) payload.repo = a.repo;
271
234
  if (a.branch !== undefined) payload.branch = a.branch;
272
235
  if (a.pane !== undefined) payload.pane = a.pane;
273
236
  if (a.statusText !== undefined) payload.statusText = a.statusText;
237
+ if (a.viaPane !== undefined) payload.viaPane = a.viaPane;
238
+ if (a.room !== undefined) payload.room = a.room;
239
+ if (a.noRoom !== undefined) payload.noRoom = a.noRoom;
274
240
  return rtCommand<Commands["chat:sign-in"]["data"]>("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
275
241
  }
276
242
 
277
243
  export function chatSignOut(
278
- a: { sessionId: string },
244
+ a: Commands["chat:sign-out"]["payload"],
279
245
  o: RtClientOptions = {},
280
- ): Promise<RtResponse<Record<string, never>>> {
281
- return rtCommand<Record<string, never>>("chat:sign-out", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
246
+ ): Promise<RtResponse<Commands["chat:sign-out"]["data"]>> {
247
+ const payload: Record<string, unknown> = {};
248
+ if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
249
+ if (a.pane !== undefined) payload.pane = a.pane;
250
+ if (a.viaPane !== undefined) payload.viaPane = a.viaPane;
251
+ return rtCommand<Commands["chat:sign-out"]["data"]>("chat:sign-out", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
282
252
  }
283
253
 
284
254
  export function chatAway(
@@ -301,22 +271,6 @@ export function chatBuddies(
301
271
  return rtCommand<{ buddies: Array<PresenceRow & { status: BuddyStatus }> }>("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
302
272
  }
303
273
 
304
- export function chatPulse(
305
- a: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string },
306
- o: RtClientOptions = {},
307
- ): Promise<RtResponse<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>> {
308
- const payload: Record<string, unknown> = { sessionId: a.sessionId };
309
- if (a.cwd !== undefined) payload.cwd = a.cwd;
310
- if (a.repo !== undefined) payload.repo = a.repo;
311
- if (a.branch !== undefined) payload.branch = a.branch;
312
- if (a.pane !== undefined) payload.pane = a.pane;
313
- return rtCommand<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>(
314
- "chat:pulse",
315
- payload,
316
- { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 },
317
- );
318
- }
319
-
320
274
  export function chatDm(
321
275
  a: { from: string; to: string; body: string; sessionId?: string },
322
276
  o: RtClientOptions = {},
package/src/commands.ts CHANGED
@@ -92,8 +92,6 @@ export interface ChatMember {
92
92
  joinedAt: number;
93
93
  lastReadId: number;
94
94
  wakeOn: WakeMode;
95
- lastSeenAt?: number;
96
- armedAt?: number;
97
95
  cwd?: string;
98
96
  pane?: string;
99
97
  /** Presence-joined by chat:who's handler — the only place this type is ever returned, and it always attaches one. */
@@ -130,7 +128,7 @@ export interface RoomSummary {
130
128
  * above: mirrors lib/state/presence-store.ts's types, which rt-client
131
129
  * cannot import.
132
130
  */
133
- export type BuddyStatus = "live" | "idle" | "deaf" | "offline";
131
+ export type BuddyStatus = "live" | "idle" | "offline";
134
132
 
135
133
  export interface PresenceRow {
136
134
  sessionId: string;
@@ -143,8 +141,6 @@ export interface PresenceRow {
143
141
  statusText?: string;
144
142
  signedInAt: number;
145
143
  lastSeenAt: number;
146
- tailSeenAt?: number;
147
- armedAt?: number;
148
144
  signedOutAt?: number;
149
145
  }
150
146
 
@@ -223,7 +219,7 @@ export interface AgentRecord {
223
219
  id: string; repo: string; cwd: string; provider: string;
224
220
  surface: AgentSurface; sessionId: string;
225
221
  model?: string; effort?: string; account?: string;
226
- label?: string; caller?: string;
222
+ label?: string; caller?: string; handle?: string;
227
223
  paneId?: string; tabId?: string; workspaceId?: string;
228
224
  extraArgs?: string; exitCode?: number; resultPath?: string;
229
225
  createdAt: number; lastResumedAt?: number; finishedAt?: number;
@@ -293,27 +289,51 @@ export interface Commands {
293
289
  "chat:who": { payload: { room: string }; data: { members: ChatMember[] } };
294
290
  "chat:mark": { payload: { handle: string; room?: string }; data: Record<string, never> };
295
291
  "chat:messages": { payload: { room: string; before?: number; limit?: number }; data: { messages: ChatMessage[] } };
296
- "chat:arm": { payload: { handle: string; room?: string; sessionId?: string }; data: Record<string, never> };
297
- "chat:touch": { payload: { handle: string; room?: string; sessionId?: string }; data: Record<string, never> };
298
- "chat:disarm": { payload: { handle: string; sessionId?: string }; data: Record<string, never> };
299
- "chat:unread-waking": { payload: { handle: string; room?: string }; data: { rooms: { room: string; count: number; mentions: number; maxId: number }[] } };
300
292
 
301
293
  // A session id keys these to one signed-in handle, not a room-membership
302
294
  // handle string.
303
- /** No `baseHandle` means "draw me a first name": the daemon picks the least recently used pool name no live session holds. */
295
+ /**
296
+ * No `baseHandle` means "draw me a first name": the daemon picks the least
297
+ * recently used pool name no live session holds. `viaPane` resolves
298
+ * `sessionId` daemon-side from herdr's `session.snapshot` for the pane id
299
+ * in `pane`, rather than trusting a caller-supplied id -- the caller may
300
+ * have none of its own (a human or another agent signing a target pane in
301
+ * on its behalf); the response's `sessionId` and `room` are then the only
302
+ * way that caller learns what got signed in and joined, since it has
303
+ * nothing local to derive either from.
304
+ */
304
305
  "chat:sign-in": {
305
- payload: { sessionId: string; baseHandle?: string; cwd?: string; repo?: string; branch?: string; pane?: string; statusText?: string };
306
- data: { handle: string; baseHandle: string; reclaimed: boolean };
306
+ payload: {
307
+ sessionId?: string;
308
+ baseHandle?: string;
309
+ cwd?: string;
310
+ repo?: string;
311
+ branch?: string;
312
+ pane?: string;
313
+ statusText?: string;
314
+ viaPane?: boolean;
315
+ /** `viaPane` only: overrides the pane-cwd-derived room outright. */
316
+ room?: string;
317
+ /** `viaPane` only: skip room derivation/join entirely, same as --no-room on the non-pane path. */
318
+ noRoom?: boolean;
319
+ };
320
+ data: { handle: string; baseHandle: string; reclaimed: boolean; sessionId: string; room: string | null };
321
+ };
322
+ /**
323
+ * `viaPane` mirrors `chat:sign-in`'s: the daemon resolves `pane` to a
324
+ * session id via herdr's `session.snapshot` rather than trusting a
325
+ * caller-supplied one, so a process signing another pane out (herdr-chat,
326
+ * or a human invoking a target pane) needs neither that pane's session id
327
+ * nor its own. The response's `sessionId` is the RESOLVED id, the only way
328
+ * such a caller learns which session file to delete locally.
329
+ */
330
+ "chat:sign-out": {
331
+ payload: { sessionId?: string; pane?: string; viaPane?: boolean };
332
+ data: { sessionId: string };
307
333
  };
308
- "chat:sign-out": { payload: { sessionId: string }; data: Record<string, never> };
309
334
  "chat:away": { payload: { sessionId: string; text: string }; data: Record<string, never> };
310
335
  "chat:back": { payload: { sessionId: string }; data: Record<string, never> };
311
336
  "chat:buddies": { payload: Record<string, never>; data: { buddies: Array<PresenceRow & { status: BuddyStatus }> } };
312
- /** `unread`'s three fields are disjoint and sum to the true total: `dms` is DM-room waking count; `mentions` is non-DM waking mentions; `rooms` is non-DM waking count minus those mentions (never negative). */
313
- "chat:pulse": {
314
- payload: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string };
315
- data: { unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus };
316
- };
317
337
  "chat:dm": { payload: { from: string; to: string; body: string; sessionId?: string }; data: { room: string; id: number; recipients: string[] } };
318
338
  "chat:archive": { payload: { room: string; handle: string; archived: boolean }; data: { room: string; archivedAt: number | null } };
319
339
  "chat:dm-open": { payload: { from: string; to: string; sessionId?: string }; data: { room: string; created: boolean } };
@@ -358,16 +378,11 @@ export const COMMAND_NAMES: readonly CommandName[] = [
358
378
  "chat:who",
359
379
  "chat:mark",
360
380
  "chat:messages",
361
- "chat:arm",
362
- "chat:touch",
363
- "chat:disarm",
364
- "chat:unread-waking",
365
381
  "chat:sign-in",
366
382
  "chat:sign-out",
367
383
  "chat:away",
368
384
  "chat:back",
369
385
  "chat:buddies",
370
- "chat:pulse",
371
386
  "chat:dm",
372
387
  "chat:archive",
373
388
  "chat:dm-open",
package/src/index.ts CHANGED
@@ -18,16 +18,11 @@ export {
18
18
  chatWho,
19
19
  chatMark,
20
20
  chatMessages,
21
- chatArm,
22
- chatTouch,
23
- chatDisarm,
24
- chatUnreadWaking,
25
21
  chatSignIn,
26
22
  chatSignOut,
27
23
  chatAway,
28
24
  chatBack,
29
25
  chatBuddies,
30
- chatPulse,
31
26
  chatDm,
32
27
  chatArchive,
33
28
  chatDmOpen,