openmausbot 0.1.90 → 0.1.91

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.
Files changed (112) hide show
  1. package/dist/assets/{abnfDiagram-O67JEVCF-DUyXd0kL.js → abnfDiagram-O67JEVCF-yP-w8pRq.js} +1 -1
  2. package/dist/assets/{arc-q73qM-At.js → arc-DsivfZEh.js} +1 -1
  3. package/dist/assets/{architectureDiagram-NJMV4G6O-D33O3Wur.js → architectureDiagram-NJMV4G6O-CILw7lzT.js} +1 -1
  4. package/dist/assets/{blockDiagram-BEXU5L5S-DLsJeoXC.js → blockDiagram-BEXU5L5S-DEQUV3HF.js} +1 -1
  5. package/dist/assets/{c4Diagram-YGBWAQC7-DnaljZnu.js → c4Diagram-YGBWAQC7-lSXCZoKA.js} +1 -1
  6. package/dist/assets/channel-DrfGMu7X.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-Da0h2i79.js → chunk-2Q5K7J3B-QCD5bgWP.js} +1 -1
  8. package/dist/assets/{chunk-3FUC2YCW-CfzHi54f.js → chunk-3FUC2YCW-C5-iZJuL.js} +2 -2
  9. package/dist/assets/{chunk-5VM5RSS4-LjE4d9Jf.js → chunk-5VM5RSS4-cxsawMpY.js} +1 -1
  10. package/dist/assets/{chunk-GTNCS2PH-DY-UGmvc.js → chunk-GTNCS2PH-DV8tV8H7.js} +1 -1
  11. package/dist/assets/{chunk-GWA4HPMP-BqeGhyhP.js → chunk-GWA4HPMP-CJFZyfbW.js} +1 -1
  12. package/dist/assets/{chunk-JWPE2WC7-BYa9EBAg.js → chunk-JWPE2WC7-BigbMXco.js} +1 -1
  13. package/dist/assets/{chunk-NETBCI7D-vTrDtWyb.js → chunk-NETBCI7D-oVxTfpDy.js} +1 -1
  14. package/dist/assets/{chunk-WEXAMYUT-ic5tXadM.js → chunk-WEXAMYUT-CH4BiroW.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-DvRCY-c6.js → chunk-XXDRQBXY-KEDa6gax.js} +1 -1
  16. package/dist/assets/{classDiagram-v2-NBCMYWYE-DVbmgz_L.js → classDiagram-v2-NBCMYWYE-BiPjfqVv.js} +1 -1
  17. package/dist/assets/{cose-bilkent-JH36ORCC-vdUs2n57.js → cose-bilkent-JH36ORCC-Caxew3OY.js} +1 -1
  18. package/dist/assets/{cynefin-OW5HDTMX-BvLGlV3U.js → cynefin-OW5HDTMX-B_Pui369.js} +1 -1
  19. package/dist/assets/{cynefinDiagram-VND7K2PF-C4GcJQDt.js → cynefinDiagram-VND7K2PF-X64ydIhP.js} +1 -1
  20. package/dist/assets/{dagre-6A5THRUB-BMSaHpt_.js → dagre-6A5THRUB-TqYCI1E1.js} +1 -1
  21. package/dist/assets/{diagram-22UHCM2B-D5b9UBhC.js → diagram-22UHCM2B-TeOt3rAQ.js} +1 -1
  22. package/dist/assets/{diagram-3UASUU5V-CIKLlB7Y.js → diagram-3UASUU5V-D70BLhDA.js} +1 -1
  23. package/dist/assets/{diagram-ATOU4E4O-BzXb-7k7.js → diagram-ATOU4E4O-BbQDLwUC.js} +1 -1
  24. package/dist/assets/{diagram-CDSNMT55-HsTvJ7uT.js → diagram-CDSNMT55-ChNJZ1u-.js} +1 -1
  25. package/dist/assets/{diagram-MLGK6HIB-Db9ySNbQ.js → diagram-MLGK6HIB-DMBl2APa.js} +1 -1
  26. package/dist/assets/{diagram-MPIPVDR6-D6BKbpqY.js → diagram-MPIPVDR6-CEqDchOp.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-ZINNZB2B-Bq067L0t.js → ebnfDiagram-ZINNZB2B-BVDKRp_-.js} +1 -1
  28. package/dist/assets/{elk-276RUBZZ-DhaD7fNJ.js → elk-276RUBZZ-CRkofO9B.js} +1 -1
  29. package/dist/assets/{erDiagram-OPXOYQCR-fwIhfz36.js → erDiagram-OPXOYQCR-DruOMh5e.js} +1 -1
  30. package/dist/assets/{flowDiagram-KWPJA3E3-DygIyu0V.js → flowDiagram-KWPJA3E3-8GMQeB7t.js} +1 -1
  31. package/dist/assets/{ganttDiagram-FUAMR5RP-DPYfSO96.js → ganttDiagram-FUAMR5RP-ORE9jcsM.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-X574FWY7-DhkHeCle.js → gitGraphDiagram-X574FWY7-BmsedsWh.js} +1 -1
  33. package/dist/assets/index-BlnCEcU6.js +588 -0
  34. package/dist/assets/{index-G1PHKGyh.js → index-DKNEM8Q6.js} +1 -1
  35. package/dist/assets/index-DVxjOvID.css +1 -0
  36. package/dist/assets/{infoDiagram-VRGFBTTK-DstBLxME.js → infoDiagram-VRGFBTTK-E_p7AooJ.js} +1 -1
  37. package/dist/assets/{ishikawaDiagram-OU5B5YK6-CrIsGgch.js → ishikawaDiagram-OU5B5YK6-Y-B2mKda.js} +1 -1
  38. package/dist/assets/{journeyDiagram-ZHPQQLJL-CYypGDbB.js → journeyDiagram-ZHPQQLJL-eA8RELcK.js} +1 -1
  39. package/dist/assets/{kanban-definition-PNTS6WVX-DGjPt8E_.js → kanban-definition-PNTS6WVX-DqIL2e8e.js} +1 -1
  40. package/dist/assets/{linear-Dwc3Hrru.js → linear-yC_29R6e.js} +1 -1
  41. package/dist/assets/{mermaid.core-Dc4OePBL.js → mermaid.core-VhEMkSND.js} +5 -5
  42. package/dist/assets/{mindmap-definition-NLK3R4M7-DV-vepj-.js → mindmap-definition-NLK3R4M7-oJ7kyQZy.js} +1 -1
  43. package/dist/assets/{pegDiagram-GJSIUBJH-lx5Kj-n5.js → pegDiagram-GJSIUBJH-BmfYcY0O.js} +1 -1
  44. package/dist/assets/{pieDiagram-5QR66LMP-Bq7Vmm15.js → pieDiagram-5QR66LMP-DKvgP7w9.js} +1 -1
  45. package/dist/assets/{quadrantDiagram-O4NWA36T-DeqeqOBj.js → quadrantDiagram-O4NWA36T-BEKL3W0L.js} +1 -1
  46. package/dist/assets/{railroadDiagram-XR7U4H2S-CXwRQzSC.js → railroadDiagram-XR7U4H2S-BEsiqR5s.js} +1 -1
  47. package/dist/assets/{requirementDiagram-PLB6GJNP-8tAeoB88.js → requirementDiagram-PLB6GJNP-DWBcWvAP.js} +1 -1
  48. package/dist/assets/{sankeyDiagram-IPEJSGJF-BG5zjGWU.js → sankeyDiagram-IPEJSGJF-rpZ2_O4s.js} +1 -1
  49. package/dist/assets/{sequenceDiagram-PO4LG4MO-DKF3iLcw.js → sequenceDiagram-PO4LG4MO-B99HQdJR.js} +1 -1
  50. package/dist/assets/{sizeCapture-INFHLROL-B-y1qUgT.js → sizeCapture-INFHLROL-BjN0qT5T.js} +1 -1
  51. package/dist/assets/{stateDiagram-v2-GCMORJYK-MkzPgxAc.js → stateDiagram-v2-GCMORJYK-BbsVgGsC.js} +1 -1
  52. package/dist/assets/{swimlanes-2SLR337P-DnbaOEwH.js → swimlanes-2SLR337P-DG3gKkeR.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-TC7HE7FX-BCZhxl7e.js +8 -0
  54. package/dist/assets/{timeline-definition-EJHVYXUP-Bb4xG_Bt.js → timeline-definition-EJHVYXUP-BTHOdtF_.js} +1 -1
  55. package/dist/assets/{usecaseDiagram-POWQR4AR-JgkR9pFf.js → usecaseDiagram-POWQR4AR-acLSLFwc.js} +1 -1
  56. package/dist/assets/{vennDiagram-UO4OBE2U-CIPIrAYe.js → vennDiagram-UO4OBE2U-C5JzNX9G.js} +1 -1
  57. package/dist/assets/{wardleyDiagram-VNRHLVJA-BjCLuUvJ.js → wardleyDiagram-VNRHLVJA-DL1-PrR_.js} +1 -1
  58. package/dist/assets/{xychartDiagram-PMCCYNJV-DKgiaYxT.js → xychartDiagram-PMCCYNJV-Bvr1477p.js} +1 -1
  59. package/dist/index.html +2 -2
  60. package/dist-server/cloud-home-start.js +18 -3
  61. package/dist-server/container-mcp.js +84 -32
  62. package/dist-server/drivers/agents-proxy.js +11 -0
  63. package/dist-server/index.js +25183 -23580
  64. package/dist-server/openmausbot.js +313 -159
  65. package/dist-server/pair-cli.js +313 -159
  66. package/dist-server/server/boat.js +66 -21
  67. package/dist-server/server/bot-overview.js +7 -4
  68. package/dist-server/server/cloud-home-start.js +23 -5
  69. package/dist-server/server/cloud-home.js +25 -2
  70. package/dist-server/server/cloud-lending.js +103 -0
  71. package/dist-server/server/cloud-move-http.js +268 -0
  72. package/dist-server/server/cloud-move.js +418 -0
  73. package/dist-server/server/config.js +42 -4
  74. package/dist-server/server/decider/index.js +238 -0
  75. package/dist-server/server/decider/jev.js +183 -0
  76. package/dist-server/server/decider/log.js +62 -0
  77. package/dist-server/server/decider/relay.js +52 -0
  78. package/dist-server/server/decider/room-routing.js +88 -0
  79. package/dist-server/server/decider/types.js +5 -0
  80. package/dist-server/server/drivers/agents-catalog.js +16 -0
  81. package/dist-server/server/drivers/boatagent.js +20 -12
  82. package/dist-server/server/drivers/claude.js +1 -0
  83. package/dist-server/server/group-goal-run.js +6 -4
  84. package/dist-server/server/harness-capabilities.js +2 -0
  85. package/dist-server/server/included-services.js +70 -0
  86. package/dist-server/server/index.js +438 -102
  87. package/dist-server/server/model-context-window.js +4 -4
  88. package/dist-server/server/package-export.js +17 -10
  89. package/dist-server/server/package-import.js +6 -3
  90. package/dist-server/server/package-parts.js +3 -1
  91. package/dist-server/server/request-auth.js +2 -0
  92. package/dist-server/server/routes/decider.js +19 -0
  93. package/dist-server/server/shared-computers.js +116 -30
  94. package/dist-server/server/store.js +17 -2
  95. package/dist-server/server/surface.js +13 -0
  96. package/dist-server/server/system-prompt.js +11 -0
  97. package/dist-server/server/team-backup.js +6 -2
  98. package/dist-server/server/team-manifest.js +11 -3
  99. package/dist-server/server/tts/elevenlabs.js +11 -6
  100. package/dist-server/server/tts/index.js +17 -10
  101. package/dist-server/server/workspace-backup-policy.js +22 -3
  102. package/dist-server/server/workspace-backup.js +25 -8
  103. package/dist-server/shared/cloud-home.js +4 -0
  104. package/dist-server/shared/package-format.js +30 -4
  105. package/dist-server/shared/team-backup.js +20 -3
  106. package/dist-server/vps-container-mcp.js +84 -32
  107. package/dist-server/workspace-backup.worker.js +113 -31
  108. package/package.json +1 -1
  109. package/dist/assets/channel-BCuyHjFV.js +0 -1
  110. package/dist/assets/index-CRvo-DS2.css +0 -1
  111. package/dist/assets/index-CWYGlA63.js +0 -588
  112. package/dist/assets/swimlanesDiagram-TC7HE7FX-BZQ07DGE.js +0 -8
@@ -1,10 +1,9 @@
1
1
  import { newEventId, newId } from "../contracts.js";
2
+ import { boatCredential, boatProviderApi } from "../included-services.js";
2
3
  import { appendNative } from "./native.js";
3
4
  import { OMB_ASK_TOOL, answerWithoutPreamble, askQuestionSummary, capAnswerEcho, ombAskProtocolPrompt, parseOmbAskQuestions, questionChoices, stripOmbAskBlock, } from "../../shared/ask-question.js";
4
5
  // "boxAgent" is Boat's historical driver kind (catalog and wire); keep it.
5
6
  const DRIVER_KIND = "boxAgent";
6
- // overridable so tests and a dev backend can be pointed at instead of the live provider
7
- const BOAT_API = process.env.OMB_BOX_API || "https://ascii.dev/api/box/v1";
8
7
  const MODELS = {
9
8
  default: "claude-fable-5",
10
9
  options: [
@@ -20,10 +19,15 @@ const ASK_FENCE_ANY = /(^|\n)[ \t]{0,3}(`{3,}|~{3,})[ \t]*omb-ask\b/;
20
19
  * prime-agent, kimi). Which one a model id belongs to comes from the public
21
20
  * catalog, `GET /api/provider-models` at the API root: an object keyed by
22
21
  * harness, each with its `models`. A bot that arrives here from another engine
23
- * carries that engine's model id, so this is what lets it keep its model. */
22
+ * carries that engine's model id, so this is what lets it keep its model. The
23
+ * root is the API in use (Boat, or Cloud Pro's relay), so it is cached per root. */
24
24
  let catalog = null;
25
- async function loadCatalog() {
26
- const root = BOAT_API.replace(/\/api\/box\/v1\/?$/, "");
25
+ let catalogRoot = "";
26
+ async function loadCatalog(api) {
27
+ const root = api.replace(/\/api\/box\/v1\/?$/, "");
28
+ if (catalog && catalogRoot === root)
29
+ return;
30
+ catalogRoot = root;
27
31
  catalog = await fetch(`${root}/api/provider-models`, { signal: AbortSignal.timeout(15_000) })
28
32
  .then((res) => (res.ok ? res.json() : null))
29
33
  .catch(() => null);
@@ -53,7 +57,11 @@ export const BoatAgentDriver = {
53
57
  defaultConfig: () => decodeConfig({}),
54
58
  async create(input) {
55
59
  const { instanceId, config } = input;
56
- const token = input.environment.BOX_TOKEN ?? process.env.BOX_TOKEN ?? "";
60
+ // The person's own token (instanceConfigs injects it), else Cloud Pro's
61
+ // included one, with the one base URL each may be sent to. Resolved per
62
+ // request, like every other Boat call.
63
+ const ownToken = input.environment.BOX_TOKEN ?? process.env.BOX_TOKEN;
64
+ const account = () => boatCredential(ownToken);
57
65
  const listeners = new Set();
58
66
  const active = new Map();
59
67
  /** One open ask per thread: the turn-held transport's pending card. The
@@ -75,9 +83,10 @@ export const BoatAgentDriver = {
75
83
  createdAt: new Date().toISOString(),
76
84
  });
77
85
  const api = async (path, opts = {}) => {
78
- const res = await fetch(`${BOAT_API}${path}`, {
86
+ const credential = account();
87
+ const res = await fetch(`${credential?.api ?? boatProviderApi()}${path}`, {
79
88
  ...opts,
80
- headers: { authorization: `Bearer ${token}`, "content-type": "application/json", ...opts.headers },
89
+ headers: { authorization: `Bearer ${credential?.token ?? ""}`, "content-type": "application/json", ...opts.headers },
81
90
  signal: opts.signal ?? AbortSignal.timeout(30_000),
82
91
  });
83
92
  const body = await res.json().catch(() => null);
@@ -90,7 +99,7 @@ export const BoatAgentDriver = {
90
99
  const { threadId } = turn;
91
100
  const computer = turn.integrations?.computer;
92
101
  const boxId = computer && (!computer.kind || computer.kind === "box") ? computer.boxId : undefined;
93
- if (!token)
102
+ if (!account())
94
103
  throw new Error('box not configured — add {"box":{"token":"…"}} to ~/.openmausbot/config.json');
95
104
  if (!boxId) {
96
105
  throw new Error("this bot has no computer yet — open the Computer panel and provision one");
@@ -113,8 +122,7 @@ export const BoatAgentDriver = {
113
122
  .filter((s) => s !== undefined)
114
123
  .join("\n");
115
124
  const postPrompt = async (promptText) => {
116
- if (!catalog)
117
- await loadCatalog();
125
+ await loadCatalog(account()?.api ?? boatProviderApi());
118
126
  const started = await api(`/boxes/${boxId}/prompt`, {
119
127
  method: "POST",
120
128
  body: JSON.stringify({ ...providerFor(model), prompt: promptText }),
@@ -355,7 +363,7 @@ export const BoatAgentDriver = {
355
363
  return { turnId };
356
364
  };
357
365
  const snapshot = async () => {
358
- if (!token) {
366
+ if (!account()) {
359
367
  return { state: "unavailable", reason: 'no Boat token — add {"box":{"token":"…"}} to ~/.openmausbot/config.json' };
360
368
  }
361
369
  try {
@@ -328,6 +328,7 @@ export const STATIC_CLAUDE_MODELS = {
328
328
  { id: "claude-fable-5", label: "Claude Fable 5" },
329
329
  { id: "claude-opus-5-5", label: "Claude Opus 5.5", contextWindow: 1_000_000 },
330
330
  { id: "claude-opus-5", label: "Claude Opus 5" },
331
+ { id: "claude-sonnet-5-5", label: "Claude Sonnet 5.5", contextWindow: 1_000_000 },
331
332
  { id: "claude-sonnet-5", label: "Claude Sonnet 5" },
332
333
  { id: "claude-haiku-4-5", label: "Claude Haiku 4.5" },
333
334
  ],
@@ -66,12 +66,14 @@ export function parseGroupGoalDecision(text) {
66
66
  }
67
67
  return { visibleText, decision: null };
68
68
  }
69
- /** Explicit room lead wins. Otherwise prefer an in-room Chief, then the
70
- * first active member. A goal run never recruits somebody outside the room. */
69
+ /** Explicit room lead (or an Auto room's fallback) wins. Otherwise prefer
70
+ * an in-room Chief, then the first active member. A goal run never recruits
71
+ * somebody outside the room. */
71
72
  export function selectGroupGoalCoordinator(members, responder) {
72
73
  const active = members.filter((member) => !member.hidden);
73
- if (responder.kind === "member") {
74
- const explicit = active.find((member) => member.id === responder.botId);
74
+ const lead = responder.kind === "member" ? responder.botId : responder.kind === "auto" ? responder.fallbackBotId : undefined;
75
+ if (lead) {
76
+ const explicit = active.find((member) => member.id === lead);
75
77
  if (explicit)
76
78
  return explicit;
77
79
  }
@@ -20,6 +20,8 @@ const ENVELOPE = {
20
20
  skillAuthoring: true,
21
21
  sharedComputers: true,
22
22
  voiceNotes: true,
23
+ // Not a tool it adds: a Cloud home only hides its Local VM shell.
24
+ cloudHome: false,
23
25
  botId: "",
24
26
  };
25
27
  /** Extra card lines for an engine switch, empty when nothing changes. */
@@ -0,0 +1,70 @@
1
+ // Boat cloud computers, ElevenLabs voice and the Jev decision model included
2
+ // with Cloud Pro (docs/cloud-pro.md, "Included Boat computers, voice and
3
+ // decisions").
4
+ // The Admin relays each on its own accounts, so its provider keys never reach
5
+ // this machine; each Cloud home gets its own relay tokens:
6
+ //
7
+ // OMB_CLOUD_BOAT_URL + OMB_CLOUD_BOAT_TOKEN the Boat relay, ending in /api/box/v1
8
+ // OMB_CLOUD_VOICE_URL + OMB_CLOUD_VOICE_TOKEN the ElevenLabs relay, ending in /v1
9
+ // OMB_CLOUD_DECIDER_URL + OMB_CLOUD_DECIDER_TOKEN the Jev relay, a Jev base URL
10
+ //
11
+ // An included token is only a fallback. The person's own key (Settings, or
12
+ // BOX_TOKEN / OMB_TTS_KEY / OMB_JEV_API_KEY) always wins, and removing it
13
+ // falls back again, so the credential is resolved on every request, never
14
+ // cached. The relay knows only our account, so each credential goes to one
15
+ // place: an own key to the provider (OMB_BOX_API / OMB_ELEVENLABS_API /
16
+ // decider.baseUrl when set, for dev and tests), an included token to its
17
+ // relay. An included token is never written to config.json, never reported
18
+ // to a client, and never the saved key Settings verifies, rotates or clears.
19
+ import { JEV_DEFAULT_BASE_URL } from "./decider/jev.js";
20
+ export const BOAT_API_DEFAULT = "https://ascii.dev/api/box/v1";
21
+ export const ELEVENLABS_API_DEFAULT = "https://api.elevenlabs.io/v1";
22
+ /** Also on WORKSPACE_CREDENTIAL_ENV (config.ts). */
23
+ export const INCLUDED_TOKEN_ENV = ["OMB_CLOUD_BOAT_TOKEN", "OMB_CLOUD_VOICE_TOKEN", "OMB_CLOUD_DECIDER_TOKEN"];
24
+ function includedService(url, token) {
25
+ const api = url?.trim().replace(/\/+$/, "");
26
+ const secret = token?.trim();
27
+ return api && secret ? { token: secret, api, included: true } : null;
28
+ }
29
+ const includedFrom = (env) => ({
30
+ boat: includedService(env.OMB_CLOUD_BOAT_URL, env.OMB_CLOUD_BOAT_TOKEN),
31
+ voice: includedService(env.OMB_CLOUD_VOICE_URL, env.OMB_CLOUD_VOICE_TOKEN),
32
+ // A Jev base URL as it is: the decider adds /v1/systemone, the relay's one route.
33
+ decider: includedService(env.OMB_CLOUD_DECIDER_URL, env.OMB_CLOUD_DECIDER_TOKEN),
34
+ });
35
+ let held = null;
36
+ /** At server startup: keep the included tokens in memory and drop them from
37
+ * the environment, as with the Cloud bootstrap secret, so nothing the server
38
+ * starts can inherit them. Before this (tests, scripts) they are read from
39
+ * the environment on each call. */
40
+ export function holdIncludedServices(env = process.env) {
41
+ held = includedFrom(env);
42
+ for (const name of INCLUDED_TOKEN_ENV)
43
+ delete env[name];
44
+ }
45
+ function resolve(own, providerApi, included) {
46
+ if (own) {
47
+ // A leased computer descriptor hands the token in use back as a plain
48
+ // token; the included one still goes only to its relay.
49
+ if (included && own === included.token)
50
+ return included;
51
+ return { token: own, api: providerApi, included: false };
52
+ }
53
+ return included;
54
+ }
55
+ export const boatProviderApi = (env = process.env) => env.OMB_BOX_API || BOAT_API_DEFAULT;
56
+ export const elevenLabsProviderApi = (env = process.env) => env.OMB_ELEVENLABS_API || ELEVENLABS_API_DEFAULT;
57
+ /** The Boat credential in use: the person's own token, else the included one. */
58
+ export function boatCredential(own, env = process.env) {
59
+ return resolve(own, boatProviderApi(env), (held ?? includedFrom(env)).boat);
60
+ }
61
+ /** The ElevenLabs credential in use: the person's own key, else the included one. */
62
+ export function voiceCredential(own, env = process.env) {
63
+ return resolve(own, elevenLabsProviderApi(env), (held ?? includedFrom(env)).voice);
64
+ }
65
+ /** The decision model's credential in use: the person's own Jev key (saved,
66
+ * or OMB_JEV_API_KEY) with their `decider.baseUrl` or Jev's own, else the
67
+ * included token with the relay. `api` is a Jev base URL. */
68
+ export function deciderCredential(own, ownBaseUrl, env = process.env) {
69
+ return resolve(own?.trim(), ownBaseUrl?.trim() || JEV_DEFAULT_BASE_URL, (held ?? includedFrom(env)).decider);
70
+ }