agent-relay-server 0.25.0 → 0.27.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "CONTRIBUTING.md"
34
34
  ],
35
35
  "dependencies": {
36
- "agent-relay-sdk": "0.2.14"
36
+ "agent-relay-sdk": "0.2.16"
37
37
  },
38
38
  "scripts": {
39
39
  "prepack": "bun run build:dashboard:bundle >&2",
package/public/index.html CHANGED
@@ -11806,36 +11806,36 @@ function downloadText(filename, text, type) {
11806
11806
  }
11807
11807
  //#endregion
11808
11808
  //#region ../sdk/src/provider-catalog.ts
11809
- var CLAUDE_LOW_TO_MAX = [
11809
+ var CLAUDE_LOW_TO_MAX$1 = [
11810
11810
  "low",
11811
11811
  "medium",
11812
11812
  "high",
11813
11813
  "max"
11814
11814
  ];
11815
- var CLAUDE_LOW_TO_XHIGH_MAX = [
11815
+ var CLAUDE_LOW_TO_XHIGH_MAX$1 = [
11816
11816
  "low",
11817
11817
  "medium",
11818
11818
  "high",
11819
11819
  "xhigh",
11820
11820
  "max"
11821
11821
  ];
11822
- var CODEX_REASONING = [
11822
+ var CODEX_REASONING$1 = [
11823
11823
  "low",
11824
11824
  "medium",
11825
11825
  "high",
11826
11826
  "xhigh"
11827
11827
  ];
11828
- var CONTEXT_200K = {
11828
+ var CONTEXT_200K$1 = {
11829
11829
  value: 2e5,
11830
11830
  source: "catalog",
11831
11831
  confidence: "declared"
11832
11832
  };
11833
- var CONTEXT_1M = {
11833
+ var CONTEXT_1M$1 = {
11834
11834
  value: 1e6,
11835
11835
  source: "catalog",
11836
11836
  confidence: "declared"
11837
11837
  };
11838
- var CODE_MODEL_CAPABILITIES = {
11838
+ var CODE_MODEL_CAPABILITIES$1 = {
11839
11839
  modalities: {
11840
11840
  input: {
11841
11841
  text: true,
@@ -11852,88 +11852,96 @@ var CODE_MODEL_CAPABILITIES = {
11852
11852
  source: "catalog",
11853
11853
  confidence: "declared"
11854
11854
  };
11855
- function codeModel(limits) {
11855
+ function codeModel$1(limits) {
11856
11856
  return {
11857
11857
  ...limits ? { limits } : {},
11858
- capabilities: CODE_MODEL_CAPABILITIES
11858
+ capabilities: CODE_MODEL_CAPABILITIES$1
11859
11859
  };
11860
11860
  }
11861
- var PROVIDER_CATALOG = {
11861
+ var PROVIDER_CATALOG$1 = {
11862
11862
  claude: {
11863
11863
  provider: "claude",
11864
11864
  label: "Claude Code",
11865
11865
  defaultModel: "sonnet-4.6",
11866
11866
  models: [
11867
+ {
11868
+ alias: "fable-5",
11869
+ label: "Fable 5",
11870
+ providerModel: "claude-fable-5",
11871
+ efforts: CLAUDE_LOW_TO_XHIGH_MAX$1,
11872
+ defaultEffort: "medium",
11873
+ ...codeModel$1({ contextWindowTokens: CONTEXT_1M$1 })
11874
+ },
11867
11875
  {
11868
11876
  alias: "opus-4.8",
11869
11877
  label: "Opus 4.8",
11870
11878
  providerModel: "claude-opus-4-8",
11871
- efforts: CLAUDE_LOW_TO_XHIGH_MAX,
11879
+ efforts: CLAUDE_LOW_TO_XHIGH_MAX$1,
11872
11880
  defaultEffort: "medium",
11873
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11881
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11874
11882
  },
11875
11883
  {
11876
11884
  alias: "opus-4.8[1m]",
11877
11885
  label: "Opus 4.8 1M",
11878
11886
  providerModel: "claude-opus-4-8[1m]",
11879
- efforts: CLAUDE_LOW_TO_XHIGH_MAX,
11887
+ efforts: CLAUDE_LOW_TO_XHIGH_MAX$1,
11880
11888
  defaultEffort: "medium",
11881
- ...codeModel({ contextWindowTokens: CONTEXT_1M })
11889
+ ...codeModel$1({ contextWindowTokens: CONTEXT_1M$1 })
11882
11890
  },
11883
11891
  {
11884
11892
  alias: "opus-4.7",
11885
11893
  label: "Opus 4.7",
11886
11894
  providerModel: "claude-opus-4-7",
11887
- efforts: CLAUDE_LOW_TO_XHIGH_MAX,
11895
+ efforts: CLAUDE_LOW_TO_XHIGH_MAX$1,
11888
11896
  defaultEffort: "medium",
11889
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11897
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11890
11898
  },
11891
11899
  {
11892
11900
  alias: "opus-4.7[1m]",
11893
11901
  label: "Opus 4.7 1M",
11894
11902
  providerModel: "claude-opus-4-7[1m]",
11895
- efforts: CLAUDE_LOW_TO_XHIGH_MAX,
11903
+ efforts: CLAUDE_LOW_TO_XHIGH_MAX$1,
11896
11904
  defaultEffort: "medium",
11897
- ...codeModel({ contextWindowTokens: CONTEXT_1M })
11905
+ ...codeModel$1({ contextWindowTokens: CONTEXT_1M$1 })
11898
11906
  },
11899
11907
  {
11900
11908
  alias: "opus-4.6",
11901
11909
  label: "Opus 4.6",
11902
11910
  providerModel: "claude-opus-4-6",
11903
- efforts: CLAUDE_LOW_TO_MAX,
11911
+ efforts: CLAUDE_LOW_TO_MAX$1,
11904
11912
  defaultEffort: "medium",
11905
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11913
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11906
11914
  },
11907
11915
  {
11908
11916
  alias: "opus-4.6[1m]",
11909
11917
  label: "Opus 4.6 1M",
11910
11918
  providerModel: "claude-opus-4-6[1m]",
11911
- efforts: CLAUDE_LOW_TO_MAX,
11919
+ efforts: CLAUDE_LOW_TO_MAX$1,
11912
11920
  defaultEffort: "medium",
11913
- ...codeModel({ contextWindowTokens: CONTEXT_1M })
11921
+ ...codeModel$1({ contextWindowTokens: CONTEXT_1M$1 })
11914
11922
  },
11915
11923
  {
11916
11924
  alias: "sonnet-4.6",
11917
11925
  label: "Sonnet 4.6",
11918
11926
  providerModel: "claude-sonnet-4-6",
11919
- efforts: CLAUDE_LOW_TO_MAX,
11927
+ efforts: CLAUDE_LOW_TO_MAX$1,
11920
11928
  defaultEffort: "medium",
11921
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11929
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11922
11930
  },
11923
11931
  {
11924
11932
  alias: "sonnet-4.6[1m]",
11925
11933
  label: "Sonnet 4.6 1M",
11926
11934
  providerModel: "claude-sonnet-4-6[1m]",
11927
- efforts: CLAUDE_LOW_TO_MAX,
11935
+ efforts: CLAUDE_LOW_TO_MAX$1,
11928
11936
  defaultEffort: "medium",
11929
- ...codeModel({ contextWindowTokens: CONTEXT_1M })
11937
+ ...codeModel$1({ contextWindowTokens: CONTEXT_1M$1 })
11930
11938
  },
11931
11939
  {
11932
11940
  alias: "haiku",
11933
11941
  label: "Haiku",
11934
11942
  providerModel: "haiku",
11935
11943
  efforts: [],
11936
- ...codeModel()
11944
+ ...codeModel$1()
11937
11945
  }
11938
11946
  ]
11939
11947
  },
@@ -11946,49 +11954,49 @@ var PROVIDER_CATALOG = {
11946
11954
  alias: "gpt-5.5",
11947
11955
  label: "GPT-5.5",
11948
11956
  providerModel: "gpt-5.5",
11949
- efforts: CODEX_REASONING,
11957
+ efforts: CODEX_REASONING$1,
11950
11958
  defaultEffort: "medium",
11951
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11959
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11952
11960
  },
11953
11961
  {
11954
11962
  alias: "gpt-5.4",
11955
11963
  label: "GPT-5.4",
11956
11964
  providerModel: "gpt-5.4",
11957
- efforts: CODEX_REASONING,
11965
+ efforts: CODEX_REASONING$1,
11958
11966
  defaultEffort: "medium",
11959
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11967
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11960
11968
  },
11961
11969
  {
11962
11970
  alias: "gpt-5.4-mini",
11963
11971
  label: "GPT-5.4 Mini",
11964
11972
  providerModel: "gpt-5.4-mini",
11965
- efforts: CODEX_REASONING,
11973
+ efforts: CODEX_REASONING$1,
11966
11974
  defaultEffort: "medium",
11967
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11975
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11968
11976
  },
11969
11977
  {
11970
11978
  alias: "gpt-5.3-codex",
11971
11979
  label: "GPT-5.3 Codex",
11972
11980
  providerModel: "gpt-5.3-codex",
11973
- efforts: CODEX_REASONING,
11981
+ efforts: CODEX_REASONING$1,
11974
11982
  defaultEffort: "medium",
11975
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11983
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11976
11984
  },
11977
11985
  {
11978
11986
  alias: "gpt-5.3-codex-spark",
11979
11987
  label: "GPT-5.3 Codex Spark",
11980
11988
  providerModel: "gpt-5.3-codex-spark",
11981
- efforts: CODEX_REASONING,
11989
+ efforts: CODEX_REASONING$1,
11982
11990
  defaultEffort: "high",
11983
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11991
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11984
11992
  },
11985
11993
  {
11986
11994
  alias: "gpt-5.2",
11987
11995
  label: "GPT-5.2",
11988
11996
  providerModel: "gpt-5.2",
11989
- efforts: CODEX_REASONING,
11997
+ efforts: CODEX_REASONING$1,
11990
11998
  defaultEffort: "medium",
11991
- ...codeModel({ contextWindowTokens: CONTEXT_200K })
11999
+ ...codeModel$1({ contextWindowTokens: CONTEXT_200K$1 })
11992
12000
  }
11993
12001
  ]
11994
12002
  }
@@ -11999,7 +12007,7 @@ function firstAvailableProvider(orchestrator) {
11999
12007
  return orchestrator?.providers[0] ?? "claude";
12000
12008
  }
12001
12009
  function defaultModelFor(provider) {
12002
- return PROVIDER_CATALOG[provider]?.defaultModel ?? "";
12010
+ return PROVIDER_CATALOG$1[provider]?.defaultModel ?? "";
12003
12011
  }
12004
12012
  function pathWithinBase(path, baseDir) {
12005
12013
  const value = path.trim().replace(/\\/g, "/").replace(/\/+$/, "");
@@ -13876,7 +13884,7 @@ var useRelayStore = create$1()(persist((set, get) => ({
13876
13884
  const provCaps = agent.providerCapabilities;
13877
13885
  const provider = provCaps?.model?.provider || (typeof agent.meta?.provider === "string" ? agent.meta.provider : "");
13878
13886
  const reportedModel = provCaps?.model?.alias || provCaps?.model?.id || "";
13879
- const model = ((provider ? PROVIDER_CATALOG[provider] : void 0)?.models.find((m) => m.alias === reportedModel || m.providerModel === reportedModel))?.alias || "";
13887
+ const model = ((provider ? PROVIDER_CATALOG$1[provider] : void 0)?.models.find((m) => m.alias === reportedModel || m.providerModel === reportedModel))?.alias || "";
13880
13888
  const effort = provCaps?.model?.effort || "";
13881
13889
  const cwd = typeof agent.meta?.cwd === "string" ? agent.meta.cwd : "";
13882
13890
  const approval = provCaps?.session?.approvalMode || (typeof agent.meta?.approvalMode === "string" ? agent.meta.approvalMode : "guarded");
@@ -91948,10 +91956,10 @@ function wrap(middleware, callback) {
91948
91956
  //#endregion
91949
91957
  //#region node_modules/vfile/lib/minpath.browser.js
91950
91958
  var minpath = {
91951
- basename: basename$2,
91952
- dirname,
91959
+ basename: basename$3,
91960
+ dirname: dirname$1,
91953
91961
  extname,
91954
- join,
91962
+ join: join$1,
91955
91963
  sep: "/"
91956
91964
  };
91957
91965
  /**
@@ -91964,7 +91972,7 @@ var minpath = {
91964
91972
  * @returns {string}
91965
91973
  * Stem or basename.
91966
91974
  */
91967
- function basename$2(path, extname) {
91975
+ function basename$3(path, extname) {
91968
91976
  if (extname !== void 0 && typeof extname !== "string") throw new TypeError("\"ext\" argument must be a string");
91969
91977
  assertPath$1(path);
91970
91978
  let start = 0;
@@ -92016,7 +92024,7 @@ function basename$2(path, extname) {
92016
92024
  * @returns {string}
92017
92025
  * File path.
92018
92026
  */
92019
- function dirname(path) {
92027
+ function dirname$1(path) {
92020
92028
  assertPath$1(path);
92021
92029
  if (path.length === 0) return ".";
92022
92030
  let end = -1;
@@ -92077,7 +92085,7 @@ function extname(path) {
92077
92085
  * @returns {string}
92078
92086
  * File path.
92079
92087
  */
92080
- function join(...segments) {
92088
+ function join$1(...segments) {
92081
92089
  let index = -1;
92082
92090
  /** @type {string | undefined} */
92083
92091
  let joined;
@@ -101370,10 +101378,10 @@ function mergeObjects(target, ...sources) {
101370
101378
  });
101371
101379
  return target;
101372
101380
  }
101373
- function basename$1(path) {
101381
+ function basename$2(path) {
101374
101382
  const idx = ~path.lastIndexOf("/") || ~path.lastIndexOf("\\");
101375
101383
  if (idx === 0) return path;
101376
- else if (~idx === path.length - 1) return basename$1(path.substring(0, path.length - 1));
101384
+ else if (~idx === path.length - 1) return basename$2(path.substring(0, path.length - 1));
101377
101385
  else return path.substr(~idx + 1);
101378
101386
  }
101379
101387
  function strcmp(a, b) {
@@ -102426,7 +102434,7 @@ var init_dist$2 = __esmMin((() => {
102426
102434
  this._contentNameIsCapturing = RegexSource.hasCaptures(this._contentName);
102427
102435
  }
102428
102436
  get debugName() {
102429
- const location = this.$location ? `${basename$1(this.$location.filename)}:${this.$location.line}` : "unknown";
102437
+ const location = this.$location ? `${basename$2(this.$location.filename)}:${this.$location.line}` : "unknown";
102430
102438
  return `${this.constructor.name}#${this.id} @ ${location}`;
102431
102439
  }
102432
102440
  getName(lineText, captureIndices) {
@@ -108590,7 +108598,7 @@ function getShiki() {
108590
108598
  function byteLength(value) {
108591
108599
  return new TextEncoder().encode(value).length;
108592
108600
  }
108593
- function basename(path) {
108601
+ function basename$1(path) {
108594
108602
  return path.split("/").filter(Boolean).pop() || path;
108595
108603
  }
108596
108604
  function detectShebangLanguage(content) {
@@ -108609,7 +108617,7 @@ function detectShebangLanguage(content) {
108609
108617
  }
108610
108618
  function detectCodeLanguage(path, mediaType, languageHint, content) {
108611
108619
  if (languageHint) return languageHint;
108612
- const name = basename(path).toLowerCase();
108620
+ const name = basename$1(path).toLowerCase();
108613
108621
  if (BASENAME_LANGUAGES[name]) return BASENAME_LANGUAGES[name];
108614
108622
  if (name.endsWith(".d.ts")) return "typescript";
108615
108623
  const shebangLanguage = detectShebangLanguage(content);
@@ -129418,6 +129426,51 @@ function OrchestratorsView() {
129418
129426
  });
129419
129427
  }
129420
129428
  //#endregion
129429
+ //#region ../sdk/dist/types.js
129430
+ /** Terminal workspace statuses shared across server + dashboard filters. */
129431
+ var TERMINAL_WORKSPACE_STATUS_VALUES = [
129432
+ "cleaned",
129433
+ "merged",
129434
+ "abandoned"
129435
+ ];
129436
+ var CONTEXT_200K = {
129437
+ value: 2e5,
129438
+ source: "catalog",
129439
+ confidence: "declared"
129440
+ };
129441
+ var CONTEXT_1M = {
129442
+ value: 1e6,
129443
+ source: "catalog",
129444
+ confidence: "declared"
129445
+ };
129446
+ var CODE_MODEL_CAPABILITIES = {
129447
+ modalities: {
129448
+ input: {
129449
+ text: true,
129450
+ image: true
129451
+ },
129452
+ output: { text: true }
129453
+ },
129454
+ tools: {
129455
+ code: true,
129456
+ review: true,
129457
+ debug: true,
129458
+ refactor: true
129459
+ },
129460
+ source: "catalog",
129461
+ confidence: "declared"
129462
+ };
129463
+ function codeModel(limits) {
129464
+ return {
129465
+ ...limits ? { limits } : {},
129466
+ capabilities: CODE_MODEL_CAPABILITIES
129467
+ };
129468
+ }
129469
+ ({ ...codeModel({ contextWindowTokens: CONTEXT_1M }) }), { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_1M }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_1M }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_1M }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_1M }) }, { ...codeModel() }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) }, { ...codeModel({ contextWindowTokens: CONTEXT_200K }) };
129470
+ (0, (/* @__PURE__ */ __commonJSMin(((exports, module) => {
129471
+ module.exports = {};
129472
+ })))().tmpdir)();
129473
+ //#endregion
129421
129474
  //#region src/components/views/workspaces.tsx
129422
129475
  var LIVE_STATUSES = new Set([
129423
129476
  "active",
@@ -129474,7 +129527,7 @@ var STATUS_ORDER = {
129474
129527
  abandoned: 7,
129475
129528
  cleaned: 8
129476
129529
  };
129477
- var TERMINAL_STATUSES = new Set(["cleaned", "merged"]);
129530
+ var TERMINAL_STATUSES = new Set(TERMINAL_WORKSPACE_STATUS_VALUES);
129478
129531
  function shortPath(path) {
129479
129532
  const parts = path.split("/").filter(Boolean);
129480
129533
  if (parts.length <= 3) return path || "-";
@@ -133144,7 +133197,7 @@ function blankForm(orchestratorId = "") {
133144
133197
  orchestratorId,
133145
133198
  targetMode: "existing_agent",
133146
133199
  provider: "codex",
133147
- model: PROVIDER_CATALOG.codex.defaultModel || "",
133200
+ model: PROVIDER_CATALOG$1.codex.defaultModel || "",
133148
133201
  effort: "",
133149
133202
  profile: "default-relay",
133150
133203
  label: "",
@@ -133183,7 +133236,7 @@ function automationToForm(automation) {
133183
133236
  orchestratorId: automation.orchestratorId,
133184
133237
  targetMode: policy.mode,
133185
133238
  provider: policy.mode === "on_demand_agent" ? policy.provider : policy.selector.provider || "codex",
133186
- model: policy.mode === "on_demand_agent" ? policy.model || PROVIDER_CATALOG[policy.provider].defaultModel || "" : "",
133239
+ model: policy.mode === "on_demand_agent" ? policy.model || PROVIDER_CATALOG$1[policy.provider].defaultModel || "" : "",
133187
133240
  effort: policy.mode === "on_demand_agent" ? policy.effort || "" : "",
133188
133241
  profile: policy.mode === "on_demand_agent" ? policy.profile || "default-relay" : "default-relay",
133189
133242
  label: policy.mode === "existing_agent" ? policy.selector.label || "" : "",
@@ -133593,21 +133646,21 @@ function AutomationCard({ automation, runs, now, selected, onEdit, onRun, onTogg
133593
133646
  }
133594
133647
  function AutomationEditor({ form, selected, saving, orchestrators, agentProfiles, onChange, onSave }) {
133595
133648
  const providers = orchestrators.find((orch) => orch.id === form.orchestratorId)?.providers || [];
133596
- const models = PROVIDER_CATALOG[form.provider]?.models || [];
133649
+ const models = PROVIDER_CATALOG$1[form.provider]?.models || [];
133597
133650
  const efforts = models.find((model) => model.alias === form.model)?.efforts || [];
133598
133651
  function selectOrchestrator(orchestratorId) {
133599
133652
  const provider = orchestrators.find((orch) => orch.id === orchestratorId)?.providers[0] || form.provider;
133600
133653
  onChange({
133601
133654
  orchestratorId,
133602
133655
  provider,
133603
- model: PROVIDER_CATALOG[provider]?.defaultModel || "",
133656
+ model: PROVIDER_CATALOG$1[provider]?.defaultModel || "",
133604
133657
  effort: ""
133605
133658
  });
133606
133659
  }
133607
133660
  function selectProvider(provider) {
133608
133661
  onChange({
133609
133662
  provider,
133610
- model: PROVIDER_CATALOG[provider]?.defaultModel || "",
133663
+ model: PROVIDER_CATALOG$1[provider]?.defaultModel || "",
133611
133664
  effort: ""
133612
133665
  });
133613
133666
  }
@@ -157363,7 +157416,7 @@ function OrchestratorSpawnModal() {
157363
157416
  const agentProfiles = useRelayStore((s) => s.agentProfiles);
157364
157417
  const orch = orchestrators.find((o) => o.id === orchId);
157365
157418
  const providers = orch?.providers || [];
157366
- const models = PROVIDER_CATALOG[provider]?.models || [];
157419
+ const models = PROVIDER_CATALOG$1[provider]?.models || [];
157367
157420
  const selectedModel = models.find((m) => m.alias === model);
157368
157421
  const efforts = selectedModel?.efforts || [];
157369
157422
  const canSpawn = Boolean(orch && providers.length > 0 && cwd.trim());
@@ -157373,7 +157426,7 @@ function OrchestratorSpawnModal() {
157373
157426
  set({
157374
157427
  spawnOrchId: nextId,
157375
157428
  spawnProvider: nextProvider,
157376
- spawnModel: nextProvider ? PROVIDER_CATALOG[nextProvider]?.defaultModel || "" : "",
157429
+ spawnModel: nextProvider ? PROVIDER_CATALOG$1[nextProvider]?.defaultModel || "" : "",
157377
157430
  spawnEffort: "",
157378
157431
  spawnCwd: nextOrch?.baseDir || ""
157379
157432
  });
@@ -157381,7 +157434,7 @@ function OrchestratorSpawnModal() {
157381
157434
  function selectProvider(nextProvider) {
157382
157435
  set({
157383
157436
  spawnProvider: nextProvider,
157384
- spawnModel: PROVIDER_CATALOG[nextProvider]?.defaultModel || "",
157437
+ spawnModel: PROVIDER_CATALOG$1[nextProvider]?.defaultModel || "",
157385
157438
  spawnEffort: ""
157386
157439
  });
157387
157440
  }
@@ -157710,7 +157763,7 @@ function ManagedPolicyModal() {
157710
157763
  const submitPolicy = useRelayStore((s) => s.submitPolicy);
157711
157764
  const orch = orchestrators.find((o) => o.id === m.orchestratorId);
157712
157765
  const providers = orch?.providers || [];
157713
- const models = PROVIDER_CATALOG[m.provider]?.models || [];
157766
+ const models = PROVIDER_CATALOG$1[m.provider]?.models || [];
157714
157767
  const selectedModel = models.find((model) => model.alias === m.model);
157715
157768
  const efforts = selectedModel?.efforts || [];
157716
157769
  const isEditing = Boolean(m.editing);
@@ -157726,7 +157779,7 @@ function ManagedPolicyModal() {
157726
157779
  update({
157727
157780
  orchestratorId,
157728
157781
  provider,
157729
- model: PROVIDER_CATALOG[provider]?.defaultModel || "",
157782
+ model: PROVIDER_CATALOG$1[provider]?.defaultModel || "",
157730
157783
  effort: ""
157731
157784
  });
157732
157785
  }
@@ -157734,7 +157787,7 @@ function ManagedPolicyModal() {
157734
157787
  update({
157735
157788
  provider,
157736
157789
  rig: "",
157737
- model: PROVIDER_CATALOG[provider]?.defaultModel || "",
157790
+ model: PROVIDER_CATALOG$1[provider]?.defaultModel || "",
157738
157791
  effort: ""
157739
157792
  });
157740
157793
  }
@@ -1,7 +1,9 @@
1
1
  import { emitRelayEvent } from "./events";
2
2
  import { getNotificationsConfig } from "./config-store";
3
3
  import { notifySystemMessage } from "./notify";
4
- import type { WorkspaceRecord } from "./types";
4
+ import { listAgents } from "./db";
5
+ import { isAgentOnline } from "./agent-ref";
6
+ import type { AgentCard, WorkspaceRecord } from "./types";
5
7
 
6
8
  export interface BranchLandedInput {
7
9
  /**
@@ -52,26 +54,58 @@ export function notifyBranchLanded(input: BranchLandedInput): void {
52
54
  if (!config.enabled || !config.branchLanded) return;
53
55
 
54
56
  const author = workspace.ownerAgentId;
55
- if (!author) return;
56
-
57
- const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
58
57
  const shaLabel = shortSha ? ` as \`${shortSha}\`` : "";
59
58
  const subjectLabel = input.subject ? ` — "${input.subject}"` : "";
60
- const continueLabel = input.newBranch
61
- ? ` You're now on \`${input.newBranch}\` — keep working there.`
62
- : " Worktree reclaimed.";
59
+ const payload = {
60
+ kind: "branch.landed",
61
+ workspaceId: workspace.id,
62
+ repoRoot: workspace.repoRoot,
63
+ branch: landedBranch,
64
+ base,
65
+ sha: input.mergedSha,
66
+ author,
67
+ newBranch: input.newBranch,
68
+ };
63
69
 
64
- notifySystemMessage(author, {
65
- subject: "Your branch landed",
66
- body: `✅ ${branchLabel} landed on \`${base}\`${shaLabel}${subjectLabel}.${continueLabel}`,
67
- payload: {
68
- kind: "branch.landed",
69
- workspaceId: workspace.id,
70
- repoRoot: workspace.repoRoot,
71
- branch: landedBranch,
72
- base,
73
- sha: input.mergedSha,
74
- newBranch: input.newBranch,
75
- },
70
+ // The branch author cares most — push regardless of online (store-ahead delivers it on
71
+ // next poll if they've moved on, #234). They land-and-continue onto the recycled branch.
72
+ if (author) {
73
+ const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
74
+ const continueLabel = input.newBranch
75
+ ? ` You're now on \`${input.newBranch}\` — keep working there.`
76
+ : " Worktree reclaimed.";
77
+ notifySystemMessage(author, {
78
+ subject: "Your branch landed",
79
+ body: `✅ ${branchLabel} landed on \`${base}\`${shaLabel}${subjectLabel}.${continueLabel}`,
80
+ payload,
81
+ });
82
+ }
83
+
84
+ // Agents on `main` — those whose cwd IS the main checkout (not an isolated worktree) —
85
+ // get a live "merged" notice so a long-lived main agent's context stays current as work
86
+ // lands under it (#239). Online-only: a stale/exited main session needs no wake, and
87
+ // store-ahead to it would just pile up noise. The author is in a worktree (cwd ≠ repoRoot)
88
+ // so it's naturally excluded; guard anyway for shared-mode owners.
89
+ const branchLabel = landedBranch ? `\`${landedBranch}\`` : "A branch";
90
+ const authorLabel = author ? ` by \`${author}\`` : "";
91
+ for (const agent of agentsOnMain(workspace.repoRoot, author)) {
92
+ notifySystemMessage(agent.id, {
93
+ subject: `Merged to ${base}`,
94
+ body: `🔀 ${branchLabel}${authorLabel} merged to \`${base}\`${shaLabel}${subjectLabel}.`,
95
+ payload,
96
+ });
97
+ }
98
+ }
99
+
100
+ // An agent is "on `main`" when its registered cwd equals the repo's main checkout — i.e. it
101
+ // works in the base, not an isolated worktree. Excludes the author, pseudo agents (system/
102
+ // user), channels, and offline sessions.
103
+ function agentsOnMain(repoRoot: string, author: string | undefined): AgentCard[] {
104
+ return listAgents().filter((a) => {
105
+ if (a.id === author || a.id === "system" || a.id === "user") return false;
106
+ if (a.kind === "channel" || a.meta?.kind === "channel") return false;
107
+ const cwd = a.meta?.cwd;
108
+ if (typeof cwd !== "string" || cwd !== repoRoot) return false;
109
+ return isAgentOnline(a);
76
110
  });
77
111
  }
@@ -33,6 +33,7 @@ import {
33
33
  import type { WorkspaceMergePreview, WorkspaceRecord, WorkspaceStatus } from "./types";
34
34
  import { requestWorkspaceMerge } from "./workspace-merge";
35
35
  import { workspaceActiveClaim } from "./workspace-claim";
36
+ import { reapOrphanedWorktrees } from "./workspace-orphans";
36
37
  import { READY_TO_LAND_STATUSES, TERMINAL_WORKSPACE_STATUSES } from "./workspace-phase";
37
38
  import { errMessage, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
38
39
  import { getStewardConfig } from "./config-store";
@@ -66,6 +67,10 @@ const DB_VACUUM_EVERY = Number(process.env.AGENT_RELAY_DB_VACUUM_EVERY) || 7;
66
67
  let dbMaintenanceRuns = 0;
67
68
  const WORKSPACE_REVIEW_TTL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_REVIEW_TTL_MS) || 3 * DAY_MS;
68
69
  const WORKSPACE_GC_INTERVAL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_GC_INTERVAL_MS) || 60 * 60 * 1000;
70
+ // Disk⇄DB orphan reconcile cadence (#244). Runs on start for a boot-time pass,
71
+ // then periodically — orphans accrue slowly (one per crashed/killed session), so
72
+ // a 30-min sweep is plenty without hammering the hosts with probes.
73
+ const WORKSPACE_ORPHAN_REAPER_INTERVAL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_ORPHAN_REAPER_INTERVAL_MS) || 30 * 60 * 1000;
69
74
  // Deterministic auto-land (Layer 0): merge clean fast-forwards with no human in
70
75
  // the loop. Default on for the seamless workflow; set AGENT_RELAY_WORKSPACE_AUTO_MERGE=0
71
76
  // to require a manual or steward merge per repo. Read at call-time so operators can
@@ -411,6 +416,15 @@ const definitions: MaintenanceJobDefinition[] = [
411
416
  timeoutMs: 60 * 1000,
412
417
  handler: workspaceGC,
413
418
  },
419
+ {
420
+ id: "workspace-orphan-reaper",
421
+ title: "Workspace orphan reaper",
422
+ description: "Reconcile disk⇄DB: reap orphaned worktrees whose work has landed (or is empty), flag orphans holding un-landed work as needs-attention instead of deleting, and report rows whose worktree vanished. git worktree prune can't do this — it no-ops while the directory still exists.",
423
+ intervalMs: WORKSPACE_ORPHAN_REAPER_INTERVAL_MS,
424
+ runOnStart: true,
425
+ timeoutMs: 2 * 60 * 1000,
426
+ handler: reapOrphanedWorktrees,
427
+ },
414
428
  ];
415
429
 
416
430
  function workspacePathWithinBase(path: string | undefined, baseDir: string | undefined): boolean {