clearotron 0.3.2-beta.7 → 0.3.2-beta.8

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 (85) hide show
  1. package/.env.example +24 -23
  2. package/INSTALL.md +142 -75
  3. package/README.md +3 -3
  4. package/bin/onboard.mjs +637 -216
  5. package/bin/start.mjs +133 -23
  6. package/bin/update.mjs +58 -11
  7. package/build-info.json +2 -2
  8. package/docs/architecture/04-configuration-reference.md +26 -11
  9. package/docs/architecture/05-config-governance.md +17 -7
  10. package/driver/CHANGELOG.md +76 -0
  11. package/driver/band-size.mjs +59 -0
  12. package/driver/config-inventory.mjs +112 -9
  13. package/driver/contract-arm2-baseline.json +1 -3
  14. package/driver/contract-e3-backlog.mjs +26 -26
  15. package/driver/contract-vocabulary.mjs +44 -10
  16. package/driver/door-gates.mjs +41 -7
  17. package/driver/driver.config.mjs +272 -59
  18. package/driver/engine/CONTRACT.md +10 -3
  19. package/driver/engine/README.md +2 -2
  20. package/driver/engine/anthropic-agent.mjs +77 -21
  21. package/driver/engine/auth.mjs +129 -10
  22. package/driver/engine/jx-turn.mjs +7 -6
  23. package/driver/engine/mcp/recording-server.mjs +13 -0
  24. package/driver/engine/openai-agent.mjs +4 -2
  25. package/driver/engine/probe.mjs +110 -23
  26. package/driver/findings-model.mjs +1 -1
  27. package/driver/flag-snapshot.mjs +28 -5
  28. package/driver/gateway.mjs +24 -18
  29. package/driver/jx-lanes.mjs +21 -2
  30. package/driver/jx-units.mjs +6 -3
  31. package/driver/jx.mjs +4 -2
  32. package/driver/matter-frame-record.mjs +90 -1
  33. package/driver/named-band.mjs +34 -2
  34. package/driver/package.json +1 -1
  35. package/driver/pipeline.mjs +200 -23
  36. package/driver/portal-config-view.mjs +30 -1
  37. package/driver/portal-report.mjs +15 -1
  38. package/driver/portal-service.mjs +46 -6
  39. package/driver/predelivery-lint.mjs +12 -2
  40. package/driver/publish/index.mjs +46 -5
  41. package/driver/publish/knockout.mjs +10 -1
  42. package/driver/publish/render-knockout.mjs +69 -7
  43. package/driver/publish/render.mjs +170 -59
  44. package/driver/publish/report-data.mjs +4 -1
  45. package/driver/publish/report-topbar.mjs +58 -0
  46. package/driver/publish/templates/report.css +18 -1
  47. package/driver/publish/xlsx.mjs +13 -1
  48. package/driver/register-availability.mjs +2 -2
  49. package/driver/register-coverage.mjs +94 -1
  50. package/driver/register-digest-record.mjs +236 -11
  51. package/driver/register-plan.mjs +170 -0
  52. package/driver/result-noun-fields.mjs +2 -2
  53. package/driver/run-economics.mjs +41 -10
  54. package/driver/run-requirements.mjs +173 -9
  55. package/driver/runner.mjs +3 -3
  56. package/driver/stages.mjs +12 -8
  57. package/driver/suite-census.json +142 -64
  58. package/driver/systemd/README.md +7 -4
  59. package/driver/terminal-clamp.mjs +107 -1
  60. package/driver/tokens.mjs +169 -3
  61. package/driver/unit-environment.mjs +42 -15
  62. package/driver/unit-inventory.mjs +19 -2
  63. package/driver/verify.mjs +27 -0
  64. package/mcp-server/CHANGELOG.md +4 -0
  65. package/mcp-server/package.json +1 -1
  66. package/mcp-server/server.mjs +15 -1
  67. package/package.json +1 -1
  68. package/portal-ui/dist/assets/{index-5UyqAyNM.js → index-6jzO9HiX.js} +155 -79
  69. package/portal-ui/dist/index.html +1 -1
  70. package/portal-ui/package.json +1 -1
  71. package/providers/jx/README.md +2 -1
  72. package/providers/jx/src/turn-envelope.mjs +8 -3
  73. package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
  74. package/providers/oauth-mcp-bridge/package.json +1 -1
  75. package/providers/uspto-local/README.md +1 -1
  76. package/scripts/authority-boundary-probe.mjs +4 -2
  77. package/scripts/env-audit.mjs +12 -6
  78. package/scripts/freeze-example-run.mjs +49 -16
  79. package/scripts/generated-files-are-current.mjs +69 -4
  80. package/scripts/settings-render-check.mjs +75 -2
  81. package/scripts/test-full.mjs +96 -3
  82. package/scripts/test-run.mjs +10 -0
  83. package/shared/deployment-box.mjs +7 -2
  84. package/shared/driver-dir.mjs +1 -1
  85. package/shared/names-in-force.mjs +1 -1
@@ -11926,6 +11926,7 @@ var api = {
11926
11926
  };
11927
11927
  }),
11928
11928
  ...!("territories" in b) ? {} : { registerTerritories: b["territories"] === null ? null : asArray(b["territories"]).filter((t) => typeof t === "string") },
11929
+ ...asString(b["registerLabel"]) ? { registerLabel: asString(b["registerLabel"]) } : {},
11929
11930
  recipes: asArray(b["recipes"]).map((x) => {
11930
11931
  const r = x;
11931
11932
  return {
@@ -12193,11 +12194,38 @@ var api = {
12193
12194
  billing: {
12194
12195
  mode: asString(bill["mode"]) ?? "unknown",
12195
12196
  apiBilled: bill["apiBilled"] === true,
12196
- missing: asStrings(bill["missing"])
12197
+ missing: asStrings(bill["missing"]),
12198
+ cloud: asString(bill["cloud"]),
12199
+ cloudName: asString(bill["cloudName"]),
12200
+ reason: bill["reason"] != null ? (() => {
12201
+ const raw = bill["reason"];
12202
+ const r = typeof raw === "object" && !Array.isArray(raw) ? asRecord(raw) : {};
12203
+ return {
12204
+ kind: asString(r["kind"]) ?? "unclassified",
12205
+ mode: asString(r["mode"]),
12206
+ defaulted: r["defaulted"] === true,
12207
+ setting: asString(r["setting"]) ?? "",
12208
+ clouds: asArray(r["clouds"]).flatMap((c) => {
12209
+ const x = asRecord(c);
12210
+ const name = asString(x["name"]);
12211
+ const setting = asString(x["setting"]);
12212
+ return name && setting ? [{
12213
+ name,
12214
+ setting
12215
+ }] : [];
12216
+ }),
12217
+ modes: asStrings(r["modes"]),
12218
+ gateway: asString(r["gateway"]),
12219
+ engineSetting: asString(r["engineSetting"]),
12220
+ engineChoice: asString(r["engineChoice"])
12221
+ };
12222
+ })() : null
12197
12223
  },
12198
12224
  binaryPresent: e["binaryPresent"] === true,
12199
12225
  program: asString(e["program"]),
12200
- install: asString(e["install"])
12226
+ install: asString(e["install"]),
12227
+ programSetting: asString(e["programSetting"]),
12228
+ setupRoute: e["setupRoute"] === "packaged" ? "packaged" : e["setupRoute"] === "checkout" ? "checkout" : null
12201
12229
  };
12202
12230
  })() : null,
12203
12231
  providers: Array.isArray(b["providers"]) ? asArray(b["providers"]).map((p) => {
@@ -12330,13 +12358,6 @@ var NAV = [
12330
12358
  icon: "panel-left",
12331
12359
  scope: "account"
12332
12360
  },
12333
- {
12334
- id: "ai",
12335
- label: "Connect your AI",
12336
- path: "/portal/ai",
12337
- icon: "sparkles",
12338
- scope: "account"
12339
- },
12340
12361
  {
12341
12362
  id: "people",
12342
12363
  label: "People",
@@ -12424,6 +12445,14 @@ var NAV = [
12424
12445
  }
12425
12446
  ]
12426
12447
  },
12448
+ {
12449
+ id: "ai",
12450
+ label: "Connect your AI",
12451
+ path: "/portal/ai",
12452
+ icon: "sparkles",
12453
+ scope: "account",
12454
+ rail: "below"
12455
+ },
12427
12456
  {
12428
12457
  id: "new-company",
12429
12458
  label: "New company",
@@ -12463,11 +12492,12 @@ function navFor(who, entries = NAV) {
12463
12492
  children: e.children.filter((c) => visible(c, who) && !c.hidden)
12464
12493
  } : e);
12465
12494
  }
12495
+ var railSideOf = (e) => e.rail ?? ((e.scope ?? "account") === "account" ? "above" : "below");
12466
12496
  function navGroupsFor(who, entries = NAV) {
12467
12497
  const visibleEntries = navFor(who, entries);
12468
12498
  return {
12469
- account: visibleEntries.filter((e) => (e.scope ?? "account") === "account"),
12470
- owner: visibleEntries.filter((e) => e.scope === "owner")
12499
+ account: visibleEntries.filter((e) => railSideOf(e) === "above"),
12500
+ owner: visibleEntries.filter((e) => railSideOf(e) === "below")
12471
12501
  };
12472
12502
  }
12473
12503
  function scopeOf(id, entries = NAV) {
@@ -13818,6 +13848,7 @@ function readableFailure(failedStage, reason) {
13818
13848
  var EMPTY_DRAFT = {
13819
13849
  product: null,
13820
13850
  territories: [],
13851
+ replacesOwnTerritories: false,
13821
13852
  nativeLanguage: false
13822
13853
  };
13823
13854
  var REGIONS = [
@@ -13941,9 +13972,6 @@ function reachesTerritory(name, covered) {
13941
13972
  if (!Array.isArray(covered)) return true;
13942
13973
  return covered.includes(name);
13943
13974
  }
13944
- function vocabularyFor(product, covered) {
13945
- return offerableFor(product).filter((n) => reachesTerritory(n, covered));
13946
- }
13947
13975
  function territoryMatches(query, chosen, product, limit = 8, _covered) {
13948
13976
  return matchTerritoriesIn(offerableFor(product), query, chosen, limit);
13949
13977
  }
@@ -13959,8 +13987,9 @@ function matchTerritoriesIn(vocabulary, query, chosen, limit = 8) {
13959
13987
  };
13960
13988
  return vocabulary.filter((n) => !taken.has(n.toLowerCase()) && hit(n)).slice(0, limit);
13961
13989
  }
13962
- function addTerritory(d, name, product, _covered) {
13990
+ function addTerritory(d, name, product, covered) {
13963
13991
  if (!offerableFor(product).includes(name)) return d;
13992
+ if (!reachesTerritory(name, covered)) return d;
13964
13993
  if (d.territories.includes(name)) return d;
13965
13994
  if (product?.geography === "exactly one country") return {
13966
13995
  ...d,
@@ -13977,14 +14006,35 @@ function removeTerritory(d, name) {
13977
14006
  territories: d.territories.filter((t) => t !== name)
13978
14007
  };
13979
14008
  }
13980
- function geographyFor(d) {
13981
- return d.territories.length ? {
14009
+ function notAvailableLine(names, registerLabel) {
14010
+ const named = names.join(", ");
14011
+ const who = typeof registerLabel === "string" && registerLabel.trim() ? registerLabel.trim() : null;
14012
+ return `${named ? `${named} \u00b7 ` : ""}not available${who ? ` with register ${who}` : ""}`;
14013
+ }
14014
+ function takeOverOwnTerritories(d, own, without) {
14015
+ return {
14016
+ ...d,
14017
+ territories: own.filter((t) => t !== without),
14018
+ replacesOwnTerritories: true
14019
+ };
14020
+ }
14021
+ function geographyFor(d, product = null, resolved = []) {
14022
+ if (product?.geography === "worldwide, and nothing else") return {
14023
+ mode: "worldwide",
14024
+ territories: []
14025
+ };
14026
+ if (d.territories.length) return {
13982
14027
  mode: "named",
13983
14028
  territories: [...d.territories]
13984
- } : {
14029
+ };
14030
+ if (d.replacesOwnTerritories) return {
13985
14031
  mode: "worldwide",
13986
14032
  territories: []
13987
14033
  };
14034
+ return {
14035
+ mode: "account-default",
14036
+ territories: [...resolved]
14037
+ };
13988
14038
  }
13989
14039
  function nativeLanguageControl(product) {
13990
14040
  if (product?.nativeLanguage === "offered") return "toggle";
@@ -14003,6 +14053,7 @@ function chooseProduct(d, product) {
14003
14053
  return {
14004
14054
  product: product?.key ?? null,
14005
14055
  territories,
14056
+ replacesOwnTerritories: d.replacesOwnTerritories,
14006
14057
  nativeLanguage: nativeLanguageControl(product) === "toggle" ? d.nativeLanguage : false
14007
14058
  };
14008
14059
  }
@@ -14485,6 +14536,7 @@ function draftFromSaved(recipe, products) {
14485
14536
  return chooseProduct({
14486
14537
  product: base,
14487
14538
  territories: Array.isArray(scope["jurisdictions"]) ? scope["jurisdictions"].filter((j) => typeof j === "string") : [],
14539
+ replacesOwnTerritories: false,
14488
14540
  nativeLanguage: recipe["nativeLanguage"] === true
14489
14541
  }, product);
14490
14542
  }
@@ -15510,7 +15562,7 @@ function resumeWording(iso) {
15510
15562
  });
15511
15563
  return t.toDateString() === new Date().toDateString() ? `at ${time}` : `${t.toLocaleDateString([], { weekday: "long" })} at ${time}`;
15512
15564
  }
15513
- function StatusCell({ state, step, stepN, stepTotal, reason, failedStage, pausedKind = null, resetsAt = null, stopRequestedAt = null, detailed = false, count = null }) {
15565
+ function StatusCell({ state, step, reason, failedStage, pausedKind = null, resetsAt = null, stopRequestedAt = null, detailed = false, count = null }) {
15514
15566
  if (stopRequestedAt && state !== "delivered" && state !== "failed" && state !== "cancelled") return (0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsxs)("span", {
15515
15567
  className: "status",
15516
15568
  children: [(0, import_jsx_runtime.jsx)("span", {
@@ -15577,7 +15629,7 @@ function StatusCell({ state, step, stepN, stepTotal, reason, failedStage, paused
15577
15629
  ] });
15578
15630
  }
15579
15631
  const label = state === "queued" ? "Queued" : state === "paused" ? "Paused" : "Running";
15580
- const detail = state === "paused" ? pausedKind === "recovering" ? "Held after a problem — retrying on its own" : pausedKind === "operator" ? "Paused by a system restart — resumes on its own" : resetsAt ? `Paused by a provider limit — resumes ${resumeWording(resetsAt)}` : "Paused by a provider limit — resumes on its own" : step ? stepN != null && stepTotal != null ? `${step} · ${stepN} of ${stepTotal}` : step : null;
15632
+ const detail = state === "paused" ? pausedKind === "recovering" ? "Held after a problem — retrying on its own" : pausedKind === "operator" ? "Paused by a system restart — resumes on its own" : resetsAt ? `Paused by a provider limit — resumes ${resumeWording(resetsAt)}` : "Paused by a provider limit — resumes on its own" : step;
15581
15633
  return (0, import_jsx_runtime.jsxs)("span", { children: [(0, import_jsx_runtime.jsxs)("span", {
15582
15634
  className: "status",
15583
15635
  children: [(0, import_jsx_runtime.jsx)("span", { className: `dot ${state}` }), count ? (0, import_jsx_runtime.jsxs)("span", { children: [
@@ -15602,7 +15654,7 @@ function allowanceLine(usage, brand) {
15602
15654
  if (left == null) return null;
15603
15655
  if (left > 5) return null;
15604
15656
  const who = operatorName(brand);
15605
- if (left === 0) return `You have used all ${usage?.dailyRuns ?? 0} of this account's searches for today. The allowance resets at midnight UTC — or ask your ${who} contact to run this one for you.`;
15657
+ if (left === 0) return `You have used all ${usage?.dailyRuns ?? 0} of this account's searches for today. The allowance resets at midnight UTC — or ask ${who} to run this one for you.`;
15606
15658
  return `${left} search${left === 1 ? "" : "es"} left today. The allowance resets at midnight UTC, or ask ${who} to run one for you.`;
15607
15659
  }
15608
15660
  //#endregion
@@ -16688,8 +16740,6 @@ function FamilyRows({ family, isOpen, onToggle, go, picking, showOwner = false,
16688
16740
  }) : (0, import_jsx_runtime.jsx)(StatusCell, {
16689
16741
  state: family.state,
16690
16742
  step: null,
16691
- stepN: null,
16692
- stepTotal: null,
16693
16743
  reason: null,
16694
16744
  failedStage: null
16695
16745
  }) }),
@@ -16798,8 +16848,6 @@ function MarkRow({ mark, open, onToggle, go, indent = false, picking = false, sh
16798
16848
  (0, import_jsx_runtime.jsx)("td", { children: (0, import_jsx_runtime.jsx)(StatusCell, {
16799
16849
  state: run.state,
16800
16850
  step: run.step,
16801
- stepN: run.stepN,
16802
- stepTotal: run.stepTotal,
16803
16851
  reason: run.reason,
16804
16852
  failedStage: run.failedStage,
16805
16853
  pausedKind: run.pausedKind,
@@ -16936,8 +16984,6 @@ function ReadRow({ read, go, picking, indent, showOwner = false, latest = false,
16936
16984
  (0, import_jsx_runtime.jsx)("td", { children: (0, import_jsx_runtime.jsx)(StatusCell, {
16937
16985
  state: read.state,
16938
16986
  step: read.step,
16939
- stepN: read.stepN,
16940
- stepTotal: read.stepTotal,
16941
16987
  reason: read.reason,
16942
16988
  failedStage: read.failedStage,
16943
16989
  stopRequestedAt: read.stopRequestedAt,
@@ -17764,26 +17810,68 @@ function Missing({ go, reason }) {
17764
17810
  }
17765
17811
  //#endregion
17766
17812
  //#region src/contract/engineState.ts
17813
+ var SETUP_COMMAND = "`npx clearotron install`";
17814
+ var listed = (xs, and = "and") => xs.length <= 1 ? xs[0] ?? "" : `${xs.slice(0, -1).join(", ")} ${and} ${xs[xs.length - 1]}`;
17815
+ var PAID_BY = {
17816
+ subscription: "subscription",
17817
+ "api-key": "an API key",
17818
+ cloud: "a cloud account"
17819
+ };
17820
+ var paidBy = (mode) => mode !== null && Object.hasOwn(PAID_BY, mode) ? PAID_BY[mode] : mode ?? "a way of paying";
17821
+ var COUNT = [
17822
+ "no",
17823
+ "one",
17824
+ "two",
17825
+ "three",
17826
+ "four"
17827
+ ];
17828
+ var NAMES_CLOUDS = new Set([
17829
+ "switch-beside-mode",
17830
+ "two-clouds",
17831
+ "no-cloud"
17832
+ ]);
17833
+ function billingRefusalSentence(r) {
17834
+ const setting = r.setting || "the billing setting";
17835
+ const generic = `Searches will be refused over how this machine is set to pay. Check ${setting} and the settings beside it.`;
17836
+ if (NAMES_CLOUDS.has(r.kind) && r.clouds.length === 0) return generic;
17837
+ const paid = r.defaulted ? `${paidBy(r.mode)}, the default while ${setting} is not set` : `set to ${paidBy(r.mode)}`;
17838
+ switch (r.kind) {
17839
+ case "switch-beside-mode": {
17840
+ const one = r.clouds.length === 1;
17841
+ const names = listed(r.clouds.map((c) => c.name));
17842
+ const switches = listed(r.clouds.map((c) => c.setting));
17843
+ const keep = r.mode === "api-key" ? "to pay with the API key" : "to pay by subscription";
17844
+ return `Searches will be refused: payment is ${r.defaulted ? "by " : ""}${paid}, but the ${names} ${one ? "switch" : "switches"} (${switches}) ${one ? "is" : "are"} also on. ` + (one ? `Turn ${switches} off ${keep}, or set ${setting} to cloud to pay through ${names}.` : `Turn them off ${keep}, or set ${setting} to cloud and leave one on.`);
17845
+ }
17846
+ case "two-clouds": return `Searches will be refused: payment is set to a cloud account, but ${COUNT[r.clouds.length] ?? r.clouds.length} clouds are switched on, ${listed(r.clouds.map((c) => `${c.name} (${c.setting})`))}. Turn off all but the one you pay through.`;
17847
+ case "no-cloud": return `Searches will be refused: payment is set to a cloud account, but no cloud is chosen. Turn on your cloud's switch (${listed(r.clouds.map((c) => `${c.setting} for ${c.name}`), "or")}) with the settings that cloud needs` + (r.gateway ? `, set ${r.gateway} for a gateway,` : ",") + ` or set ${setting} to subscription.`;
17848
+ case "not-a-mode": return `Searches will be refused: ${setting} is set to a word Clearotron does not know. Set it to ${listed(r.modes, "or")}.`;
17849
+ case "cloud-on-codex": return `Searches will be refused: payment is set to a cloud account, which pays only for Claude, and this machine runs the Codex engine. Set ${setting} to ${listed(r.modes, "or")}` + (r.engineSetting && r.engineChoice ? `, or set ${r.engineSetting} to ${r.engineChoice} to pay for Claude through your cloud.` : ".");
17850
+ default: return generic;
17851
+ }
17852
+ }
17767
17853
  function engineRowFaults(engine, { programDisputed = false } = {}) {
17854
+ const reason = engine.billing.reason ?? null;
17768
17855
  return [
17769
17856
  ...engine.known ? [] : [`This build does not ship an engine called ${engine.id}.`],
17770
17857
  ...engine.billing.missing.length ? ["Set to bill an API key, and no key is set — a run is refused rather than billed to the subscription."] : [],
17771
- ...engine.binaryPresent ? [] : engine.program && engine.install ? [`The engine program \`${engine.program}\` cannot be found or run on this machine. Install it with \`${engine.install}\`, then restart this service so it looks for the program again.`] : ["The engine program cannot be found or run on this machine."],
17772
- ...programDisputed && engine.binaryPresent ? ["The engine program is on this machine, but the engine could not find it when it last started — so a new search will refuse. Restart the engine service, or install the CLI where the service can see it."] : []
17858
+ ...reason && !engine.billing.missing.length ? [billingRefusalSentence(reason)] : [],
17859
+ ...engine.binaryPresent ? [] : engine.program ? [`The engine program \`${engine.program}\` cannot be found or run on this machine. Run the setup wizard, ${SETUP_COMMAND}: it offers to install the program and proves it with one turn. Then restart Clearotron's services, which look for the program when they start.`] : ["The engine program cannot be found or run on this machine."],
17860
+ ...programDisputed && engine.binaryPresent ? [`The engine program is on this machine, but the engine could not find it when it last started — so a new search will refuse. Restart the engine service so it looks again. If it still cannot find it, set ${engine.programSetting ?? "the engine's program setting"} to the program's full path in the settings file the service reads, then restart it again.`] : []
17773
17861
  ];
17774
17862
  }
17775
17863
  function engineNotice({ programDisputed }) {
17776
17864
  if (programDisputed === true) return {
17777
17865
  state: "disputed",
17778
17866
  headline: "The engine program is on this machine, but the engine could not find it when it last started.",
17779
- before: "So a new search will refuse. Restart the engine service, or install the CLI where the service can see it — installing it again where it already is will not change this.",
17867
+ before: "So a new search will refuse. Restart the engine service so it looks again. If it still cannot find it, set the engine's program setting to the program's full path in the settings file the service reads — installing it again where it already is will not change this.",
17780
17868
  namesSetupCommand: false,
17781
17869
  after: ""
17782
17870
  };
17783
17871
  return {
17784
17872
  state: "absent",
17785
17873
  headline: "No search engine is attached to this install.",
17786
- before: "Everything else works — the example report, its audit trail and the assistant connection are live right now. To start new searches, install a reasoning CLI and sign in, then run the setup wizard again",
17874
+ before: "Everything else works — the example report, its audit trail and the assistant connection are live right now. The setup wizard offers to install a reasoning CLI if this machine has none, and asks how it is paid for. To start new searches, run the setup wizard",
17787
17875
  namesSetupCommand: true,
17788
17876
  after: "Then restart the service: this reading was taken when it started, and it will not notice a new install until it starts again."
17789
17877
  };
@@ -17794,7 +17882,7 @@ function engineRow(engine, { programDisputed = false } = {}) {
17794
17882
  ok: faults.length === 0,
17795
17883
  name: engine.vendor ?? engine.id,
17796
17884
  mono: engine.id,
17797
- state: engine.billing.apiBilled ? "API key" : "Subscription",
17885
+ state: engine.billing.missing.length || engine.billing.reason ? "Refused" : engine.billing.cloudName ?? (engine.billing.apiBilled ? "API key" : "Subscription"),
17798
17886
  faults
17799
17887
  };
17800
17888
  }
@@ -18270,6 +18358,7 @@ function NewClearance({ ctx }) {
18270
18358
  const { result: usageRes } = useLoad(() => api.usage(account), [account]);
18271
18359
  const levels = searches?.kind === "ok" ? searches.value.products : [];
18272
18360
  const registerTerritories = searches?.kind === "ok" ? searches.value.registerTerritories : void 0;
18361
+ const registerLabel = searches?.kind === "ok" ? searches.value.registerLabel : void 0;
18273
18362
  const savedSearches = searches?.kind === "ok" ? searches.value.recipes : [];
18274
18363
  const readCan = searches?.kind === "ok" ? searches.value.read : {
18275
18364
  available: false,
@@ -18387,10 +18476,11 @@ function NewClearance({ ctx }) {
18387
18476
  setSaveNote(null);
18388
18477
  };
18389
18478
  const setPick = (next) => edit({ pick: next });
18390
- const recommendation = (0, import_react.useMemo)(() => recommendSearch(levels, names.length, draft.pick.territories), [
18479
+ const recommendation = (0, import_react.useMemo)(() => recommendSearch(levels, names.length, draft.pick.territories.length ? draft.pick.territories : own.territories), [
18391
18480
  levels,
18392
18481
  names.length,
18393
- draft.pick.territories
18482
+ draft.pick.territories,
18483
+ own.territories
18394
18484
  ]);
18395
18485
  (0, import_react.useEffect)(() => {
18396
18486
  if (pickedByHand || draft.savedSearch || editingSlug || !recommendation) return;
@@ -18427,7 +18517,7 @@ function NewClearance({ ctx }) {
18427
18517
  deadline: d.deadline
18428
18518
  };
18429
18519
  let after = applyRead(before, read, own.classes, { worldwide });
18430
- const fits = recommendSearch(levels, parseNames(after.names).length, after.draft.territories);
18520
+ const fits = recommendSearch(levels, parseNames(after.names).length, after.draft.territories.length ? after.draft.territories : own.territories);
18431
18521
  if (read.product == null && !pickedByHand && !d.savedSearch && fits) after = {
18432
18522
  ...after,
18433
18523
  draft: chooseProduct(after.draft, fits.product)
@@ -18456,7 +18546,7 @@ function NewClearance({ ctx }) {
18456
18546
  ...draft.savedSearch ? { recipeKey: draft.savedSearch } : draft.pick.product ? { product: draft.pick.product } : {},
18457
18547
  ...draft.project ? { projectKey: draft.project } : {},
18458
18548
  ...draft.pick.territories.length ? { jurisdictions: [...draft.pick.territories] } : {},
18459
- geography: { mode: geographyFor(draft.pick).mode },
18549
+ geography: { mode: geographyFor(draft.pick, activeLevel, own.territories).mode },
18460
18550
  ...marketplacesApply && parseList(draft.platforms).length ? { platforms: parseList(draft.platforms) } : {},
18461
18551
  ...!draft.savedSearch && nativeControl === "toggle" && draft.pick.nativeLanguage ? { nativeLanguage: true } : {},
18462
18552
  ...draft.ref.trim() ? { ref: draft.ref.trim() } : {},
@@ -18795,10 +18885,20 @@ function NewClearance({ ctx }) {
18795
18885
  gap: 6,
18796
18886
  margin: "9px 0 12px"
18797
18887
  },
18798
- children: draft.pick.territories.length === 0 ? own.territories.length ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [own.territories.map((t) => (0, import_jsx_runtime.jsx)("span", {
18799
- className: "chip",
18888
+ children: draft.pick.territories.length === 0 ? own.territories.length ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [own.territories.map((t) => (0, import_jsx_runtime.jsxs)("span", {
18800
18889
  "data-anon": "mark",
18801
- children: t
18890
+ className: reachesTerritory(t, registerTerritories) ? "chip" : "chip chip-deferred",
18891
+ title: reachesTerritory(t, registerTerritories) ? void 0 : notAvailableLine([t], registerLabel),
18892
+ children: [reachesTerritory(t, registerTerritories) ? t : notAvailableLine([t], registerLabel), (0, import_jsx_runtime.jsx)("button", {
18893
+ type: "button",
18894
+ "aria-label": `Remove ${t}`,
18895
+ className: "chip-x",
18896
+ onClick: () => setPick(takeOverOwnTerritories(draft.pick, own.territories, t)),
18897
+ children: (0, import_jsx_runtime.jsx)(Icon, {
18898
+ name: "x",
18899
+ size: 13
18900
+ })
18901
+ })]
18802
18902
  }, t)), (0, import_jsx_runtime.jsx)("span", {
18803
18903
  style: {
18804
18904
  fontSize: 11,
@@ -18811,21 +18911,17 @@ function NewClearance({ ctx }) {
18811
18911
  children: "Worldwide"
18812
18912
  }) : draft.pick.territories.map((t) => (0, import_jsx_runtime.jsxs)("span", {
18813
18913
  className: reachesTerritory(t, registerTerritories) ? "chip chip-own" : "chip chip-own chip-deferred",
18814
- title: reachesTerritory(t, registerTerritories) ? void 0 : "The register wired to this deployment does not reach this territory — it is disclosed in the report as deferred coverage rather than searched.",
18815
- children: [
18816
- t,
18817
- reachesTerritory(t, registerTerritories) ? "" : " · register deferred",
18818
- (0, import_jsx_runtime.jsx)("button", {
18819
- type: "button",
18820
- "aria-label": `Remove ${t}`,
18821
- className: "chip-x",
18822
- onClick: () => setPick(removeTerritory(draft.pick, t)),
18823
- children: (0, import_jsx_runtime.jsx)(Icon, {
18824
- name: "x",
18825
- size: 13
18826
- })
18914
+ title: reachesTerritory(t, registerTerritories) ? void 0 : notAvailableLine([t], registerLabel),
18915
+ children: [reachesTerritory(t, registerTerritories) ? t : notAvailableLine([t], registerLabel), (0, import_jsx_runtime.jsx)("button", {
18916
+ type: "button",
18917
+ "aria-label": `Remove ${t}`,
18918
+ className: "chip-x",
18919
+ onClick: () => setPick(removeTerritory(draft.pick, t)),
18920
+ children: (0, import_jsx_runtime.jsx)(Icon, {
18921
+ name: "x",
18922
+ size: 13
18827
18923
  })
18828
- ]
18924
+ })]
18829
18925
  }, t))
18830
18926
  }),
18831
18927
  (0, import_jsx_runtime.jsx)("div", {
@@ -18845,33 +18941,18 @@ function NewClearance({ ctx }) {
18845
18941
  className: "typeahead",
18846
18942
  children: territoryMatches(territoryQuery, draft.pick.territories, whereLevel, 8, registerTerritories).map((t) => (0, import_jsx_runtime.jsxs)("button", {
18847
18943
  type: "button",
18944
+ disabled: !reachesTerritory(t, registerTerritories),
18848
18945
  onClick: () => {
18849
18946
  setPick(addTerritory(draft.pick, t, whereLevel, registerTerritories));
18850
18947
  setTerritoryQuery("");
18851
18948
  },
18852
18949
  children: [t, reachesTerritory(t, registerTerritories) ? null : (0, import_jsx_runtime.jsx)("span", {
18853
18950
  className: "typeahead-note",
18854
- children: "register deferred"
18951
+ children: notAvailableLine([], registerLabel)
18855
18952
  })]
18856
18953
  }, t))
18857
18954
  }) : null]
18858
18955
  }),
18859
- Array.isArray(registerTerritories) ? (0, import_jsx_runtime.jsxs)("p", {
18860
- className: "section-hint",
18861
- style: { marginTop: 10 },
18862
- children: [
18863
- "The trademark register wired to this deployment reaches",
18864
- " ",
18865
- vocabularyFor(whereLevel, registerTerritories).length,
18866
- " of the",
18867
- " ",
18868
- offerableFor(whereLevel).length,
18869
- " territories you can name here:",
18870
- " ",
18871
- vocabularyFor(whereLevel, registerTerritories).join(", "),
18872
- ". Anywhere else can still be ordered — it is disclosed in the report as deferred coverage rather than searched at the register."
18873
- ]
18874
- }) : null,
18875
18956
  whereLevel?.geography === "exactly one country" && draft.pick.territories.length === 1 ? (0, import_jsx_runtime.jsxs)("div", {
18876
18957
  className: "callout-accent",
18877
18958
  children: [draft.pick.territories[0], " — naming another country replaces it, because this search reads one at a time."]
@@ -19397,6 +19478,7 @@ function NewClearance({ ctx }) {
19397
19478
  plan ? (0, import_jsx_runtime.jsx)(ReviewDialog, {
19398
19479
  uses: runCount(effort),
19399
19480
  left: searchesLeft(usage),
19481
+ registerLabel,
19400
19482
  plan,
19401
19483
  busy,
19402
19484
  owner: ownerLabel,
@@ -19933,7 +20015,7 @@ function PickRow({ selected, onPick, title, tagline, description, chips, recomme
19933
20015
  }) : null]
19934
20016
  });
19935
20017
  }
19936
- function ReviewDialog({ plan, busy, owner, project, names, goods, nativeOn, onStart, onBack, failure, onReview, uses, left }) {
20018
+ function ReviewDialog({ plan, busy, owner, project, names, goods, nativeOn, onStart, onBack, failure, onReview, uses, left, registerLabel }) {
19937
20019
  (0, import_react.useEffect)(() => {
19938
20020
  const on = (e) => {
19939
20021
  if (e.key === "Escape") onBack();
@@ -20018,13 +20100,7 @@ function ReviewDialog({ plan, busy, owner, project, names, goods, nativeOn, onSt
20018
20100
  }),
20019
20101
  (0, import_jsx_runtime.jsx)(Row$3, {
20020
20102
  label: "Registers to search",
20021
- children: plan.coverage ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [plan.coverage.reached.length ? joinAnd(plan.coverage.reached) : "None", plan.coverage.missing.length ? (0, import_jsx_runtime.jsxs)(Muted, { children: [
20022
- ". ",
20023
- joinAnd(plan.coverage.missing),
20024
- " ",
20025
- plan.coverage.missing.length === 1 ? "is" : "are",
20026
- " not reached by the register wired here, and will be disclosed in the report as deferred coverage rather than searched."
20027
- ] }) : null] }) : places.length ? joinAnd(places) : "Worldwide"
20103
+ children: plan.coverage ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [plan.coverage.reached.length ? joinAnd(plan.coverage.reached) : "None", plan.coverage.missing.length ? (0, import_jsx_runtime.jsxs)(Muted, { children: [". ", notAvailableLine(plan.coverage.missing, registerLabel)] }) : null] }) : places.length ? joinAnd(places) : "Worldwide"
20028
20104
  }),
20029
20105
  scope && scope.classes.length ? (0, import_jsx_runtime.jsxs)(Row$3, {
20030
20106
  label: "Classes",
@@ -25476,7 +25552,7 @@ function About() {
25476
25552
  (0, import_jsx_runtime.jsx)("dt", { children: "Copyright" }),
25477
25553
  (0, import_jsx_runtime.jsx)("dd", { children: info.copyright.replace(/^Copyright\s+/i, "") }),
25478
25554
  (0, import_jsx_runtime.jsx)("dt", { children: "Model access" }),
25479
- (0, import_jsx_runtime.jsx)("dd", { children: "This runs on your own model access — a subscription or your own API key — and the paid registers on your own agreements with those providers. The reasoning stages are a proprietary third-party CLI that you install and license under that vendor’s own terms; this licence grants nothing over any of them." }),
25555
+ (0, import_jsx_runtime.jsx)("dd", { children: "This runs on your own model access — a subscription, an API key, or your own cloud account — and the paid registers on your own agreements with those providers. The reasoning stages are a third-party command-line program, Claude Code (proprietary) or the Codex CLI (Apache-2.0), that you install and license under that vendor’s own terms; this licence grants nothing over any of them." }),
25480
25556
  (0, import_jsx_runtime.jsx)("dt", { children: "Trade marks" }),
25481
25557
  (0, import_jsx_runtime.jsxs)("dd", { children: [
25482
25558
  (0, import_jsx_runtime.jsx)("strong", { children: info.name }),
@@ -49,7 +49,7 @@
49
49
  -->
50
50
  <link rel="preconnect" href="https://api.fontshare.com" crossorigin />
51
51
  <link href="https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700,900&display=swap" rel="stylesheet" />
52
- <script type="module" crossorigin src="/portal/assets/index-5UyqAyNM.js"></script>
52
+ <script type="module" crossorigin src="/portal/assets/index-6jzO9HiX.js"></script>
53
53
  <link rel="stylesheet" crossorigin href="/portal/assets/index-CVOIvdhc.css">
54
54
  </head>
55
55
  <body>
@@ -2,7 +2,7 @@
2
2
  "name": "portal-ui",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "0.3.2-beta.7",
5
+ "version": "0.3.2-beta.8",
6
6
  "license": "AGPL-3.0-only",
7
7
  "description": "The unified trademark portal UI. One address, one login: who you are decides what you see. Built as a static bundle, served by driver/portal-service.mjs — the browser never reaches profile-service or recipe-service.",
8
8
  "engines": {
@@ -9,7 +9,8 @@ validated at parse.
9
9
  **One vendor, one billing mode, decided by the run and not by this directory.** The lanes go through
10
10
  `engine.runTurn()` — the same door all fourteen agentic stages use — via
11
11
  [`../../driver/engine/jx-turn.mjs`](../../driver/engine/jx-turn.mjs). Whatever program the customer configured
12
- (`CLEAROTRON_AI`) and whatever billing mode the run is on (API key or subscription) carries these calls too.
12
+ (`CLEAROTRON_AI`) and whatever billing mode the run is on (subscription, API key or, for Claude, a cloud account)
13
+ carries these calls too.
13
14
  That is the owner's standing rule — *one LLM provider only ever, API or auth, no mix* — and until 2026-08-20
14
15
  these three lanes were the one place in the product that broke it: they POSTed to the Anthropic Messages API on
15
16
  `ANTHROPIC_API_KEY` at a hardcoded cheap tier no matter what the rest of the run was doing.
@@ -106,15 +106,20 @@ export function envelopeFromTurnText(text, toolName) {
106
106
  * so the ORDER of those checks lives here once rather than three times.
107
107
  *
108
108
  * `turn` is supplied by the driver and is injectable for tests:
109
- * async ({prompt, kind}) => { ok, text, truncated, usage:{input,output}, model, vendor, authMode, cause }
109
+ * async ({prompt, kind}) => { ok, text, truncated, usage:{input,output}, model, vendor, authMode, cloud, cause }
110
110
  *
111
111
  * ATTRIBUTION RIDES EVERY RETURN, including the failures. A degrade still spent tokens, and without the
112
112
  * model, vendor and billing mode beside them they cannot be attributed in the run's rollup — which is
113
113
  * the half of that says a run must be able to state who did the work.
114
+ *
115
+ * `cloud` is part of that attribution: under the cloud billing mode it names the account that paid
116
+ * ("foundry", "vertex", "bedrock", "gateway"), and it is null under every other mode. The ledger stamp
117
+ * reads it from THIS return, so a field left off here reached every native-language record as
118
+ * `cloud: null` while the main steps of the same run said which cloud paid.
114
119
  */
115
120
  export async function runJxTurn({ body, turn, kind, started, truncatedCause, parse }) {
116
121
  const t0 = Number.isFinite(started) ? started : Date.now();
117
- const blank = { tookMs: 0, model: null, vendor: null, authMode: null, usage: null };
122
+ const blank = { tookMs: 0, model: null, vendor: null, authMode: null, cloud: null, usage: null };
118
123
  if (typeof turn !== "function") return { ok: false, cause: `${kind}: no turn runner was supplied`, ...blank };
119
124
 
120
125
  const { prompt, toolName } = promptFromRequest(body);
@@ -124,7 +129,7 @@ export async function runJxTurn({ body, turn, kind, started, truncatedCause, par
124
129
 
125
130
  const attribution = {
126
131
  tookMs: Date.now() - t0,
127
- model: r?.model ?? null, vendor: r?.vendor ?? null, authMode: r?.authMode ?? null,
132
+ model: r?.model ?? null, vendor: r?.vendor ?? null, authMode: r?.authMode ?? null, cloud: r?.cloud ?? null,
128
133
  // Passed through WHOLE, and `null` stays null. The driver hands over the engine contract's canonical
129
134
  // Usage ({input, output, cacheRead, cacheWrite, total}); re-shaping it to two fields here would drop
130
135
  // cache and total tokens from the rollup, and a zeroed object in place of null would report a
@@ -1,5 +1,9 @@
1
1
  # trademark-oauth-mcp-bridge
2
2
 
3
+ ## 0.3.2-beta.8
4
+
5
+ No changes in this release.
6
+
3
7
  ## 0.3.2-beta.7
4
8
 
5
9
  No changes in this release.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trademark-oauth-mcp-bridge",
3
- "version": "0.3.2-beta.7",
3
+ "version": "0.3.2-beta.8",
4
4
  "license": "AGPL-3.0-only",
5
5
  "private": true,
6
6
  "description": "OAuth 2.1 MCP stdio bridge used by the engine's case-law gather stage (courtlistener / legaldatahunter).",
@@ -155,7 +155,7 @@ what an index actually holds, including whether the 1884 backfile is in it (a da
155
155
  identical on every other number and is missing a century).
156
156
 
157
157
  This replaces only the *register* half of a clearance
158
- — the reasoning engine still needs its own subscription or API key, and the unregistered-use half
158
+ — the reasoning engine still needs its own subscription, API key or cloud account, and the unregistered-use half
159
159
  still wants `PERPLEXITY_API_KEY`.
160
160
 
161
161
  **It refuses rather than answering zero.** An absent index, a schema with no rows, or an index whose
@@ -28,7 +28,7 @@ import { join } from "node:path";
28
28
  import { driverDir, ensureDriverDir } from "../shared/driver-dir.mjs"; // — one definition of where `_driver/` is
29
29
  import { tmpdir } from "node:os";
30
30
  import { buildClaudeArgs, spawnEnv } from "../driver/engine/anthropic-agent.mjs";
31
- import { envFrom } from "../shared/env-aliases.mjs"; // — resolves EITHER spelling; names the retired one because that is the live-writable half
31
+ import { resolveEngineProgram } from "../driver/driver.config.mjs"; // — the program a run would spawn, found the way a run finds it
32
32
 
33
33
  const arg = (f, d) => { const i = process.argv.indexOf(f); return i > 0 ? process.argv[i + 1] : d; };
34
34
  const model = arg("--model", "claude-sonnet-5");
@@ -73,7 +73,9 @@ console.log(`probe root: ${root}`);
73
73
  console.log(`--add-dir roots: ${args.filter((a, i) => args[i - 1] === "--add-dir").join(", ")}`);
74
74
  console.log(`--settings present: ${args.includes("--settings")}`);
75
75
 
76
- const child = spawn(envFrom(process.env, "CLEAROTRON_CLAUDE_PATH") || "claude", [...args, "--include-hook-events"],
76
+ // The program a run would spawn (driver.config.mjs resolveEngineProgram), so this probes the same copy.
77
+ const program = resolveEngineProgram("anthropic-agent");
78
+ const child = spawn(program.resolved ?? program.bin, [...args, "--include-hook-events"],
77
79
  { stdio: ["pipe", "pipe", "pipe"], cwd: runDir, env: spawnEnv() });
78
80
  let out = "", err = "";
79
81
  child.stdout.on("data", (d) => { out += d; });
@@ -535,13 +535,18 @@ export function auditEnv(root = ROOT) {
535
535
  // roster, while ADR-0003 had already ruled case-law setup an OAuth flow
536
536
  // and not a variable at all. Evidence of a reader is not evidence of a
537
537
  // READ: the roster is a list of names to look for, not a call site.
538
- // 4 the AZURE_OPENAI_* block, an external contract (below).
538
+ // 4 the AZURE_OPENAI_* block, then kept as an external contract. That call did
539
+ // not hold: nothing the product runs read them, and the rows were removed
540
+ // from `.env.example` on 2026-09-15 (below). They are no longer counted
541
+ // among the wrong deletions.
539
542
  // 1 CLEAROTRON_SEND_TOOL_PREFIX — genuinely dead, and this direction does not
540
543
  // catch it either: its one surviving mention is a governance-doc line, and
541
544
  // a mention is enough to spare a row. Under-firing is the cost of the
542
545
  // trade, taken deliberately. It is the prose-sweep class.
543
546
  //
544
- // So SIX of ten deletions would have been wrong, two of them credential rows.
547
+ // So FOUR of ten deletions would have been wrong, the four live reads above,
548
+ // one of them a credential row. (This said six until 2026-09-15; that count
549
+ // did not follow from the rows as listed, and it included the Azure block.)
545
550
  // Evidence used instead: the bare NAME, on a name boundary, anywhere in the
546
551
  // tracked tree. The accessor family is filed as and does NOT belong here —
547
552
  // see below.
@@ -561,10 +566,11 @@ export function auditEnv(root = ROOT) {
561
566
  // ── AND IT IS NOT A SUPPRESSION LIST ────────────────────────────────────────────────────────────
562
567
  //
563
568
  // forbids one, rightly: "if a row is deliberately readerless, the row goes, not the guard."
564
- // Applied literally that ruling deletes three rows it should not. `.env.example`'s Azure block
565
- // documents the variables an EXTERNAL agent platform consumes — the block says so itself, ruled
566
- // it, and `MODELS.azure` / `CLEAROTRON_AZURE_MODEL` / `jxPolicy.providerStance: "azure-only"` are live.
567
- // There is no reader in this tree and there never was one to retire.
569
+ // Applied literally, that ruling deletes a row whose consumer is not this tree at all: a variable read
570
+ // by a program the product spawns, such as the Claude program's own sign-in token. The row documents a
571
+ // contract with that consumer, and there is no reader here to retire. (The four AZURE_OPENAI_* rows that
572
+ // first raised this were a different case. They named another platform's settings, nothing the product
573
+ // runs read them, and they were removed from `.env.example` on 2026-09-15.)
568
574
  //
569
575
  // So a row is ACCOUNTED FOR two ways, and this is one rule applied to every row rather than a list of
570
576
  // exempt names: something in the tree names it, OR the row carries an inline `# external:` line