@mattstack/rt-client 0.15.1 → 0.19.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/dist/index.js CHANGED
@@ -205,7 +205,7 @@ function eventsList(payload, o = {}) {
205
205
  }
206
206
  function agentStart(a, o = {}) {
207
207
  const payload = { repo: a.repo, cwd: a.cwd };
208
- for (const k of ["prompt", "surface", "model", "effort", "account", "label", "caller", "workspace", "tab", "extraArgs"]) {
208
+ for (const k of ["prompt", "surface", "model", "effort", "account", "label", "caller", "workspace", "tab", "extraArgs", "env", "herdrSocket", "handle", "bg"]) {
209
209
  if (a[k] !== undefined)
210
210
  payload[k] = a[k];
211
211
  }
@@ -268,11 +268,14 @@ function paneSend(a, o = {}) {
268
268
  return rtCommand("pane:send", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
269
269
  }
270
270
  function paneFocus(a, o = {}) {
271
- return rtCommand("pane:focus", { paneId: a.paneId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
271
+ const payload = { paneId: a.paneId };
272
+ if (a.callerWorkspace !== undefined)
273
+ payload.callerWorkspace = a.callerWorkspace;
274
+ return rtCommand("pane:focus", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
272
275
  }
273
276
  function gateOpen(a, o = {}) {
274
277
  const payload = { subject: a.subject, kind: a.kind, questions: a.questions };
275
- for (const k of ["meta", "agent", "pane", "nudge"])
278
+ for (const k of ["meta", "agent", "pane", "nudge", "context", "origin"])
276
279
  if (a[k] !== undefined)
277
280
  payload[k] = a[k];
278
281
  return rtCommand("gate:open", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
@@ -312,8 +315,93 @@ function gateSubscriptions(a, o = {}) {
312
315
  payload[k] = a[k];
313
316
  return rtCommand("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
314
317
  }
318
+ function herdStart(a, o = {}) {
319
+ const payload = { name: a.name, repo: a.repo, session: a.session };
320
+ if (a.hidden !== undefined)
321
+ payload.hidden = a.hidden;
322
+ return rtCommand("herd:start", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
323
+ }
324
+ function herdSpawn(a, o = {}) {
325
+ const payload = { herd: a.herd, job: a.job };
326
+ for (const k of ["brief", "dir", "model", "effort", "account", "disposable"])
327
+ if (a[k] !== undefined)
328
+ payload[k] = a[k];
329
+ return rtCommand("herd:spawn", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
330
+ }
331
+ function herdAsk(a, o = {}) {
332
+ const payload = { herd: a.herd, job: a.job, session: a.session, questions: a.questions };
333
+ for (const k of ["pane", "context"])
334
+ if (a[k] !== undefined)
335
+ payload[k] = a[k];
336
+ return rtCommand("herd:ask", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
337
+ }
338
+ function herdMilestone(a, o = {}) {
339
+ const payload = { herd: a.herd, job: a.job, session: a.session, artifact: a.artifact };
340
+ for (const k of ["pane", "summary"])
341
+ if (a[k] !== undefined)
342
+ payload[k] = a[k];
343
+ return rtCommand("herd:milestone", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
344
+ }
345
+ function herdAnswer(a, o = {}) {
346
+ return rtCommand("herd:answer", { gate: a.gate }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
347
+ }
348
+ function herdReport(a, o = {}) {
349
+ return rtCommand("herd:report", { herd: a.herd, job: a.job, body: a.body }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
350
+ }
351
+ function herdGates(a, o = {}) {
352
+ return rtCommand("herd:gates", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
353
+ }
354
+ function herdStatus(a, o = {}) {
355
+ return rtCommand("herd:status", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
356
+ }
357
+ function herdList(a = {}, o = {}) {
358
+ const payload = {};
359
+ if (a.all !== undefined)
360
+ payload.all = a.all;
361
+ return rtCommand("herd:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
362
+ }
363
+ function herdResume(a, o = {}) {
364
+ return rtCommand("herd:resume", { herd: a.herd, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
365
+ }
366
+ function herdClose(a, o = {}) {
367
+ return rtCommand("herd:close", { herd: a.herd, job: a.job }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
368
+ }
369
+ function herdAttend(a, o = {}) {
370
+ return rtCommand("herd:attend", { herd: a.herd, job: a.job, callerWorkspace: a.callerWorkspace }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
371
+ }
372
+ function herdWrapUp(a, o = {}) {
373
+ const payload = { herd: a.herd };
374
+ for (const k of ["closePanes", "dispose", "deleteJobDirs", "archiveRoom"])
375
+ if (a[k] !== undefined)
376
+ payload[k] = a[k];
377
+ return rtCommand("herd:wrap-up", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 120000 });
378
+ }
379
+ function herdStopHidden(_a, o = {}) {
380
+ return rtCommand("herd:stop-hidden", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
381
+ }
382
+ function bgEnsure(a = {}, o = {}) {
383
+ const payload = {};
384
+ if (a.claim !== undefined)
385
+ payload.claim = a.claim;
386
+ return rtCommand("bg:ensure", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 15000 });
387
+ }
388
+ function bgStatus(o = {}) {
389
+ return rtCommand("bg:status", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
390
+ }
391
+ function bgStop(o = {}) {
392
+ return rtCommand("bg:stop", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
393
+ }
394
+ function bgRelease(a, o = {}) {
395
+ return rtCommand("bg:release", { claim: a.claim }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
396
+ }
315
397
  // src/commands.ts
316
398
  var GATE_BY_PANE = "pane";
399
+ function gateOptionValue(o) {
400
+ return typeof o === "string" ? o : o.value;
401
+ }
402
+ function gateOptionLabel(o) {
403
+ return typeof o === "string" ? o : o.label || o.value;
404
+ }
317
405
  var COMMAND_NAMES = [
318
406
  "project-mrs:read",
319
407
  "discussions:read",
@@ -391,6 +479,20 @@ var COMMAND_NAMES = [
391
479
  "gate:subscribe",
392
480
  "gate:unsubscribe",
393
481
  "gate:subscriptions",
482
+ "herd:start",
483
+ "herd:resume",
484
+ "herd:status",
485
+ "herd:list",
486
+ "herd:close",
487
+ "herd:spawn",
488
+ "herd:gates",
489
+ "herd:ask",
490
+ "herd:milestone",
491
+ "herd:answer",
492
+ "herd:report",
493
+ "herd:attend",
494
+ "herd:stop-hidden",
495
+ "herd:wrap-up",
394
496
  "hooks:repair",
395
497
  "hooks:watch",
396
498
  "sdm:catalog",
@@ -404,7 +506,11 @@ var COMMAND_NAMES = [
404
506
  "worktree:list",
405
507
  "worktree:restore",
406
508
  "worktree:freshen",
407
- "worktree:adopt"
509
+ "worktree:adopt",
510
+ "bg:ensure",
511
+ "bg:status",
512
+ "bg:stop",
513
+ "bg:release"
408
514
  ];
409
515
  // src/relay.ts
410
516
  var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
@@ -581,6 +687,26 @@ async function openSmartPane(herdr, anchorPaneId, opts = {}) {
581
687
  }
582
688
  return { paneId, placement };
583
689
  }
690
+ // src/pane-ref.ts
691
+ var BG_PREFIX = "bg:";
692
+ function parsePaneRef(ref) {
693
+ if (ref.startsWith(BG_PREFIX)) {
694
+ return {
695
+ server: "bg",
696
+ paneId: ref.slice(BG_PREFIX.length)
697
+ };
698
+ }
699
+ return {
700
+ server: "visible",
701
+ paneId: ref
702
+ };
703
+ }
704
+ function formatPaneRef(paneId, server) {
705
+ if (server === "visible") {
706
+ return paneId;
707
+ }
708
+ return BG_PREFIX + paneId;
709
+ }
584
710
  // src/settings/resolve.ts
585
711
  import { homedir as homedir4 } from "os";
586
712
  import { join as join5 } from "path";
@@ -598,6 +724,9 @@ function userSettingsPath() {
598
724
  function teamSettingsPath(team) {
599
725
  return join3(teamsDir(), team, "mattstack", "settings.team.jsonc");
600
726
  }
727
+ function teamLocalPath(team) {
728
+ return join3(home(), ".mattstack", "rt", "teams", `${team}.json`);
729
+ }
601
730
  function machineSettingsPath() {
602
731
  return join3(home(), ".mattstack", "user", "local", machineKey(), "settings.local.jsonc");
603
732
  }
@@ -690,7 +819,7 @@ var REGISTRY = [
690
819
  scopes: ["user"],
691
820
  default: [],
692
821
  merge: "replace",
693
- description: "Event-bus glob rules that raise a desktop notification: [{pattern, category, title, message}]. pattern is matched against the events-bus topic (Bun.Glob semantics); title/message may interpolate `{field}` from the event payload. A fresh key, not an ownership-latch port, so a default is fine here."
822
+ description: "Event-bus glob rules that raise a desktop notification: [{pattern, category, title, message, subjectPrefix?, url?}]. pattern is matched against the events-bus topic (Bun.Glob semantics); title/message may interpolate `{field}` from the event payload, plus the computed `{question}` field (the event payload's first question label, `payload.questions[0].label`, empty string when absent); optional subjectPrefix matches the event payload's subject as a prefix. The optional url is interpolated the same way as title/message and becomes the notification's Open target; a gate rule should set it. A fresh key, not an ownership-latch port, so a default is fine here."
694
823
  },
695
824
  {
696
825
  key: "rt.cron",
@@ -1706,10 +1835,23 @@ function explainSetting(key, opts = {}) {
1706
1835
  return resolveDef(def, readStores(), opts).rows;
1707
1836
  }
1708
1837
  // src/settings/write.ts
1709
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "fs";
1838
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync5, renameSync, unlinkSync, writeFileSync } from "fs";
1710
1839
  import { applyEdits, modify, parseTree } from "jsonc-parser";
1711
1840
  import { randomBytes } from "crypto";
1712
1841
  import { dirname as dirname2 } from "path";
1842
+
1843
+ // src/settings/team-local-read.ts
1844
+ import { readFileSync as readFileSync4 } from "fs";
1845
+ function isJoinedTeam(team) {
1846
+ try {
1847
+ const parsed = JSON.parse(readFileSync4(teamLocalPath(team), "utf8"));
1848
+ return typeof parsed === "object" && parsed !== null && parsed.joinedByRt === true;
1849
+ } catch {
1850
+ return false;
1851
+ }
1852
+ }
1853
+
1854
+ // src/settings/write.ts
1713
1855
  var FORMAT = { tabSize: 2, insertSpaces: true, eol: `
1714
1856
  ` };
1715
1857
  function refuse(message) {
@@ -1767,6 +1909,11 @@ function migratedFalseMessage(key, def) {
1767
1909
  const legacyPart = def.legacyFile ? ` — it is still read from ${def.legacyFile}` : "";
1768
1910
  return `"${key}" is not writable through the settings resolver yet${legacyPart}`;
1769
1911
  }
1912
+ function refuseIfJoined(team) {
1913
+ if (isJoinedTeam(team)) {
1914
+ refuse(`this machine joined "${team}" by invite, so its clone is pull-only and team settings cannot be written here. Ask the team's owner to make this change. Member-proposed changes are tracked in MAT-415.`);
1915
+ }
1916
+ }
1770
1917
  function resolveStorePath(scope, opts) {
1771
1918
  if (scope === "user")
1772
1919
  return userSettingsPath();
@@ -1777,6 +1924,7 @@ function resolveStorePath(scope, opts) {
1777
1924
  if (!existsSync3(path)) {
1778
1925
  refuse(`team store for "${opts.team}" does not exist (${path}) — clone/seed it before writing to it`);
1779
1926
  }
1927
+ refuseIfJoined(opts.team);
1780
1928
  return path;
1781
1929
  }
1782
1930
  const teams = listTeams();
@@ -1786,7 +1934,9 @@ function resolveStorePath(scope, opts) {
1786
1934
  if (teams.length > 1) {
1787
1935
  refuse(`multiple local team stores found (${teams.join(", ")}) — pass opts.team to choose one`);
1788
1936
  }
1789
- return teamSettingsPath(teams[0]);
1937
+ const team = teams[0];
1938
+ refuseIfJoined(team);
1939
+ return teamSettingsPath(team);
1790
1940
  }
1791
1941
  function resolveStorePathForUnset(scope, opts) {
1792
1942
  if (scope === "user")
@@ -1795,7 +1945,10 @@ function resolveStorePathForUnset(scope, opts) {
1795
1945
  return machineSettingsPath();
1796
1946
  if (opts.team !== undefined) {
1797
1947
  const path = teamSettingsPath(opts.team);
1798
- return existsSync3(path) ? path : null;
1948
+ if (!existsSync3(path))
1949
+ return null;
1950
+ refuseIfJoined(opts.team);
1951
+ return path;
1799
1952
  }
1800
1953
  const teams = listTeams();
1801
1954
  if (teams.length === 0)
@@ -1803,7 +1956,9 @@ function resolveStorePathForUnset(scope, opts) {
1803
1956
  if (teams.length > 1) {
1804
1957
  refuse(`multiple local team stores found (${teams.join(", ")}) — pass opts.team to choose one`);
1805
1958
  }
1806
- return teamSettingsPath(teams[0]);
1959
+ const team = teams[0];
1960
+ refuseIfJoined(team);
1961
+ return teamSettingsPath(team);
1807
1962
  }
1808
1963
  function seedHeader() {
1809
1964
  return `// rt settings — created by \`rt settings set\`. JSONC: comments and trailing commas are fine.
@@ -1849,7 +2004,7 @@ function findDuplicateKey(node) {
1849
2004
  function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
1850
2005
  let content;
1851
2006
  if (existsSync3(storePath)) {
1852
- content = readFileSync4(storePath, "utf8");
2007
+ content = readFileSync5(storePath, "utf8");
1853
2008
  if (content.trim() === "") {
1854
2009
  content = seedHeader();
1855
2010
  } else {
@@ -1870,7 +2025,7 @@ function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
1870
2025
  writeTempThenRename(storePath, finalText);
1871
2026
  }
1872
2027
  function removeFromStore(storePath, jsonPath) {
1873
- const content = readFileSync4(storePath, "utf8");
2028
+ const content = readFileSync5(storePath, "utf8");
1874
2029
  if (content.trim() === "")
1875
2030
  return false;
1876
2031
  assertEditableJsonc(storePath, content);
@@ -1897,7 +2052,7 @@ function writeTempThenRename(storePath, finalText) {
1897
2052
  }
1898
2053
  }
1899
2054
  // src/settings/identity.ts
1900
- import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync } from "fs";
2055
+ import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync } from "fs";
1901
2056
 
1902
2057
  // src/settings/exec.ts
1903
2058
  async function runCapture(argv, opts = {}) {
@@ -2059,7 +2214,7 @@ async function resolveNameToIdentity(name, reposJsonPath) {
2059
2214
  if (!existsSync4(reposJsonPath))
2060
2215
  return null;
2061
2216
  try {
2062
- const index = JSON.parse(readFileSync5(reposJsonPath, "utf8"));
2217
+ const index = JSON.parse(readFileSync6(reposJsonPath, "utf8"));
2063
2218
  const path = index[name];
2064
2219
  if (typeof path !== "string")
2065
2220
  return null;
@@ -2084,6 +2239,7 @@ export {
2084
2239
  readMrsByBranch,
2085
2240
  readDiscussions,
2086
2241
  readBranchCache,
2242
+ parsePaneRef,
2087
2243
  parseIdentity,
2088
2244
  paneSpawn,
2089
2245
  paneSend,
@@ -2099,6 +2255,20 @@ export {
2099
2255
  listRuns,
2100
2256
  isMigrated,
2101
2257
  identityFromRemote,
2258
+ herdWrapUp,
2259
+ herdStopHidden,
2260
+ herdStatus,
2261
+ herdStart,
2262
+ herdSpawn,
2263
+ herdResume,
2264
+ herdReport,
2265
+ herdMilestone,
2266
+ herdList,
2267
+ herdGates,
2268
+ herdClose,
2269
+ herdAttend,
2270
+ herdAsk,
2271
+ herdAnswer,
2102
2272
  getSetting,
2103
2273
  getRun,
2104
2274
  getDef,
@@ -2107,10 +2277,13 @@ export {
2107
2277
  gateSubscriptions,
2108
2278
  gateSubscribe,
2109
2279
  gatePark,
2280
+ gateOptionValue,
2281
+ gateOptionLabel,
2110
2282
  gateOpen,
2111
2283
  gateList,
2112
2284
  gateClose,
2113
2285
  gateAnswer,
2286
+ formatPaneRef,
2114
2287
  explainSetting,
2115
2288
  expandVariables,
2116
2289
  eventsWait,
@@ -2142,6 +2315,10 @@ export {
2142
2315
  chatAway,
2143
2316
  chatArchive,
2144
2317
  chatAck,
2318
+ bgStop,
2319
+ bgStatus,
2320
+ bgRelease,
2321
+ bgEnsure,
2145
2322
  allDefs,
2146
2323
  agentStart,
2147
2324
  agentResume,
@@ -2153,5 +2330,6 @@ export {
2153
2330
  GATE_BY_PANE,
2154
2331
  DEFAULT_WS_URL,
2155
2332
  DEFAULT_SOCK,
2156
- COMMAND_NAMES
2333
+ COMMAND_NAMES,
2334
+ BG_PREFIX
2157
2335
  };
@@ -0,0 +1,8 @@
1
+ export declare const BG_PREFIX = "bg:";
2
+ export type PaneServer = "visible" | "bg";
3
+ export interface PaneRef {
4
+ server: PaneServer;
5
+ paneId: string;
6
+ }
7
+ export declare function parsePaneRef(ref: string): PaneRef;
8
+ export declare function formatPaneRef(paneId: string, server: PaneServer): string;
@@ -12,6 +12,14 @@
12
12
  export declare function userSettingsPath(): string;
13
13
  /** ~/.mattstack/teams/<team>/mattstack/settings.team.jsonc — the team store. */
14
14
  export declare function teamSettingsPath(team: string): string;
15
+ /**
16
+ * ~/.mattstack/rt/teams/<team>.json: the machine-local team record. Mirrored
17
+ * from repo-tools/lib/team/team-local.ts's teamLocalPath, which is the
18
+ * authority: rt-client has no dependency on rt's lib/, so this literal is
19
+ * duplicated here rather than imported (same convention as `teamSettingsPath`
20
+ * and `userSettingsPath` above).
21
+ */
22
+ export declare function teamLocalPath(team: string): string;
15
23
  /**
16
24
  * ~/.mattstack/user/local/<machineKey()>/settings.local.jsonc — the machine
17
25
  * store, TRACKED and keyed per machine (path literals legal here only).
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One field of the machine-local team record, for the write guard. The record
3
+ * itself is owned by repo-tools/lib/team/team-local.ts; this reads only what
4
+ * the guard needs and never writes.
5
+ */
6
+ /** Unreadable, absent or malformed all read as false, so nothing that predates the field is refused. */
7
+ export declare function isJoinedTeam(team: string): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.15.1",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/client.ts CHANGED
@@ -356,7 +356,7 @@ export function agentStart(
356
356
  a: Commands["agent:start"]["payload"], o: RtClientOptions = {},
357
357
  ): Promise<RtResponse<AgentRecord>> {
358
358
  const payload: Record<string, unknown> = { repo: a.repo, cwd: a.cwd };
359
- for (const k of ["prompt", "surface", "model", "effort", "account", "label", "caller", "workspace", "tab", "extraArgs"] as const) {
359
+ for (const k of ["prompt", "surface", "model", "effort", "account", "label", "caller", "workspace", "tab", "extraArgs", "env", "herdrSocket", "handle", "bg"] as const) {
360
360
  if (a[k] !== undefined) payload[k] = a[k];
361
361
  }
362
362
  return rtCommand<AgentRecord>("agent:start", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
@@ -441,7 +441,9 @@ export function paneFocus(
441
441
  a: Commands["pane:focus"]["payload"],
442
442
  o: RtClientOptions = {},
443
443
  ): Promise<RtResponse<Commands["pane:focus"]["data"]>> {
444
- return rtCommand<Commands["pane:focus"]["data"]>("pane:focus", { paneId: a.paneId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
444
+ const payload: Record<string, unknown> = { paneId: a.paneId };
445
+ if (a.callerWorkspace !== undefined) payload.callerWorkspace = a.callerWorkspace;
446
+ return rtCommand<Commands["pane:focus"]["data"]>("pane:focus", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
445
447
  }
446
448
 
447
449
  // ─── Gates (BOARD-20/21 gate facility) ─────────────────────────────────────
@@ -451,7 +453,7 @@ export function gateOpen(
451
453
  o: RtClientOptions = {},
452
454
  ): Promise<RtResponse<Commands["gate:open"]["data"]>> {
453
455
  const payload: Record<string, unknown> = { subject: a.subject, kind: a.kind, questions: a.questions };
454
- for (const k of ["meta", "agent", "pane", "nudge"] as const) if (a[k] !== undefined) payload[k] = a[k];
456
+ for (const k of ["meta", "agent", "pane", "nudge", "context", "origin"] as const) if (a[k] !== undefined) payload[k] = a[k];
455
457
  return rtCommand<Commands["gate:open"]["data"]>("gate:open", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
456
458
  }
457
459
 
@@ -519,3 +521,153 @@ export function gateSubscriptions(
519
521
  for (const k of ["session", "live"] as const) if (a[k] !== undefined) payload[k] = a[k];
520
522
  return rtCommand<Commands["gate:subscriptions"]["data"]>("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
521
523
  }
524
+
525
+ // ─── Herd (shepherd run registry) ───────────────────────────────────────────
526
+
527
+ /** Provisions the room, workspace and subscription before it answers, so it
528
+ gets the same 60s budget as a spawn rather than the 10s default. */
529
+ export function herdStart(
530
+ a: Commands["herd:start"]["payload"],
531
+ o: RtClientOptions = {},
532
+ ): Promise<RtResponse<Commands["herd:start"]["data"]>> {
533
+ const payload: Record<string, unknown> = { name: a.name, repo: a.repo, session: a.session };
534
+ if (a.hidden !== undefined) payload.hidden = a.hidden;
535
+ return rtCommand<Commands["herd:start"]["data"]>("herd:start", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60_000 });
536
+ }
537
+
538
+ /** Worktree provision plus agent launch; the 10s default cannot cover it. */
539
+ export function herdSpawn(
540
+ a: Commands["herd:spawn"]["payload"],
541
+ o: RtClientOptions = {},
542
+ ): Promise<RtResponse<Commands["herd:spawn"]["data"]>> {
543
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job };
544
+ for (const k of ["brief", "dir", "model", "effort", "account", "disposable"] as const) if (a[k] !== undefined) payload[k] = a[k];
545
+ return rtCommand<Commands["herd:spawn"]["data"]>("herd:spawn", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60_000 });
546
+ }
547
+
548
+ export function herdAsk(
549
+ a: Commands["herd:ask"]["payload"],
550
+ o: RtClientOptions = {},
551
+ ): Promise<RtResponse<Commands["herd:ask"]["data"]>> {
552
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job, session: a.session, questions: a.questions };
553
+ for (const k of ["pane", "context"] as const) if (a[k] !== undefined) payload[k] = a[k];
554
+ return rtCommand<Commands["herd:ask"]["data"]>("herd:ask", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
555
+ }
556
+
557
+ export function herdMilestone(
558
+ a: Commands["herd:milestone"]["payload"],
559
+ o: RtClientOptions = {},
560
+ ): Promise<RtResponse<Commands["herd:milestone"]["data"]>> {
561
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job, session: a.session, artifact: a.artifact };
562
+ for (const k of ["pane", "summary"] as const) if (a[k] !== undefined) payload[k] = a[k];
563
+ return rtCommand<Commands["herd:milestone"]["data"]>("herd:milestone", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
564
+ }
565
+
566
+ export function herdAnswer(
567
+ a: Commands["herd:answer"]["payload"],
568
+ o: RtClientOptions = {},
569
+ ): Promise<RtResponse<Commands["herd:answer"]["data"]>> {
570
+ return rtCommand<Commands["herd:answer"]["data"]>("herd:answer", { gate: a.gate }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
571
+ }
572
+
573
+ /** A disposable job's report also closes its pane, one herdr CLI call under the runner's own 15s budget. */
574
+ export function herdReport(
575
+ a: Commands["herd:report"]["payload"],
576
+ o: RtClientOptions = {},
577
+ ): Promise<RtResponse<Commands["herd:report"]["data"]>> {
578
+ return rtCommand<Commands["herd:report"]["data"]>("herd:report", { herd: a.herd, job: a.job, body: a.body }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
579
+ }
580
+
581
+ export function herdGates(
582
+ a: Commands["herd:gates"]["payload"],
583
+ o: RtClientOptions = {},
584
+ ): Promise<RtResponse<Commands["herd:gates"]["data"]>> {
585
+ return rtCommand<Commands["herd:gates"]["data"]>("herd:gates", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
586
+ }
587
+
588
+ export function herdStatus(
589
+ a: Commands["herd:status"]["payload"],
590
+ o: RtClientOptions = {},
591
+ ): Promise<RtResponse<Commands["herd:status"]["data"]>> {
592
+ return rtCommand<Commands["herd:status"]["data"]>("herd:status", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
593
+ }
594
+
595
+ export function herdList(
596
+ a: Commands["herd:list"]["payload"] = {},
597
+ o: RtClientOptions = {},
598
+ ): Promise<RtResponse<Commands["herd:list"]["data"]>> {
599
+ const payload: Record<string, unknown> = {};
600
+ if (a.all !== undefined) payload.all = a.all;
601
+ return rtCommand<Commands["herd:list"]["data"]>("herd:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
602
+ }
603
+
604
+ export function herdResume(
605
+ a: Commands["herd:resume"]["payload"],
606
+ o: RtClientOptions = {},
607
+ ): Promise<RtResponse<Commands["herd:resume"]["data"]>> {
608
+ return rtCommand<Commands["herd:resume"]["data"]>("herd:resume", { herd: a.herd, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
609
+ }
610
+
611
+ /** Closes a herdr pane, one CLI call under the runner's own 15s budget. */
612
+ export function herdClose(
613
+ a: Commands["herd:close"]["payload"],
614
+ o: RtClientOptions = {},
615
+ ): Promise<RtResponse<Commands["herd:close"]["data"]>> {
616
+ return rtCommand<Commands["herd:close"]["data"]>("herd:close", { herd: a.herd, job: a.job }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
617
+ }
618
+
619
+ /** Three sequential herdr CLI calls (pane get, tab create, pane run), each under the runner's own 15s budget. */
620
+ export function herdAttend(
621
+ a: Commands["herd:attend"]["payload"],
622
+ o: RtClientOptions = {},
623
+ ): Promise<RtResponse<Commands["herd:attend"]["data"]>> {
624
+ return rtCommand<Commands["herd:attend"]["data"]>("herd:attend", { herd: a.herd, job: a.job, callerWorkspace: a.callerWorkspace }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60_000 });
625
+ }
626
+
627
+ /** Closes panes, disposes worktrees and archives the room in one pass; the
628
+ worktree disposals alone can outrun a spawn's budget. */
629
+ export function herdWrapUp(
630
+ a: Commands["herd:wrap-up"]["payload"],
631
+ o: RtClientOptions = {},
632
+ ): Promise<RtResponse<Commands["herd:wrap-up"]["data"]>> {
633
+ const payload: Record<string, unknown> = { herd: a.herd };
634
+ for (const k of ["closePanes", "dispose", "deleteJobDirs", "archiveRoom"] as const) if (a[k] !== undefined) payload[k] = a[k];
635
+ return rtCommand<Commands["herd:wrap-up"]["data"]>("herd:wrap-up", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 120_000 });
636
+ }
637
+
638
+ /** Runs `herdr session stop`, one CLI call under the runner's own 15s budget. */
639
+ export function herdStopHidden(
640
+ _a: Commands["herd:stop-hidden"]["payload"],
641
+ o: RtClientOptions = {},
642
+ ): Promise<RtResponse<Commands["herd:stop-hidden"]["data"]>> {
643
+ return rtCommand<Commands["herd:stop-hidden"]["data"]>("herd:stop-hidden", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
644
+ }
645
+
646
+ // ─── Background server (daemon-owned background herdr session) ────────────
647
+
648
+ /** May spawn `herdr server` and wait for it to bind; budget matches bg-service's own 10s readyTimeoutMs plus margin. */
649
+ export function bgEnsure(
650
+ a: Commands["bg:ensure"]["payload"] = {},
651
+ o: RtClientOptions = {},
652
+ ): Promise<RtResponse<Commands["bg:ensure"]["data"]>> {
653
+ const payload: Record<string, unknown> = {};
654
+ if (a.claim !== undefined) payload.claim = a.claim;
655
+ return rtCommand<Commands["bg:ensure"]["data"]>("bg:ensure", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 15_000 });
656
+ }
657
+
658
+ /** Never ensures/spawns; a plain read of the current state. */
659
+ export function bgStatus(o: RtClientOptions = {}): Promise<RtResponse<Commands["bg:status"]["data"]>> {
660
+ return rtCommand<Commands["bg:status"]["data"]>("bg:status", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
661
+ }
662
+
663
+ /** Asks the daemon to stop the background server; refuses (ok:false) while any claim is live, naming the owners. */
664
+ export function bgStop(o: RtClientOptions = {}): Promise<RtResponse<Commands["bg:stop"]["data"]>> {
665
+ return rtCommand<Commands["bg:stop"]["data"]>("bg:stop", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
666
+ }
667
+
668
+ export function bgRelease(
669
+ a: Commands["bg:release"]["payload"],
670
+ o: RtClientOptions = {},
671
+ ): Promise<RtResponse<Commands["bg:release"]["data"]>> {
672
+ return rtCommand<Commands["bg:release"]["data"]>("bg:release", { claim: a.claim }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
673
+ }