@mattstack/rt-client 0.16.0 → 0.19.1

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,7 +268,10 @@ 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 };
@@ -278,7 +281,11 @@ function gateOpen(a, o = {}) {
278
281
  return rtCommand("gate:open", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
279
282
  }
280
283
  function gateAnswer(a, o = {}) {
281
- return rtCommand("gate:answer", { id: a.id, answers: a.answers, by: a.by }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
284
+ const payload = { id: a.id, answers: a.answers, by: a.by };
285
+ for (const k of ["session", "override"])
286
+ if (a[k] !== undefined)
287
+ payload[k] = a[k];
288
+ return rtCommand("gate:answer", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
282
289
  }
283
290
  function gateWait(a, o = {}) {
284
291
  const payload = { id: a.id };
@@ -300,7 +307,11 @@ function gateClose(a, o = {}) {
300
307
  return rtCommand("gate:close", { id: a.id, reason: a.reason }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
301
308
  }
302
309
  function gateSubscribe(a, o = {}) {
303
- return rtCommand("gate:subscribe", { subjectPrefix: a.subjectPrefix, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
310
+ const payload = { subjectPrefix: a.subjectPrefix, session: a.session };
311
+ for (const k of ["scope", "ownerRef"])
312
+ if (a[k] !== undefined)
313
+ payload[k] = a[k];
314
+ return rtCommand("gate:subscribe", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
304
315
  }
305
316
  function gateUnsubscribe(a, o = {}) {
306
317
  return rtCommand("gate:unsubscribe", { id: a.id }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
@@ -312,6 +323,85 @@ function gateSubscriptions(a, o = {}) {
312
323
  payload[k] = a[k];
313
324
  return rtCommand("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
314
325
  }
326
+ function herdStart(a, o = {}) {
327
+ const payload = { name: a.name, repo: a.repo, session: a.session };
328
+ if (a.hidden !== undefined)
329
+ payload.hidden = a.hidden;
330
+ return rtCommand("herd:start", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
331
+ }
332
+ function herdSpawn(a, o = {}) {
333
+ const payload = { herd: a.herd, job: a.job };
334
+ for (const k of ["brief", "dir", "model", "effort", "account", "disposable"])
335
+ if (a[k] !== undefined)
336
+ payload[k] = a[k];
337
+ return rtCommand("herd:spawn", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
338
+ }
339
+ function herdAsk(a, o = {}) {
340
+ const payload = { herd: a.herd, job: a.job, session: a.session, questions: a.questions };
341
+ for (const k of ["pane", "context"])
342
+ if (a[k] !== undefined)
343
+ payload[k] = a[k];
344
+ return rtCommand("herd:ask", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
345
+ }
346
+ function herdMilestone(a, o = {}) {
347
+ const payload = { herd: a.herd, job: a.job, session: a.session, artifact: a.artifact };
348
+ for (const k of ["pane", "summary"])
349
+ if (a[k] !== undefined)
350
+ payload[k] = a[k];
351
+ return rtCommand("herd:milestone", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
352
+ }
353
+ function herdAnswer(a, o = {}) {
354
+ return rtCommand("herd:answer", { gate: a.gate }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
355
+ }
356
+ function herdReport(a, o = {}) {
357
+ return rtCommand("herd:report", { herd: a.herd, job: a.job, body: a.body }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
358
+ }
359
+ function herdGates(a, o = {}) {
360
+ return rtCommand("herd:gates", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
361
+ }
362
+ function herdStatus(a, o = {}) {
363
+ return rtCommand("herd:status", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
364
+ }
365
+ function herdList(a = {}, o = {}) {
366
+ const payload = {};
367
+ if (a.all !== undefined)
368
+ payload.all = a.all;
369
+ return rtCommand("herd:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
370
+ }
371
+ function herdResume(a, o = {}) {
372
+ return rtCommand("herd:resume", { herd: a.herd, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
373
+ }
374
+ function herdClose(a, o = {}) {
375
+ return rtCommand("herd:close", { herd: a.herd, job: a.job }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
376
+ }
377
+ function herdAttend(a, o = {}) {
378
+ return rtCommand("herd:attend", { herd: a.herd, job: a.job, callerWorkspace: a.callerWorkspace }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60000 });
379
+ }
380
+ function herdWrapUp(a, o = {}) {
381
+ const payload = { herd: a.herd };
382
+ for (const k of ["closePanes", "dispose", "deleteJobDirs", "archiveRoom"])
383
+ if (a[k] !== undefined)
384
+ payload[k] = a[k];
385
+ return rtCommand("herd:wrap-up", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 120000 });
386
+ }
387
+ function herdStopHidden(_a, o = {}) {
388
+ return rtCommand("herd:stop-hidden", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
389
+ }
390
+ function bgEnsure(a = {}, o = {}) {
391
+ const payload = {};
392
+ if (a.claim !== undefined)
393
+ payload.claim = a.claim;
394
+ return rtCommand("bg:ensure", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
395
+ }
396
+ function bgStatus(o = {}) {
397
+ return rtCommand("bg:status", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
398
+ }
399
+ function bgStop(o = {}) {
400
+ return rtCommand("bg:stop", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30000 });
401
+ }
402
+ function bgRelease(a, o = {}) {
403
+ return rtCommand("bg:release", { claim: a.claim }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
404
+ }
315
405
  // src/commands.ts
316
406
  var GATE_BY_PANE = "pane";
317
407
  function gateOptionValue(o) {
@@ -397,6 +487,20 @@ var COMMAND_NAMES = [
397
487
  "gate:subscribe",
398
488
  "gate:unsubscribe",
399
489
  "gate:subscriptions",
490
+ "herd:start",
491
+ "herd:resume",
492
+ "herd:status",
493
+ "herd:list",
494
+ "herd:close",
495
+ "herd:spawn",
496
+ "herd:gates",
497
+ "herd:ask",
498
+ "herd:milestone",
499
+ "herd:answer",
500
+ "herd:report",
501
+ "herd:attend",
502
+ "herd:stop-hidden",
503
+ "herd:wrap-up",
400
504
  "hooks:repair",
401
505
  "hooks:watch",
402
506
  "sdm:catalog",
@@ -410,7 +514,11 @@ var COMMAND_NAMES = [
410
514
  "worktree:list",
411
515
  "worktree:restore",
412
516
  "worktree:freshen",
413
- "worktree:adopt"
517
+ "worktree:adopt",
518
+ "bg:ensure",
519
+ "bg:status",
520
+ "bg:stop",
521
+ "bg:release"
414
522
  ];
415
523
  // src/relay.ts
416
524
  var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
@@ -587,6 +695,29 @@ async function openSmartPane(herdr, anchorPaneId, opts = {}) {
587
695
  }
588
696
  return { paneId, placement };
589
697
  }
698
+ // src/pane-ref.ts
699
+ var BG_PREFIX = "bg:";
700
+ function parsePaneRef(ref) {
701
+ if (ref.startsWith(BG_PREFIX)) {
702
+ return {
703
+ server: "bg",
704
+ paneId: ref.slice(BG_PREFIX.length)
705
+ };
706
+ }
707
+ return {
708
+ server: "visible",
709
+ paneId: ref
710
+ };
711
+ }
712
+ function formatPaneRef(paneId, server) {
713
+ if (server === "visible") {
714
+ return paneId;
715
+ }
716
+ if (paneId.startsWith(BG_PREFIX)) {
717
+ return paneId;
718
+ }
719
+ return BG_PREFIX + paneId;
720
+ }
590
721
  // src/settings/resolve.ts
591
722
  import { homedir as homedir4 } from "os";
592
723
  import { join as join5 } from "path";
@@ -604,6 +735,9 @@ function userSettingsPath() {
604
735
  function teamSettingsPath(team) {
605
736
  return join3(teamsDir(), team, "mattstack", "settings.team.jsonc");
606
737
  }
738
+ function teamLocalPath(team) {
739
+ return join3(home(), ".mattstack", "rt", "teams", `${team}.json`);
740
+ }
607
741
  function machineSettingsPath() {
608
742
  return join3(home(), ".mattstack", "user", "local", machineKey(), "settings.local.jsonc");
609
743
  }
@@ -654,7 +788,7 @@ var REGISTRY = [
654
788
  merge: "deep",
655
789
  repoScoped: true,
656
790
  migrated: true,
657
- description: "Per-repo worktree pool config (onDeck size, ready steps, name pool); root/branchFormat/ready computed-or-empty in the reader."
791
+ description: "Per-repo worktree pool config (onDeck size, ready steps, name pool, staleClaimDays); root/branchFormat/ready computed-or-empty in the reader."
658
792
  },
659
793
  {
660
794
  key: "rt.worktreeReadyApproval",
@@ -696,7 +830,7 @@ var REGISTRY = [
696
830
  scopes: ["user"],
697
831
  default: [],
698
832
  merge: "replace",
699
- 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."
833
+ description: 'Event-bus glob rules that raise a desktop notification: [{pattern, category, title, message, subjectPrefix?, url?, owner?}]. 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. The optional owner field (only literal "human" is valid) suppresses events whose payload.owner starts with "herd:", allowing gate rules to skip herd-owned events. A typical setup pairs a gate/opened rule with owner: "human" (human-owned gates notify) and a gate/escalated rule without owner (all escalations notify, whether human or herd). A fresh key, not an ownership-latch port, so a default is fine here.'
700
834
  },
701
835
  {
702
836
  key: "rt.cron",
@@ -1015,7 +1149,7 @@ var REGISTRY = [
1015
1149
  type: "array",
1016
1150
  scopes: ["team"],
1017
1151
  merge: "replace",
1018
- description: "Board tabs ({id, label, source, slackChannel?, reviewSkill?}), editable from the board's settings modal. source.kind 'authors' is the classic roster board; 'codeowners' lists MRs from any author blocked on an unapproved CODEOWNERS section. Absent = one implicit authors tab (fallback lives in the board reader, never here)."
1152
+ description: "Board tabs ({id, label, source, slackChannel?, reviewSkill?}), editable from the board's settings modal. source.kind 'authors' is the classic roster board; 'codeowners' lists any author's open MRs carrying a CODE_OWNER rule for the section, until merge/close. Absent = one implicit authors tab (fallback lives in the board reader, never here)."
1019
1153
  },
1020
1154
  {
1021
1155
  key: "board.staleAfterDays",
@@ -1258,6 +1392,14 @@ var REGISTRY = [
1258
1392
  scopes: ["user", "machine"],
1259
1393
  merge: "replace",
1260
1394
  description: "Opaque extra claude arguments appended to every rt agent launch (escape hatch)."
1395
+ },
1396
+ {
1397
+ key: "rt.gates.escalationTtlMinutes",
1398
+ type: "number",
1399
+ scopes: ["user"],
1400
+ default: 10,
1401
+ merge: "replace",
1402
+ description: `Minutes an open herd-owned gate waits before the escalation sweep surfaces it to the human (topic gate/escalated/<id>). Fires on either trigger: the TTL elapses (reason "ttl"), or the owning herd's shepherd subscription is gone or dead before the TTL (reason "owner-dead"). 0 escalates any eligible gate on the first sweep after it opens. A fresh key, not an ownership-latch port, so a default is fine here.`
1261
1403
  }
1262
1404
  ];
1263
1405
 
@@ -1712,10 +1854,23 @@ function explainSetting(key, opts = {}) {
1712
1854
  return resolveDef(def, readStores(), opts).rows;
1713
1855
  }
1714
1856
  // src/settings/write.ts
1715
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "fs";
1857
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync5, renameSync, unlinkSync, writeFileSync } from "fs";
1716
1858
  import { applyEdits, modify, parseTree } from "jsonc-parser";
1717
1859
  import { randomBytes } from "crypto";
1718
1860
  import { dirname as dirname2 } from "path";
1861
+
1862
+ // src/settings/team-local-read.ts
1863
+ import { readFileSync as readFileSync4 } from "fs";
1864
+ function isJoinedTeam(team) {
1865
+ try {
1866
+ const parsed = JSON.parse(readFileSync4(teamLocalPath(team), "utf8"));
1867
+ return typeof parsed === "object" && parsed !== null && parsed.joinedByRt === true;
1868
+ } catch {
1869
+ return false;
1870
+ }
1871
+ }
1872
+
1873
+ // src/settings/write.ts
1719
1874
  var FORMAT = { tabSize: 2, insertSpaces: true, eol: `
1720
1875
  ` };
1721
1876
  function refuse(message) {
@@ -1773,6 +1928,11 @@ function migratedFalseMessage(key, def) {
1773
1928
  const legacyPart = def.legacyFile ? ` — it is still read from ${def.legacyFile}` : "";
1774
1929
  return `"${key}" is not writable through the settings resolver yet${legacyPart}`;
1775
1930
  }
1931
+ function refuseIfJoined(team) {
1932
+ if (isJoinedTeam(team)) {
1933
+ 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.`);
1934
+ }
1935
+ }
1776
1936
  function resolveStorePath(scope, opts) {
1777
1937
  if (scope === "user")
1778
1938
  return userSettingsPath();
@@ -1783,6 +1943,7 @@ function resolveStorePath(scope, opts) {
1783
1943
  if (!existsSync3(path)) {
1784
1944
  refuse(`team store for "${opts.team}" does not exist (${path}) — clone/seed it before writing to it`);
1785
1945
  }
1946
+ refuseIfJoined(opts.team);
1786
1947
  return path;
1787
1948
  }
1788
1949
  const teams = listTeams();
@@ -1792,7 +1953,9 @@ function resolveStorePath(scope, opts) {
1792
1953
  if (teams.length > 1) {
1793
1954
  refuse(`multiple local team stores found (${teams.join(", ")}) — pass opts.team to choose one`);
1794
1955
  }
1795
- return teamSettingsPath(teams[0]);
1956
+ const team = teams[0];
1957
+ refuseIfJoined(team);
1958
+ return teamSettingsPath(team);
1796
1959
  }
1797
1960
  function resolveStorePathForUnset(scope, opts) {
1798
1961
  if (scope === "user")
@@ -1801,7 +1964,10 @@ function resolveStorePathForUnset(scope, opts) {
1801
1964
  return machineSettingsPath();
1802
1965
  if (opts.team !== undefined) {
1803
1966
  const path = teamSettingsPath(opts.team);
1804
- return existsSync3(path) ? path : null;
1967
+ if (!existsSync3(path))
1968
+ return null;
1969
+ refuseIfJoined(opts.team);
1970
+ return path;
1805
1971
  }
1806
1972
  const teams = listTeams();
1807
1973
  if (teams.length === 0)
@@ -1809,7 +1975,9 @@ function resolveStorePathForUnset(scope, opts) {
1809
1975
  if (teams.length > 1) {
1810
1976
  refuse(`multiple local team stores found (${teams.join(", ")}) — pass opts.team to choose one`);
1811
1977
  }
1812
- return teamSettingsPath(teams[0]);
1978
+ const team = teams[0];
1979
+ refuseIfJoined(team);
1980
+ return teamSettingsPath(team);
1813
1981
  }
1814
1982
  function seedHeader() {
1815
1983
  return `// rt settings — created by \`rt settings set\`. JSONC: comments and trailing commas are fine.
@@ -1855,7 +2023,7 @@ function findDuplicateKey(node) {
1855
2023
  function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
1856
2024
  let content;
1857
2025
  if (existsSync3(storePath)) {
1858
- content = readFileSync4(storePath, "utf8");
2026
+ content = readFileSync5(storePath, "utf8");
1859
2027
  if (content.trim() === "") {
1860
2028
  content = seedHeader();
1861
2029
  } else {
@@ -1876,7 +2044,7 @@ function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
1876
2044
  writeTempThenRename(storePath, finalText);
1877
2045
  }
1878
2046
  function removeFromStore(storePath, jsonPath) {
1879
- const content = readFileSync4(storePath, "utf8");
2047
+ const content = readFileSync5(storePath, "utf8");
1880
2048
  if (content.trim() === "")
1881
2049
  return false;
1882
2050
  assertEditableJsonc(storePath, content);
@@ -1903,7 +2071,7 @@ function writeTempThenRename(storePath, finalText) {
1903
2071
  }
1904
2072
  }
1905
2073
  // src/settings/identity.ts
1906
- import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync } from "fs";
2074
+ import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync } from "fs";
1907
2075
 
1908
2076
  // src/settings/exec.ts
1909
2077
  async function runCapture(argv, opts = {}) {
@@ -2065,7 +2233,7 @@ async function resolveNameToIdentity(name, reposJsonPath) {
2065
2233
  if (!existsSync4(reposJsonPath))
2066
2234
  return null;
2067
2235
  try {
2068
- const index = JSON.parse(readFileSync5(reposJsonPath, "utf8"));
2236
+ const index = JSON.parse(readFileSync6(reposJsonPath, "utf8"));
2069
2237
  const path = index[name];
2070
2238
  if (typeof path !== "string")
2071
2239
  return null;
@@ -2090,6 +2258,7 @@ export {
2090
2258
  readMrsByBranch,
2091
2259
  readDiscussions,
2092
2260
  readBranchCache,
2261
+ parsePaneRef,
2093
2262
  parseIdentity,
2094
2263
  paneSpawn,
2095
2264
  paneSend,
@@ -2105,6 +2274,20 @@ export {
2105
2274
  listRuns,
2106
2275
  isMigrated,
2107
2276
  identityFromRemote,
2277
+ herdWrapUp,
2278
+ herdStopHidden,
2279
+ herdStatus,
2280
+ herdStart,
2281
+ herdSpawn,
2282
+ herdResume,
2283
+ herdReport,
2284
+ herdMilestone,
2285
+ herdList,
2286
+ herdGates,
2287
+ herdClose,
2288
+ herdAttend,
2289
+ herdAsk,
2290
+ herdAnswer,
2108
2291
  getSetting,
2109
2292
  getRun,
2110
2293
  getDef,
@@ -2119,6 +2302,7 @@ export {
2119
2302
  gateList,
2120
2303
  gateClose,
2121
2304
  gateAnswer,
2305
+ formatPaneRef,
2122
2306
  explainSetting,
2123
2307
  expandVariables,
2124
2308
  eventsWait,
@@ -2150,6 +2334,10 @@ export {
2150
2334
  chatAway,
2151
2335
  chatArchive,
2152
2336
  chatAck,
2337
+ bgStop,
2338
+ bgStatus,
2339
+ bgRelease,
2340
+ bgEnsure,
2153
2341
  allDefs,
2154
2342
  agentStart,
2155
2343
  agentResume,
@@ -2161,5 +2349,6 @@ export {
2161
2349
  GATE_BY_PANE,
2162
2350
  DEFAULT_WS_URL,
2163
2351
  DEFAULT_SOCK,
2164
- COMMAND_NAMES
2352
+ COMMAND_NAMES,
2353
+ BG_PREFIX
2165
2354
  };
@@ -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.16.0",
3
+ "version": "0.19.1",
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) ─────────────────────────────────────
@@ -459,7 +461,9 @@ export function gateAnswer(
459
461
  a: Commands["gate:answer"]["payload"],
460
462
  o: RtClientOptions = {},
461
463
  ): Promise<RtResponse<Commands["gate:answer"]["data"]>> {
462
- return rtCommand<Commands["gate:answer"]["data"]>("gate:answer", { id: a.id, answers: a.answers, by: a.by }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
464
+ const payload: Record<string, unknown> = { id: a.id, answers: a.answers, by: a.by };
465
+ for (const k of ["session", "override"] as const) if (a[k] !== undefined) payload[k] = a[k];
466
+ return rtCommand<Commands["gate:answer"]["data"]>("gate:answer", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
463
467
  }
464
468
 
465
469
  /** Daemon clamps its own wait to 240s (gates-store.ts); the client abort
@@ -501,7 +505,9 @@ export function gateSubscribe(
501
505
  a: Commands["gate:subscribe"]["payload"],
502
506
  o: RtClientOptions = {},
503
507
  ): Promise<RtResponse<Commands["gate:subscribe"]["data"]>> {
504
- return rtCommand<Commands["gate:subscribe"]["data"]>("gate:subscribe", { subjectPrefix: a.subjectPrefix, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
508
+ const payload: Record<string, unknown> = { subjectPrefix: a.subjectPrefix, session: a.session };
509
+ for (const k of ["scope", "ownerRef"] as const) if (a[k] !== undefined) payload[k] = a[k];
510
+ return rtCommand<Commands["gate:subscribe"]["data"]>("gate:subscribe", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
505
511
  }
506
512
 
507
513
  export function gateUnsubscribe(
@@ -519,3 +525,156 @@ export function gateSubscriptions(
519
525
  for (const k of ["session", "live"] as const) if (a[k] !== undefined) payload[k] = a[k];
520
526
  return rtCommand<Commands["gate:subscriptions"]["data"]>("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
521
527
  }
528
+
529
+ // ─── Herd (shepherd run registry) ───────────────────────────────────────────
530
+
531
+ /** Provisions the room, workspace and subscription before it answers, so it
532
+ gets the same 60s budget as a spawn rather than the 10s default. */
533
+ export function herdStart(
534
+ a: Commands["herd:start"]["payload"],
535
+ o: RtClientOptions = {},
536
+ ): Promise<RtResponse<Commands["herd:start"]["data"]>> {
537
+ const payload: Record<string, unknown> = { name: a.name, repo: a.repo, session: a.session };
538
+ if (a.hidden !== undefined) payload.hidden = a.hidden;
539
+ return rtCommand<Commands["herd:start"]["data"]>("herd:start", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60_000 });
540
+ }
541
+
542
+ /** Worktree provision plus agent launch; the 10s default cannot cover it. */
543
+ export function herdSpawn(
544
+ a: Commands["herd:spawn"]["payload"],
545
+ o: RtClientOptions = {},
546
+ ): Promise<RtResponse<Commands["herd:spawn"]["data"]>> {
547
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job };
548
+ for (const k of ["brief", "dir", "model", "effort", "account", "disposable"] as const) if (a[k] !== undefined) payload[k] = a[k];
549
+ return rtCommand<Commands["herd:spawn"]["data"]>("herd:spawn", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 60_000 });
550
+ }
551
+
552
+ export function herdAsk(
553
+ a: Commands["herd:ask"]["payload"],
554
+ o: RtClientOptions = {},
555
+ ): Promise<RtResponse<Commands["herd:ask"]["data"]>> {
556
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job, session: a.session, questions: a.questions };
557
+ for (const k of ["pane", "context"] as const) if (a[k] !== undefined) payload[k] = a[k];
558
+ return rtCommand<Commands["herd:ask"]["data"]>("herd:ask", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
559
+ }
560
+
561
+ export function herdMilestone(
562
+ a: Commands["herd:milestone"]["payload"],
563
+ o: RtClientOptions = {},
564
+ ): Promise<RtResponse<Commands["herd:milestone"]["data"]>> {
565
+ const payload: Record<string, unknown> = { herd: a.herd, job: a.job, session: a.session, artifact: a.artifact };
566
+ for (const k of ["pane", "summary"] as const) if (a[k] !== undefined) payload[k] = a[k];
567
+ return rtCommand<Commands["herd:milestone"]["data"]>("herd:milestone", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
568
+ }
569
+
570
+ export function herdAnswer(
571
+ a: Commands["herd:answer"]["payload"],
572
+ o: RtClientOptions = {},
573
+ ): Promise<RtResponse<Commands["herd:answer"]["data"]>> {
574
+ return rtCommand<Commands["herd:answer"]["data"]>("herd:answer", { gate: a.gate }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
575
+ }
576
+
577
+ /** A disposable job's report also closes its pane, one herdr CLI call under the runner's own 15s budget. */
578
+ export function herdReport(
579
+ a: Commands["herd:report"]["payload"],
580
+ o: RtClientOptions = {},
581
+ ): Promise<RtResponse<Commands["herd:report"]["data"]>> {
582
+ 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 });
583
+ }
584
+
585
+ export function herdGates(
586
+ a: Commands["herd:gates"]["payload"],
587
+ o: RtClientOptions = {},
588
+ ): Promise<RtResponse<Commands["herd:gates"]["data"]>> {
589
+ return rtCommand<Commands["herd:gates"]["data"]>("herd:gates", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
590
+ }
591
+
592
+ export function herdStatus(
593
+ a: Commands["herd:status"]["payload"],
594
+ o: RtClientOptions = {},
595
+ ): Promise<RtResponse<Commands["herd:status"]["data"]>> {
596
+ return rtCommand<Commands["herd:status"]["data"]>("herd:status", { herd: a.herd }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
597
+ }
598
+
599
+ export function herdList(
600
+ a: Commands["herd:list"]["payload"] = {},
601
+ o: RtClientOptions = {},
602
+ ): Promise<RtResponse<Commands["herd:list"]["data"]>> {
603
+ const payload: Record<string, unknown> = {};
604
+ if (a.all !== undefined) payload.all = a.all;
605
+ return rtCommand<Commands["herd:list"]["data"]>("herd:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
606
+ }
607
+
608
+ export function herdResume(
609
+ a: Commands["herd:resume"]["payload"],
610
+ o: RtClientOptions = {},
611
+ ): Promise<RtResponse<Commands["herd:resume"]["data"]>> {
612
+ return rtCommand<Commands["herd:resume"]["data"]>("herd:resume", { herd: a.herd, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
613
+ }
614
+
615
+ /** Closes a herdr pane, one CLI call under the runner's own 15s budget. */
616
+ export function herdClose(
617
+ a: Commands["herd:close"]["payload"],
618
+ o: RtClientOptions = {},
619
+ ): Promise<RtResponse<Commands["herd:close"]["data"]>> {
620
+ return rtCommand<Commands["herd:close"]["data"]>("herd:close", { herd: a.herd, job: a.job }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
621
+ }
622
+
623
+ /** Three sequential herdr CLI calls (pane get, tab create, pane run), each under the runner's own 15s budget. */
624
+ export function herdAttend(
625
+ a: Commands["herd:attend"]["payload"],
626
+ o: RtClientOptions = {},
627
+ ): Promise<RtResponse<Commands["herd:attend"]["data"]>> {
628
+ 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 });
629
+ }
630
+
631
+ /** Closes panes, disposes worktrees and archives the room in one pass; the
632
+ worktree disposals alone can outrun a spawn's budget. */
633
+ export function herdWrapUp(
634
+ a: Commands["herd:wrap-up"]["payload"],
635
+ o: RtClientOptions = {},
636
+ ): Promise<RtResponse<Commands["herd:wrap-up"]["data"]>> {
637
+ const payload: Record<string, unknown> = { herd: a.herd };
638
+ for (const k of ["closePanes", "dispose", "deleteJobDirs", "archiveRoom"] as const) if (a[k] !== undefined) payload[k] = a[k];
639
+ return rtCommand<Commands["herd:wrap-up"]["data"]>("herd:wrap-up", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 120_000 });
640
+ }
641
+
642
+ /** Runs `herdr session stop`, one CLI call under the runner's own 15s budget. */
643
+ export function herdStopHidden(
644
+ _a: Commands["herd:stop-hidden"]["payload"],
645
+ o: RtClientOptions = {},
646
+ ): Promise<RtResponse<Commands["herd:stop-hidden"]["data"]>> {
647
+ return rtCommand<Commands["herd:stop-hidden"]["data"]>("herd:stop-hidden", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
648
+ }
649
+
650
+ // ─── Background server (daemon-owned background herdr session) ────────────
651
+
652
+ /** A cold ensure spawns `herdr server`, waits out bg-service's own 10s
653
+ readyTimeoutMs, then runs its parity probes sequentially -- the 15s
654
+ default budget every other rt-client call inherits can lose that race,
655
+ so this is the one wrapper that widens its own. */
656
+ export function bgEnsure(
657
+ a: Commands["bg:ensure"]["payload"] = {},
658
+ o: RtClientOptions = {},
659
+ ): Promise<RtResponse<Commands["bg:ensure"]["data"]>> {
660
+ const payload: Record<string, unknown> = {};
661
+ if (a.claim !== undefined) payload.claim = a.claim;
662
+ return rtCommand<Commands["bg:ensure"]["data"]>("bg:ensure", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
663
+ }
664
+
665
+ /** Never ensures/spawns; a plain read of the current state. */
666
+ export function bgStatus(o: RtClientOptions = {}): Promise<RtResponse<Commands["bg:status"]["data"]>> {
667
+ return rtCommand<Commands["bg:status"]["data"]>("bg:status", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
668
+ }
669
+
670
+ /** Asks the daemon to stop the background server; refuses (ok:false) while any claim is live, naming the owners. */
671
+ export function bgStop(o: RtClientOptions = {}): Promise<RtResponse<Commands["bg:stop"]["data"]>> {
672
+ return rtCommand<Commands["bg:stop"]["data"]>("bg:stop", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 30_000 });
673
+ }
674
+
675
+ export function bgRelease(
676
+ a: Commands["bg:release"]["payload"],
677
+ o: RtClientOptions = {},
678
+ ): Promise<RtResponse<Commands["bg:release"]["data"]>> {
679
+ return rtCommand<Commands["bg:release"]["data"]>("bg:release", { claim: a.claim }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
680
+ }