@mattstack/rt-client 0.8.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,46 +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
- export declare function chatSignIn(a: {
125
- sessionId: string;
126
- baseHandle: string;
127
- cwd?: string;
128
- repo?: string;
129
- branch?: string;
130
- pane?: string;
131
- statusText?: string;
132
- }, o?: RtClientOptions): Promise<RtResponse<{
133
- handle: string;
134
- reclaimed: boolean;
135
- }>>;
136
- export declare function chatSignOut(a: {
137
- sessionId: string;
138
- }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
99
+ export declare function chatSignIn(a: Commands["chat:sign-in"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["chat:sign-in"]["data"]>>;
100
+ export declare function chatSignOut(a: Commands["chat:sign-out"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["chat:sign-out"]["data"]>>;
139
101
  export declare function chatAway(a: {
140
102
  sessionId: string;
141
103
  text: string;
@@ -148,20 +110,6 @@ export declare function chatBuddies(o?: RtClientOptions): Promise<RtResponse<{
148
110
  status: BuddyStatus;
149
111
  }>;
150
112
  }>>;
151
- export declare function chatPulse(a: {
152
- sessionId: string;
153
- cwd?: string;
154
- repo?: string;
155
- branch?: string;
156
- pane?: string;
157
- }, o?: RtClientOptions): Promise<RtResponse<{
158
- unread: {
159
- dms: number;
160
- mentions: number;
161
- rooms: number;
162
- };
163
- status: BuddyStatus;
164
- }>>;
165
113
  export declare function chatDm(a: {
166
114
  from: string;
167
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,63 +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
- };
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
+ */
551
521
  "chat:sign-in": {
552
522
  payload: {
553
- sessionId: string;
554
- baseHandle: string;
523
+ sessionId?: string;
524
+ baseHandle?: string;
555
525
  cwd?: string;
556
526
  repo?: string;
557
527
  branch?: string;
558
528
  pane?: string;
559
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;
560
535
  };
561
536
  data: {
562
537
  handle: string;
538
+ baseHandle: string;
563
539
  reclaimed: boolean;
540
+ sessionId: string;
541
+ room: string | null;
564
542
  };
565
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
+ */
566
552
  "chat:sign-out": {
567
553
  payload: {
554
+ sessionId?: string;
555
+ pane?: string;
556
+ viaPane?: boolean;
557
+ };
558
+ data: {
568
559
  sessionId: string;
569
560
  };
570
- data: Record<string, never>;
571
561
  };
572
562
  "chat:away": {
573
563
  payload: {
@@ -590,24 +580,6 @@ export interface Commands {
590
580
  }>;
591
581
  };
592
582
  };
593
- /** `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). */
594
- "chat:pulse": {
595
- payload: {
596
- sessionId: string;
597
- cwd?: string;
598
- repo?: string;
599
- branch?: string;
600
- pane?: string;
601
- };
602
- data: {
603
- unread: {
604
- dms: number;
605
- mentions: number;
606
- rooms: number;
607
- };
608
- status: BuddyStatus;
609
- };
610
- };
611
583
  "chat:dm": {
612
584
  payload: {
613
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,12 @@ 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, baseHandle: a.baseHandle };
124
+ if (a.baseHandle !== undefined)
125
+ payload.baseHandle = a.baseHandle;
150
126
  if (a.cwd !== undefined)
151
127
  payload.cwd = a.cwd;
152
128
  if (a.repo !== undefined)
@@ -157,10 +133,23 @@ function chatSignIn(a, o = {}) {
157
133
  payload.pane = a.pane;
158
134
  if (a.statusText !== undefined)
159
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;
160
142
  return rtCommand("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
161
143
  }
162
144
  function chatSignOut(a, o = {}) {
163
- 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 });
164
153
  }
165
154
  function chatAway(a, o = {}) {
166
155
  return rtCommand("chat:away", { sessionId: a.sessionId, text: a.text }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
@@ -171,18 +160,6 @@ function chatBack(a, o = {}) {
171
160
  function chatBuddies(o = {}) {
172
161
  return rtCommand("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
173
162
  }
174
- function chatPulse(a, o = {}) {
175
- const payload = { sessionId: a.sessionId };
176
- if (a.cwd !== undefined)
177
- payload.cwd = a.cwd;
178
- if (a.repo !== undefined)
179
- payload.repo = a.repo;
180
- if (a.branch !== undefined)
181
- payload.branch = a.branch;
182
- if (a.pane !== undefined)
183
- payload.pane = a.pane;
184
- return rtCommand("chat:pulse", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
185
- }
186
163
  function chatDm(a, o = {}) {
187
164
  const payload = { from: a.from, to: a.to, body: a.body };
188
165
  if (a.sessionId !== undefined)
@@ -287,16 +264,11 @@ var COMMAND_NAMES = [
287
264
  "chat:who",
288
265
  "chat:mark",
289
266
  "chat:messages",
290
- "chat:arm",
291
- "chat:touch",
292
- "chat:disarm",
293
- "chat:unread-waking",
294
267
  "chat:sign-in",
295
268
  "chat:sign-out",
296
269
  "chat:away",
297
270
  "chat:back",
298
271
  "chat:buddies",
299
- "chat:pulse",
300
272
  "chat:dm",
301
273
  "chat:archive",
302
274
  "chat:dm-open",
@@ -1781,13 +1753,10 @@ export {
1781
1753
  createRelay,
1782
1754
  clearIdentityMemo,
1783
1755
  chatWho,
1784
- chatUnreadWaking,
1785
- chatTouch,
1786
1756
  chatSignOut,
1787
1757
  chatSignIn,
1788
1758
  chatRooms,
1789
1759
  chatRead,
1790
- chatPulse,
1791
1760
  chatPost,
1792
1761
  chatMessages,
1793
1762
  chatMark,
@@ -1796,11 +1765,9 @@ export {
1796
1765
  chatInvite,
1797
1766
  chatDmOpen,
1798
1767
  chatDm,
1799
- chatDisarm,
1800
1768
  chatBuddies,
1801
1769
  chatBack,
1802
1770
  chatAway,
1803
- chatArm,
1804
1771
  chatArchive,
1805
1772
  allDefs,
1806
1773
  agentStart,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/client.ts CHANGED
@@ -220,64 +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
- a: { sessionId: string; baseHandle: string; cwd?: string; repo?: string; branch?: string; pane?: string; statusText?: string },
226
+ a: Commands["chat:sign-in"]["payload"],
265
227
  o: RtClientOptions = {},
266
- ): Promise<RtResponse<{ handle: string; reclaimed: boolean }>> {
267
- const payload: Record<string, unknown> = { sessionId: a.sessionId, baseHandle: a.baseHandle };
228
+ ): Promise<RtResponse<Commands["chat:sign-in"]["data"]>> {
229
+ const payload: Record<string, unknown> = {};
230
+ if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
231
+ if (a.baseHandle !== undefined) payload.baseHandle = a.baseHandle;
268
232
  if (a.cwd !== undefined) payload.cwd = a.cwd;
269
233
  if (a.repo !== undefined) payload.repo = a.repo;
270
234
  if (a.branch !== undefined) payload.branch = a.branch;
271
235
  if (a.pane !== undefined) payload.pane = a.pane;
272
236
  if (a.statusText !== undefined) payload.statusText = a.statusText;
273
- return rtCommand<{ handle: string; reclaimed: boolean }>("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
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;
240
+ return rtCommand<Commands["chat:sign-in"]["data"]>("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
274
241
  }
275
242
 
276
243
  export function chatSignOut(
277
- a: { sessionId: string },
244
+ a: Commands["chat:sign-out"]["payload"],
278
245
  o: RtClientOptions = {},
279
- ): Promise<RtResponse<Record<string, never>>> {
280
- 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 });
281
252
  }
282
253
 
283
254
  export function chatAway(
@@ -300,22 +271,6 @@ export function chatBuddies(
300
271
  return rtCommand<{ buddies: Array<PresenceRow & { status: BuddyStatus }> }>("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
301
272
  }
302
273
 
303
- export function chatPulse(
304
- a: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string },
305
- o: RtClientOptions = {},
306
- ): Promise<RtResponse<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>> {
307
- const payload: Record<string, unknown> = { sessionId: a.sessionId };
308
- if (a.cwd !== undefined) payload.cwd = a.cwd;
309
- if (a.repo !== undefined) payload.repo = a.repo;
310
- if (a.branch !== undefined) payload.branch = a.branch;
311
- if (a.pane !== undefined) payload.pane = a.pane;
312
- return rtCommand<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>(
313
- "chat:pulse",
314
- payload,
315
- { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 },
316
- );
317
- }
318
-
319
274
  export function chatDm(
320
275
  a: { from: string; to: string; body: string; sessionId?: string },
321
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,26 +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.
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
+ */
303
305
  "chat:sign-in": {
304
- payload: { sessionId: string; baseHandle: string; cwd?: string; repo?: string; branch?: string; pane?: string; statusText?: string };
305
- data: { handle: 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 };
306
333
  };
307
- "chat:sign-out": { payload: { sessionId: string }; data: Record<string, never> };
308
334
  "chat:away": { payload: { sessionId: string; text: string }; data: Record<string, never> };
309
335
  "chat:back": { payload: { sessionId: string }; data: Record<string, never> };
310
336
  "chat:buddies": { payload: Record<string, never>; data: { buddies: Array<PresenceRow & { status: BuddyStatus }> } };
311
- /** `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). */
312
- "chat:pulse": {
313
- payload: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string };
314
- data: { unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus };
315
- };
316
337
  "chat:dm": { payload: { from: string; to: string; body: string; sessionId?: string }; data: { room: string; id: number; recipients: string[] } };
317
338
  "chat:archive": { payload: { room: string; handle: string; archived: boolean }; data: { room: string; archivedAt: number | null } };
318
339
  "chat:dm-open": { payload: { from: string; to: string; sessionId?: string }; data: { room: string; created: boolean } };
@@ -357,16 +378,11 @@ export const COMMAND_NAMES: readonly CommandName[] = [
357
378
  "chat:who",
358
379
  "chat:mark",
359
380
  "chat:messages",
360
- "chat:arm",
361
- "chat:touch",
362
- "chat:disarm",
363
- "chat:unread-waking",
364
381
  "chat:sign-in",
365
382
  "chat:sign-out",
366
383
  "chat:away",
367
384
  "chat:back",
368
385
  "chat:buddies",
369
- "chat:pulse",
370
386
  "chat:dm",
371
387
  "chat:archive",
372
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,