tesseract-agent 0.11.14 → 0.11.15

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.
@@ -11692,8 +11692,8 @@ var capitalizeFirstCharacter, error28;
11692
11692
  var init_lt = __esm({
11693
11693
  "../../node_modules/zod/v4/locales/lt.js"() {
11694
11694
  init_util();
11695
- capitalizeFirstCharacter = (text12) => {
11696
- return text12.charAt(0).toUpperCase() + text12.slice(1);
11695
+ capitalizeFirstCharacter = (text13) => {
11696
+ return text13.charAt(0).toUpperCase() + text13.slice(1);
11697
11697
  };
11698
11698
  error28 = () => {
11699
11699
  const Sizable = {
@@ -22599,6 +22599,14 @@ var init_capabilities = __esm({
22599
22599
  vars: [],
22600
22600
  content: `Whether a personal HeyGen key is set. Never returns the key itself.`
22601
22601
  },
22602
+ {
22603
+ id: "capability.image.has_avatar_free_tier",
22604
+ group: "tools",
22605
+ label: "Free avatar-video tier available?",
22606
+ help: "What the model is told the `image.has_avatar_free_tier` App-Control capability does (image domain, read).",
22607
+ vars: [],
22608
+ content: `Whether the free Tesseract avatar tier can actually serve a request on this deployment \u2014 a signed-in account AND a HeyGen account behind our proxy. Check it before drafting a script: when it is false and the user has no HeyGen key of their own, generate_avatar_video cannot succeed.`
22609
+ },
22602
22610
  {
22603
22611
  id: "capability.image.remove_provider_key",
22604
22612
  group: "tools",
@@ -23039,6 +23047,86 @@ var init_capabilities = __esm({
23039
23047
  vars: [],
23040
23048
  content: `Open the presentation surface in the app so a deck can be picked and presented.`
23041
23049
  },
23050
+ {
23051
+ id: "capability.present.list_decks",
23052
+ group: "tools",
23053
+ label: "List presentations",
23054
+ help: "What the model is told the `present.list_decks` App-Control capability does (present domain, read).",
23055
+ vars: [],
23056
+ content: `Every presentation in the user's library, with its slide count and its deck folder \u2014 the handle every other presentation capability takes. Use it before editing, so you work on the deck the user means rather than making a second one.`
23057
+ },
23058
+ {
23059
+ id: "capability.present.create_deck",
23060
+ group: "tools",
23061
+ label: "Create a presentation",
23062
+ help: "What the model is told the `present.create_deck` App-Control capability does (present domain, write).",
23063
+ vars: [],
23064
+ content: `Make a new, empty Tesseract Stage presentation in the user's library (~/Documents/Tesseract/Presentations) and return its deck folder. This is how you make a presentation \u2014 NOT a PowerPoint file, NOT markdown, NOT an HTML canvas. Pass the title in the user's own words and language. Optionally set a theme: CSS custom properties every slide inherits, e.g. { "--bg": "#FFFFFF", "--ink": "#16130F", "--accent": "#D97757" }. Then write slides with write_slide.`
23065
+ },
23066
+ {
23067
+ id: "capability.present.write_slide",
23068
+ group: "tools",
23069
+ label: "Write a slide",
23070
+ help: "What the model is told the `present.write_slide` App-Control capability does (present domain, write).",
23071
+ vars: [],
23072
+ content: `Write one slide file into a presentation. A slide is a React component in TSX: \`export const meta = { id, title, summary?, notes?, transition?, builds? }\` plus a default-exported component. Path is \`slides/NN-name.tsx\` and FILENAME ORDER IS SLIDE ORDER, so number the prefixes (01, 02, 03\u2026); reordering later is a rename. Shared helpers may live anywhere else in the package and be imported relatively. Always run validate_deck afterwards.`
23073
+ },
23074
+ {
23075
+ id: "capability.present.read_slide",
23076
+ group: "tools",
23077
+ label: "Read a slide",
23078
+ help: "What the model is told the `present.read_slide` App-Control capability does (present domain, read).",
23079
+ vars: [],
23080
+ content: `Read one file back out of a presentation. Use it before editing a slide you did not just write, so a revision preserves what is already there instead of replacing it from memory.`
23081
+ },
23082
+ {
23083
+ id: "capability.present.delete_slide",
23084
+ group: "tools",
23085
+ label: "Delete a slide",
23086
+ help: "What the model is told the `present.delete_slide` App-Control capability does (present domain, write).",
23087
+ vars: [],
23088
+ content: `Remove one file from a presentation. Deleting a slide renumbers nothing \u2014 the remaining filenames still sort, so a gap in the numbering is harmless.`
23089
+ },
23090
+ {
23091
+ id: "capability.present.validate_deck",
23092
+ group: "tools",
23093
+ label: "Check a presentation",
23094
+ help: "What the model is told the `present.validate_deck` App-Control capability does (present domain, read).",
23095
+ vars: [],
23096
+ content: `Compile every slide and report what is wrong \u2014 syntax errors with line numbers, imports that do not resolve, missing or duplicate \`meta\`. ALWAYS call this after writing slides, and fix what it reports before telling the user the presentation is ready: a deck that fails does so in front of an audience. It reports load-time problems only; it cannot catch a mistake that appears only while a slide renders.`
23097
+ },
23098
+ {
23099
+ id: "capability.present.set_deck_theme",
23100
+ group: "tools",
23101
+ label: "Set a presentation's theme",
23102
+ help: "What the model is told the `present.set_deck_theme` App-Control capability does (present domain, write).",
23103
+ vars: [],
23104
+ content: `Replace the CSS custom properties every slide in a presentation inherits \u2014 the deck's look in one place. Restyling a deck is this call, not an edit to every slide.`
23105
+ },
23106
+ {
23107
+ id: "capability.present.add_asset",
23108
+ group: "tools",
23109
+ label: "Add an image to a presentation",
23110
+ help: "What the model is told the `present.add_asset` App-Control capability does (present domain, write).",
23111
+ vars: [],
23112
+ content: `Copy an image, photo or audio file from anywhere on disk INTO the presentation, and get back the path to import from a slide (\`import hero from '../assets/hero.png?url'\`). Do this for anything a slide shows: a presentation must carry its own files, or it breaks the moment it is moved or sent to someone.`
23113
+ },
23114
+ {
23115
+ id: "capability.present.open_deck",
23116
+ group: "tools",
23117
+ label: "Open a presentation",
23118
+ help: "What the model is told the `present.open_deck` App-Control capability does (present domain, write).",
23119
+ vars: [],
23120
+ content: `Open a presentation in the Stage tab so the user can see it. Checks it first and reports the problems instead if it cannot be shown.`
23121
+ },
23122
+ {
23123
+ id: "capability.present.delete_deck",
23124
+ group: "tools",
23125
+ label: "Delete a presentation",
23126
+ help: "What the model is told the `present.delete_deck` App-Control capability does (present domain, write).",
23127
+ vars: [],
23128
+ content: `Delete a whole presentation from the library, permanently. Ask the user first \u2014 this is their document, and there is no undo.`
23129
+ },
23042
23130
  {
23043
23131
  id: "capability.present.end",
23044
23132
  group: "tools",
@@ -23703,6 +23791,14 @@ Apply a plan the user has approved. Requires the plan_hash from tutorial_plan_in
23703
23791
  help: "What the model is told `tutorial_await_decision` does \u2014 blocks until the user approves the tour, asks for changes, or asks to re-pick a target.",
23704
23792
  vars: [],
23705
23793
  content: `Wait for the user to decide on the tour card. Call this straight after tutorial_review_scenario and WAIT \u2014 do not carry on, do not start planning, do not fill the silence. It returns when they approve, ask for changes, or ask for a step to point at something else. Their in-place edits are saved automatically, so whatever wording they left is the wording that ships.`
23794
+ },
23795
+ {
23796
+ id: "tool.capabilities.tesseract_capabilities",
23797
+ group: "tools",
23798
+ label: "tesseract_capabilities",
23799
+ help: "What the model is told the `tesseract_capabilities` tool does \u2014 the one tool that hands it Tesseract's own capability map (docs/CAPABILITIES.md). This description is the only thing deciding whether the model consults the map before answering \u201Cwhat can you do?\u201D or declaring a feature impossible, so it is the highest-leverage entry here. The builder passes the live section index as the `sections` variable.",
23800
+ vars: ["sections"],
23801
+ content: `Return Tesseract's own capability map \u2014 what THE PRODUCT can do, which is far more than the tools you happen to hold this turn (most of Tesseract is UI, services and other clients you never call). Read it BEFORE answering any question about what Tesseract, or you inside it, can do: "what can you do?", "can Tesseract <x>?", "how do I <x> in this app?", "is there a way to", "does it support", "what features", "help me get started" \u2014 and before telling a user something is impossible. It also lists what is deliberately NOT built, which is the most common way to be wrong about this product, so never claim a feature is missing without checking here first. Sections: {{sections}}. Omit \`section\` for the whole map (~22 KB); pass a section number, title or keyword to get just that part when the question is narrow.`
23706
23802
  }
23707
23803
  ];
23708
23804
  }
@@ -23997,6 +24093,17 @@ var init_permissions = __esm({
23997
24093
  }
23998
24094
  });
23999
24095
 
24096
+ // ../../packages/shared/dist/platform-availability.js
24097
+ var HOSTING_AVAILABLE, SSO_AVAILABLE, CLOUD_SERVER_AVAILABLE;
24098
+ var init_platform_availability = __esm({
24099
+ "../../packages/shared/dist/platform-availability.js"() {
24100
+ "use strict";
24101
+ HOSTING_AVAILABLE = false;
24102
+ SSO_AVAILABLE = false;
24103
+ CLOUD_SERVER_AVAILABLE = false;
24104
+ }
24105
+ });
24106
+
24000
24107
  // ../../packages/shared/dist/app-elements.js
24001
24108
  var APP_ELEMENTS, byId, APP_ELEMENT_IDS;
24002
24109
  var init_app_elements = __esm({
@@ -24347,9 +24454,9 @@ function withRunDirective(userText, directive) {
24347
24454
  return `${head}${RUN_DIRECTIVE_MARK}
24348
24455
  ${directive}`;
24349
24456
  }
24350
- function stripRunDirective(text12) {
24351
- const at = text12.indexOf(RUN_DIRECTIVE_MARK);
24352
- return at < 0 ? text12 : text12.slice(0, at).trimEnd();
24457
+ function stripRunDirective(text13) {
24458
+ const at = text13.indexOf(RUN_DIRECTIVE_MARK);
24459
+ return at < 0 ? text13 : text13.slice(0, at).trimEnd();
24353
24460
  }
24354
24461
  var RUN_DIRECTIVE_MARK;
24355
24462
  var init_run_directive = __esm({
@@ -24397,6 +24504,13 @@ var init_digit_recall = __esm({
24397
24504
  }
24398
24505
  });
24399
24506
 
24507
+ // ../../packages/shared/dist/tab-nav.js
24508
+ var init_tab_nav = __esm({
24509
+ "../../packages/shared/dist/tab-nav.js"() {
24510
+ "use strict";
24511
+ }
24512
+ });
24513
+
24400
24514
  // ../../packages/shared/dist/telemetry.js
24401
24515
  var TELEMETRY_EVENTS, EVENT_CATEGORY;
24402
24516
  var init_telemetry = __esm({
@@ -24697,6 +24811,7 @@ var NO_QUOTAS, NO_FEATURES, PLAN_CATALOG;
24697
24811
  var init_plans = __esm({
24698
24812
  "../../packages/shared/dist/plans.js"() {
24699
24813
  "use strict";
24814
+ init_platform_availability();
24700
24815
  NO_QUOTAS = { computeHours: 0, storageGb: 0, egressGb: 0, videoSeconds: 0 };
24701
24816
  NO_FEATURES = {
24702
24817
  sync: false,
@@ -24725,7 +24840,7 @@ var init_plans = __esm({
24725
24840
  tagline: "Your machine, your phone, your voice.",
24726
24841
  features: [
24727
24842
  "Unlimited agent usage",
24728
- "Full IDE: editor, files, agent, browser tool",
24843
+ "It operates this computer \u2014 screen, files and browser",
24729
24844
  "Voice mode",
24730
24845
  "Mobile app \u2014 reach your computer from your phone",
24731
24846
  "Cloud workspace \u2014 your machine and recent projects in one view",
@@ -24744,9 +24859,11 @@ var init_plans = __esm({
24744
24859
  features: [
24745
24860
  "Everything in Free",
24746
24861
  "Unlimited machines \u2014 run agents on all of them",
24747
- "Tesseract cloud server \u2014 a managed always-on agent in the cloud",
24748
- "Deploy to the cloud \u2014 your app on a *.tesseract.dev URL",
24749
- "Includes ~1 always-on service + 20 GB storage + 100 GB bandwidth (overage metered)",
24862
+ ...CLOUD_SERVER_AVAILABLE ? ["Tesseract cloud server \u2014 a managed always-on agent in the cloud"] : [],
24863
+ ...HOSTING_AVAILABLE ? [
24864
+ "Deploy to the cloud \u2014 your app on a *.tesseract.dev URL",
24865
+ "Includes ~1 always-on service + 20 GB storage + 100 GB bandwidth (overage metered)"
24866
+ ] : [],
24750
24867
  "Premium missions \u2014 GDPR, security, UX, performance audits that fix & verify",
24751
24868
  "Sync settings across your devices",
24752
24869
  "Email support"
@@ -24756,10 +24873,10 @@ var init_plans = __esm({
24756
24873
  sync: true,
24757
24874
  mobile: true,
24758
24875
  cloudWorkspace: true,
24759
- cloudServer: true,
24876
+ cloudServer: CLOUD_SERVER_AVAILABLE,
24760
24877
  remoteAgents: true,
24761
24878
  voice: true,
24762
- hosting: true,
24879
+ hosting: HOSTING_AVAILABLE,
24763
24880
  plugins: true,
24764
24881
  missions: true,
24765
24882
  scheduling: true,
@@ -24780,26 +24897,27 @@ var init_plans = __esm({
24780
24897
  "Parallel agents",
24781
24898
  "Advanced browser automation",
24782
24899
  "API access",
24783
- "SSO, audit logs, centralized billing",
24784
- "Pooled cloud hosting \u2014 bigger shared quota",
24900
+ ...SSO_AVAILABLE ? ["Company sign-on and audit log"] : [],
24901
+ "Centralized billing across seats",
24902
+ ...HOSTING_AVAILABLE ? ["Pooled cloud hosting \u2014 bigger shared quota"] : [],
24785
24903
  "Priority support"
24786
24904
  ],
24787
24905
  featurePolicy: {
24788
24906
  sync: true,
24789
24907
  mobile: true,
24790
24908
  cloudWorkspace: true,
24791
- cloudServer: true,
24909
+ cloudServer: CLOUD_SERVER_AVAILABLE,
24792
24910
  remoteAgents: true,
24793
24911
  voice: true,
24794
24912
  team: true,
24795
24913
  parallelAgents: true,
24796
24914
  advancedBrowser: true,
24797
- hosting: true,
24915
+ hosting: HOSTING_AVAILABLE,
24798
24916
  plugins: true,
24799
24917
  missions: true,
24800
24918
  scheduling: true,
24801
24919
  api: true,
24802
- sso: true,
24920
+ sso: SSO_AVAILABLE,
24803
24921
  seats: 2,
24804
24922
  maxMachines: 0
24805
24923
  },
@@ -24856,12 +24974,15 @@ function isAnthropicModelId(model) {
24856
24974
  const id = (model ?? "").trim().toLowerCase();
24857
24975
  if (!id)
24858
24976
  return false;
24977
+ if (TIER_ALIAS.test(id))
24978
+ return false;
24859
24979
  return /(^|[/.\-])(claude|opus|sonnet|haiku|fable)([/.\-]|$)/.test(id) || /^(opus|sonnet|haiku|fable)\b/.test(id);
24860
24980
  }
24861
- var ONE_MILLION_FAMILIES, SUFFIX;
24981
+ var TIER_ALIAS, ONE_MILLION_FAMILIES, SUFFIX;
24862
24982
  var init_model_long_context = __esm({
24863
24983
  "../../packages/shared/dist/model-long-context.js"() {
24864
24984
  "use strict";
24985
+ TIER_ALIAS = /^tsr-/i;
24865
24986
  ONE_MILLION_FAMILIES = [
24866
24987
  /(^|[/.])claude-sonnet-(4-6|4-7|4-8|5)\b/i,
24867
24988
  /(^|[/.])claude-opus-(4-6|4-7|4-8|5)\b/i,
@@ -25520,8 +25641,26 @@ var init_engines = __esm({
25520
25641
  capabilities: CLAUDE_CAPABILITIES,
25521
25642
  runModeTiming: CLAUDE_RUN_MODE_TIMING,
25522
25643
  homepageUrl: "https://docs.claude.com/en/docs/claude-code",
25523
- installCommand: "npm install -g @anthropic-ai/claude-code",
25524
- installHint: "Tesseract can run Claude Code without the standalone CLI; install it to use a copy you manage yourself."
25644
+ // The NATIVE installer, not npm. Two reasons, in order of how often they bite:
25645
+ // 1. npm needs a Node toolchain the machine may simply not have — and a GUI-launched app
25646
+ // sees PATH=/usr/bin:/bin:/usr/sbin:/sbin, so even an nvm-managed node is invisible.
25647
+ // The native installer needs only curl/wget and a shell.
25648
+ // 2. npm would land under our own `--prefix` (~/.tesseract/runtimes/bin), which the thing
25649
+ // that SPAWNS claude (agent-core's findUserInstalledClaude) does not search — so the
25650
+ // install would "succeed" and the engine would still read as missing. The native
25651
+ // installer writes ~/.local/bin/claude, which both the probe and the spawner know.
25652
+ // `install.sh` hard-refuses Windows, and `irm | iex` cannot take the version argument, so
25653
+ // Windows gets the documented scriptblock form rather than a quoting variant.
25654
+ installCommand: "curl -fsSL https://claude.ai/install.sh | bash -s {version}",
25655
+ installCommandWin: "& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) {version}",
25656
+ // Its own updater. A copy managed by brew/winget reports "up to date" and does nothing —
25657
+ // which autoFixUnusable surfaces honestly via its post-update re-check rather than
25658
+ // claiming success.
25659
+ updateCommand: "claude update",
25660
+ // The documented uninstall. Both halves: the launcher and the versions directory.
25661
+ removeCommand: "rm -f ~/.local/bin/claude && rm -rf ~/.local/share/claude",
25662
+ removeCommandWin: 'Remove-Item -Path "$env:USERPROFILE\\.local\\bin\\claude.exe" -Force; Remove-Item -Path "$env:USERPROFILE\\.local\\share\\claude" -Recurse -Force',
25663
+ installHint: "Tesseract runs the Claude Code CLI you have installed rather than bundling one; install it to use this engine."
25525
25664
  },
25526
25665
  "openai-codex": {
25527
25666
  id: "openai-codex",
@@ -26071,6 +26210,7 @@ var init_runner_compat = __esm({
26071
26210
  "use strict";
26072
26211
  init_engines();
26073
26212
  init_providers();
26213
+ init_model_catalog();
26074
26214
  GATEWAY_WIRES = {
26075
26215
  "claude-code": { kind: "anthropic-env" },
26076
26216
  "openai-codex": { kind: "codex-home" },
@@ -26337,6 +26477,10 @@ var init_runtimes = __esm({
26337
26477
  label: ENGINE_CATALOG[id].label,
26338
26478
  cliBinary: ENGINE_CATALOG[id].cliBinary,
26339
26479
  installCommand: ENGINE_CATALOG[id].installCommand,
26480
+ installCommandWin: ENGINE_CATALOG[id].installCommandWin,
26481
+ updateCommand: ENGINE_CATALOG[id].updateCommand,
26482
+ removeCommand: ENGINE_CATALOG[id].removeCommand,
26483
+ removeCommandWin: ENGINE_CATALOG[id].removeCommandWin,
26340
26484
  kind: "engine"
26341
26485
  })),
26342
26486
  { id: "ollama", label: "Ollama", cliBinary: "ollama", installCommand: "curl -fsSL https://ollama.com/install.sh | sh", kind: "tool" },
@@ -26482,26 +26626,46 @@ function providerBase(key2, which2) {
26482
26626
  return fromServer;
26483
26627
  return which2 === "anthropic" ? gatewayAnthropicBaseUrl(key2.baseUrl) : which2 === "openai" ? gatewayOpenAiBaseUrl(key2.baseUrl) : gatewayGeminiBaseUrl(key2.baseUrl);
26484
26628
  }
26629
+ function aliasProviders(key2) {
26630
+ const out = /* @__PURE__ */ new Map();
26631
+ for (const slot of Object.keys(TIER_PROVIDER)) {
26632
+ const id = key2.models[slot];
26633
+ if (id)
26634
+ out.set(id, TIER_PROVIDER[slot]);
26635
+ }
26636
+ return out;
26637
+ }
26485
26638
  function tiers(engineId, key2, models) {
26486
- const agentTier = engineId === "claude-code" ? { main: key2.models.claude, fast: key2.models.claudeFast } : gatewayProviderForEngine(engineId) === "openai" ? { main: key2.models.codex ?? key2.models.gpt, fast: void 0 } : { main: void 0, fast: void 0 };
26487
- const served = new Set(Object.values(key2.models).filter((v) => typeof v === "string" && !!v));
26639
+ const provider = gatewayProviderForEngine(engineId);
26640
+ const preference = provider ? TIER_PREFERENCE[provider] : void 0;
26641
+ const byAlias = aliasProviders(key2);
26488
26642
  const asked = (v) => {
26489
26643
  const t = v?.trim();
26490
26644
  if (!t)
26491
26645
  return void 0;
26492
26646
  if (t.includes("/"))
26493
26647
  return t;
26494
- return served.has(t) ? t : void 0;
26648
+ return byAlias.get(t) === provider ? t : void 0;
26495
26649
  };
26496
- const main2 = asked(models.main) || agentTier.main || key2.models.high;
26497
- const fast = asked(models.fast) || agentTier.fast || key2.models.low;
26498
- return { main: main2, fast };
26650
+ const pick3 = (slots) => {
26651
+ for (const slot of slots) {
26652
+ const id = key2.models[slot];
26653
+ if (id)
26654
+ return id;
26655
+ }
26656
+ return void 0;
26657
+ };
26658
+ const main2 = asked(models.main) || preference && pick3(preference.main);
26659
+ const fast = asked(models.fast) || preference && pick3(preference.fast) || main2;
26660
+ return { main: main2 || void 0, fast: fast || void 0 };
26499
26661
  }
26500
26662
  function planGatewayRun(engineId, key2, models, homeBase) {
26501
26663
  const wire = gatewayWireFor(engineId);
26502
26664
  if (!wire)
26503
26665
  return void 0;
26504
26666
  const { main: main2, fast } = tiers(engineId, key2, models);
26667
+ if (!main2 || !fast)
26668
+ return void 0;
26505
26669
  const effort = ENGINE_CATALOG[engineId]?.modelControls.reasoningEfforts?.includes(models.reasoningEffort ?? "") ? models.reasoningEffort : void 0;
26506
26670
  return planEndpointRun(wire.kind, {
26507
26671
  baseUrl: wire.kind === "anthropic-env" ? providerBase(key2, "anthropic") : wire.kind === "gemini-env" ? providerBase(key2, "gemini") : providerBase(key2, "openai"),
@@ -26515,13 +26679,30 @@ function planGatewayRun(engineId, key2, models, homeBase) {
26515
26679
  homeKey: HOME_DIR_NAME[engineId]
26516
26680
  }, homeBase);
26517
26681
  }
26518
- var HOME_DIR_NAME;
26682
+ var TIER_PROVIDER, TIER_PREFERENCE, HOME_DIR_NAME;
26519
26683
  var init_gateway_plan = __esm({
26520
26684
  "../../packages/shared/dist/gateway-plan.js"() {
26521
26685
  "use strict";
26522
26686
  init_runner_compat();
26523
26687
  init_engine_endpoint();
26524
26688
  init_engines();
26689
+ TIER_PROVIDER = {
26690
+ low: "openai",
26691
+ high: "openai",
26692
+ image: "openai",
26693
+ codex: "openai",
26694
+ gpt: "openai",
26695
+ vision: "google-ai",
26696
+ claude: "anthropic",
26697
+ claudeFast: "anthropic"
26698
+ };
26699
+ TIER_PREFERENCE = {
26700
+ // No fast rung of its own on the OpenAI side: Codex is one model plus a reasoning dial,
26701
+ // and `tsr-low` remains the cheap fallback for anything that asks for one.
26702
+ openai: { main: ["codex", "gpt", "high"], fast: ["low", "high"] },
26703
+ anthropic: { main: ["claude"], fast: ["claudeFast", "claude"] },
26704
+ "google-ai": { main: ["vision"], fast: ["vision"] }
26705
+ };
26525
26706
  HOME_DIR_NAME = {
26526
26707
  opencode: "opencode-bundled",
26527
26708
  "openai-codex": "gateway-openai-codex"
@@ -27637,6 +27818,14 @@ var init_stage_types = __esm({
27637
27818
  }
27638
27819
  });
27639
27820
 
27821
+ // ../../packages/shared/dist/deck-package.js
27822
+ var init_deck_package = __esm({
27823
+ "../../packages/shared/dist/deck-package.js"() {
27824
+ "use strict";
27825
+ init_stage_types();
27826
+ }
27827
+ });
27828
+
27640
27829
  // ../../packages/shared/dist/rdesk-client-ipc.js
27641
27830
  var init_rdesk_client_ipc = __esm({
27642
27831
  "../../packages/shared/dist/rdesk-client-ipc.js"() {
@@ -27662,10 +27851,10 @@ var init_user_dialog = __esm({
27662
27851
  });
27663
27852
 
27664
27853
  // ../../packages/shared/dist/session-ops.js
27665
- function cleanTaskNotifications(text12) {
27666
- if (!text12.includes("<task-notification>"))
27667
- return text12;
27668
- return text12.replace(/<task-notification>([\s\S]*?)<\/task-notification>/g, (_all, inner) => {
27854
+ function cleanTaskNotifications(text13) {
27855
+ if (!text13.includes("<task-notification>"))
27856
+ return text13;
27857
+ return text13.replace(/<task-notification>([\s\S]*?)<\/task-notification>/g, (_all, inner) => {
27669
27858
  const g = (name) => {
27670
27859
  const m = new RegExp(`<${name}>([\\s\\S]*?)</${name}>`).exec(inner);
27671
27860
  return m ? m[1].trim() : "";
@@ -27676,8 +27865,8 @@ function cleanTaskNotifications(text12) {
27676
27865
  }).trim();
27677
27866
  }
27678
27867
  function transcriptToText(msgs, cap = MERGE_PER_SOURCE_CAP) {
27679
- const text12 = msgs.map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${cleanTaskNotifications(m.content)}`).join("\n").slice(0, cap);
27680
- return text12.trim() ? text12 : "";
27868
+ const text13 = msgs.map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${cleanTaskNotifications(m.content)}`).join("\n").slice(0, cap);
27869
+ return text13.trim() ? text13 : "";
27681
27870
  }
27682
27871
  function buildMergeSeed(transcripts) {
27683
27872
  return [
@@ -27689,10 +27878,10 @@ function buildMergeSeed(transcripts) {
27689
27878
  ${t}`)
27690
27879
  ].join("\n\n");
27691
27880
  }
27692
- function parseSummaryReply(text12) {
27693
- const m = /^\s*TITLE:\s*(.+?)\s*\n-{3,}\s*\n([\s\S]+)$/.exec(text12);
27881
+ function parseSummaryReply(text13) {
27882
+ const m = /^\s*TITLE:\s*(.+?)\s*\n-{3,}\s*\n([\s\S]+)$/.exec(text13);
27694
27883
  const title = m?.[1]?.trim();
27695
- const summary = (m?.[2] ?? text12).trim();
27884
+ const summary = (m?.[2] ?? text13).trim();
27696
27885
  return title ? { title, summary } : { summary };
27697
27886
  }
27698
27887
  var MERGE_PER_SOURCE_CAP;
@@ -27797,6 +27986,7 @@ var init_dist2 = __esm({
27797
27986
  "use strict";
27798
27987
  init_surface_merge();
27799
27988
  init_permissions();
27989
+ init_platform_availability();
27800
27990
  init_app_elements();
27801
27991
  init_ws_protocol();
27802
27992
  init_context_overflow();
@@ -27807,6 +27997,7 @@ var init_dist2 = __esm({
27807
27997
  init_run_directive();
27808
27998
  init_proactive_compaction();
27809
27999
  init_digit_recall();
28000
+ init_tab_nav();
27810
28001
  init_telemetry();
27811
28002
  init_error_report();
27812
28003
  init_eula();
@@ -27849,6 +28040,7 @@ var init_dist2 = __esm({
27849
28040
  init_chat_slash();
27850
28041
  init_present_protocol();
27851
28042
  init_stage_types();
28043
+ init_deck_package();
27852
28044
  init_rdesk_client_ipc();
27853
28045
  init_user_dialog();
27854
28046
  init_session_ops();
@@ -30539,8 +30731,8 @@ function makeApi(deps, toolId, tier) {
30539
30731
  await h.close();
30540
30732
  return r.cancelled ? null : r.value ?? [];
30541
30733
  },
30542
- notify: async (text12, opts) => {
30543
- await api.open({ kind: "markdown", initial: { markdown: text12, level: opts?.level } });
30734
+ notify: async (text13, opts) => {
30735
+ await api.open({ kind: "markdown", initial: { markdown: text13, level: opts?.level } });
30544
30736
  }
30545
30737
  };
30546
30738
  return api;
@@ -30551,11 +30743,11 @@ function buildToolCtx(deps, sessionId, toolId, log10 = NOOP_LOG, tier = "third-p
30551
30743
  return getSid();
30552
30744
  }, toolId, log: log10 };
30553
30745
  }
30554
- function wrapCtxTool(tool21, ctx2) {
30555
- const raw = tool21.__ctxHandler;
30746
+ function wrapCtxTool(tool22, ctx2) {
30747
+ const raw = tool22.__ctxHandler;
30556
30748
  if (typeof raw !== "function")
30557
- return tool21;
30558
- return { ...tool21, handler: async (args) => raw(args, ctx2) };
30749
+ return tool22;
30750
+ return { ...tool22, handler: async (args) => raw(args, ctx2) };
30559
30751
  }
30560
30752
  var NOOP_LOG;
30561
30753
  var init_surface_ctx = __esm({
@@ -30713,11 +30905,11 @@ var init_hook_bus = __esm({
30713
30905
  // ../../packages/agent-core/dist/jobs/file-tailer.js
30714
30906
  import { statSync } from "node:fs";
30715
30907
  import { open } from "node:fs/promises";
30716
- function parseBackgroundTaskResult(text12) {
30717
- if (!text12 || !text12.includes(".output"))
30908
+ function parseBackgroundTaskResult(text13) {
30909
+ if (!text13 || !text13.includes(".output"))
30718
30910
  return null;
30719
- const id = /\bID:\s*([A-Za-z0-9_-]+)/.exec(text12);
30720
- const path6 = /Output is being written to:\s*([^\s]+\.output)/.exec(text12);
30911
+ const id = /\bID:\s*([A-Za-z0-9_-]+)/.exec(text13);
30912
+ const path6 = /Output is being written to:\s*([^\s]+\.output)/.exec(text13);
30721
30913
  if (!id?.[1] || !path6?.[1])
30722
30914
  return null;
30723
30915
  return { taskId: id[1], outputPath: path6[1] };
@@ -31737,6 +31929,8 @@ var init_parallel_tools = __esm({
31737
31929
  "browser_events",
31738
31930
  "capture_element_styles",
31739
31931
  // ── Listings, capability probes and status reads.
31932
+ // tesseract_capabilities returns an inlined constant — concurrent calls are indistinguishable.
31933
+ "tesseract_capabilities",
31740
31934
  "list_effects",
31741
31935
  "list_upscale_models",
31742
31936
  "list_voices",
@@ -31789,8 +31983,8 @@ var init_tool_spec = __esm({
31789
31983
  import { tool as tool14 } from "@anthropic-ai/claude-agent-sdk";
31790
31984
  function buildScreenControlSdkTools(p) {
31791
31985
  return screenControlSpecs().map((spec) => tool14(spec.name, spec.description, zodShapeFromParams(spec.params), async (args) => {
31792
- const text12 = await p.exec(spec.name, args ?? {});
31793
- return { content: [{ type: "text", text: text12 || "Done." }] };
31986
+ const text13 = await p.exec(spec.name, args ?? {});
31987
+ return { content: [{ type: "text", text: text13 || "Done." }] };
31794
31988
  }));
31795
31989
  }
31796
31990
  var ANNOTATE_STYLES, SCREEN_CONTROL_SHAPES, screenControlSpecs;
@@ -32596,6 +32790,10 @@ ${JSON.stringify(r, null, 2)}`
32596
32790
  { id: "settings.has_search_key", domain: "settings", title: "Web search available?", input: external_exports.object({}), mutation: "read", availability: "host", binding: { kind: "ipc", channel: "serp", verb: "hasKey" } },
32597
32791
  { id: "image.set_avatar_key", domain: "image", title: "Set avatar-video (HeyGen) key", input: external_exports.object({ api_key: external_exports.string().describe("HeyGen API key (secret); blank clears.") }), mutation: "write", availability: "host", binding: { kind: "ipc", channel: "heygen", verb: "setKey" } },
32598
32792
  { id: "image.has_avatar_key", domain: "image", title: "Avatar-video key configured?", input: external_exports.object({}), mutation: "read", availability: "host", binding: { kind: "ipc", channel: "heygen", verb: "hasKey" } },
32793
+ // Whether the FREE brokered tier can serve an avatar video HERE — the user's own key is
32794
+ // the other half, above. Worth asking before drafting a script: the deployment may hold
32795
+ // no HeyGen account, and generate_avatar_video then fails at submit with the work done.
32796
+ { id: "image.has_avatar_free_tier", domain: "image", title: "Free avatar-video tier available?", input: external_exports.object({}), mutation: "read", availability: "host", binding: { kind: "ipc", channel: "heygen", verb: "brokered" } },
32599
32797
  { id: "image.remove_provider_key", domain: "image", title: "Remove image provider key", input: external_exports.object({ provider_id: IMAGE_PROVIDER }), mutation: "write", availability: "host", binding: { kind: "ipc", channel: "imageEdit", verb: "removeKey" } },
32600
32798
  { id: "image.set_default_provider", domain: "image", title: "Set default image provider", input: external_exports.object({ provider_id: IMAGE_PROVIDER }), mutation: "write", availability: "host", binding: { kind: "ipc", channel: "imageEdit", verb: "setDefault" } },
32601
32799
  { id: "image.set_provider_model", domain: "image", title: "Set image provider model", input: external_exports.object({ provider_id: IMAGE_PROVIDER, model: external_exports.string() }), mutation: "write", availability: "host", binding: { kind: "ipc", channel: "imageEdit", verb: "setModel" } },
@@ -32760,6 +32958,134 @@ ${JSON.stringify(r, null, 2)}`
32760
32958
  availability: "desktop",
32761
32959
  binding: { kind: "command", command: "app.openStage" }
32762
32960
  },
32961
+ // ── The presentation LIBRARY: authoring, not driving ──────────────────────────────────
32962
+ // Everything above drives a deck that is already running, and lives in the daemon because
32963
+ // a phone remote reaches it. These WRITE FILES, so they are 'desktop': the library is a
32964
+ // folder in the user's Documents, and only the desktop process can touch it.
32965
+ //
32966
+ // A deck is TSX — the same slide contract the app's own decks use — so the model authors
32967
+ // by writing files and then calling `validate_deck` until it comes back clean. That loop
32968
+ // is the whole design: without it the first sign of a stray bracket on slide 14 is a
32969
+ // blank rectangle at the front of a room.
32970
+ {
32971
+ id: "present.list_decks",
32972
+ domain: "present",
32973
+ title: "List presentations",
32974
+ input: external_exports.object({}),
32975
+ mutation: "read",
32976
+ availability: "desktop",
32977
+ binding: { kind: "ipc", channel: "decklib", verb: "list" }
32978
+ },
32979
+ {
32980
+ id: "present.create_deck",
32981
+ domain: "present",
32982
+ title: "Create a presentation",
32983
+ input: external_exports.object({
32984
+ title: external_exports.string().describe("What the presentation is called. Shown in the library and used for its folder name; keep the user's own wording and language."),
32985
+ description: external_exports.string().optional().describe("One line about what it is for."),
32986
+ theme: external_exports.record(external_exports.string(), external_exports.string()).optional().describe('CSS custom properties applied to every slide, e.g. { "--bg": "#FFFFFF", "--ink": "#16130F", "--accent": "#D97757" }.')
32987
+ }),
32988
+ mutation: "write",
32989
+ availability: "desktop",
32990
+ binding: { kind: "ipc", channel: "decklib", verb: "create" },
32991
+ summarize: (r) => `Created the presentation "${r?.title ?? ""}".`
32992
+ },
32993
+ {
32994
+ id: "present.write_slide",
32995
+ domain: "present",
32996
+ title: "Write a slide",
32997
+ input: external_exports.object({
32998
+ deck: external_exports.string().describe("The deck folder, exactly as `list_decks` or `create_deck` returned it (ends in .tessdeck)."),
32999
+ path: external_exports.string().describe('Path inside the presentation, e.g. "slides/03-pricing.tsx". Filename order IS slide order, so number the prefixes. Helper files may live anywhere in the package.'),
33000
+ source: external_exports.string().describe("The whole file. A slide exports `meta` ({ id, title, summary?, notes?, transition?, builds? }) and a default React component.")
33001
+ }),
33002
+ mutation: "write",
33003
+ availability: "desktop",
33004
+ binding: { kind: "ipc", channel: "decklib", verb: "writeFile" },
33005
+ summarize: (_r, i) => `Wrote ${i.path ?? "a slide"}.`
33006
+ },
33007
+ {
33008
+ id: "present.read_slide",
33009
+ domain: "present",
33010
+ title: "Read a slide",
33011
+ input: external_exports.object({
33012
+ deck: external_exports.string().describe("The deck folder."),
33013
+ path: external_exports.string().describe('Path inside the presentation, e.g. "slides/03-pricing.tsx".')
33014
+ }),
33015
+ mutation: "read",
33016
+ availability: "desktop",
33017
+ binding: { kind: "ipc", channel: "decklib", verb: "readFile" }
33018
+ },
33019
+ {
33020
+ id: "present.delete_slide",
33021
+ domain: "present",
33022
+ title: "Delete a slide",
33023
+ input: external_exports.object({
33024
+ deck: external_exports.string().describe("The deck folder."),
33025
+ path: external_exports.string().describe("Path inside the presentation.")
33026
+ }),
33027
+ mutation: "write",
33028
+ availability: "desktop",
33029
+ binding: { kind: "ipc", channel: "decklib", verb: "deleteFile" },
33030
+ summarize: (_r, i) => `Deleted ${i.path ?? "a slide"}.`
33031
+ },
33032
+ {
33033
+ id: "present.validate_deck",
33034
+ domain: "present",
33035
+ title: "Check a presentation",
33036
+ input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33037
+ mutation: "read",
33038
+ availability: "desktop",
33039
+ binding: { kind: "ipc", channel: "decklib", verb: "validate" },
33040
+ summarize: (r) => typeof r === "string" ? r : "Checked the presentation."
33041
+ },
33042
+ {
33043
+ id: "present.set_deck_theme",
33044
+ domain: "present",
33045
+ title: "Set a presentation\u2019s theme",
33046
+ input: external_exports.object({
33047
+ deck: external_exports.string().describe("The deck folder."),
33048
+ theme: external_exports.record(external_exports.string(), external_exports.string()).describe(`CSS custom properties, e.g. { "--bg": "#0C0A14", "--ink": "#FFFFFF" }. Replaces the deck's current theme.`)
33049
+ }),
33050
+ mutation: "write",
33051
+ availability: "desktop",
33052
+ binding: { kind: "ipc", channel: "decklib", verb: "setTheme" },
33053
+ summarize: () => "Updated the presentation\u2019s theme."
33054
+ },
33055
+ {
33056
+ id: "present.add_asset",
33057
+ domain: "present",
33058
+ title: "Add an image to a presentation",
33059
+ input: external_exports.object({
33060
+ deck: external_exports.string().describe("The deck folder."),
33061
+ source_path: external_exports.string().describe("Absolute path of the file to copy in \u2014 a generated image, a photo, an audio clip."),
33062
+ name: external_exports.string().optional().describe("Filename to use inside the presentation.")
33063
+ }),
33064
+ mutation: "write",
33065
+ availability: "desktop",
33066
+ binding: { kind: "ipc", channel: "decklib", verb: "addAsset" },
33067
+ summarize: (r) => `Added ${typeof r === "string" ? r : "an asset"} to the presentation.`
33068
+ },
33069
+ {
33070
+ id: "present.open_deck",
33071
+ domain: "present",
33072
+ title: "Open a presentation",
33073
+ input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33074
+ mutation: "write",
33075
+ availability: "desktop",
33076
+ binding: { kind: "ipc", channel: "decklib", verb: "open" },
33077
+ summarize: () => "Opened the presentation in Stage."
33078
+ },
33079
+ {
33080
+ id: "present.delete_deck",
33081
+ domain: "present",
33082
+ title: "Delete a presentation",
33083
+ input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33084
+ mutation: "write",
33085
+ availability: "desktop",
33086
+ binding: { kind: "ipc", channel: "decklib", verb: "delete" },
33087
+ summarize: (_r, i) => `Deleted the presentation ${i.deck ?? ""}.`
33088
+ },
32763
33089
  {
32764
33090
  id: "present.end",
32765
33091
  domain: "present",
@@ -32954,6 +33280,75 @@ var init_app_control_tools = __esm({
32954
33280
  }
32955
33281
  });
32956
33282
 
33283
+ // ../../packages/agent-core/dist/capabilities-doc.generated.js
33284
+ var CAPABILITIES_DOC;
33285
+ var init_capabilities_doc_generated = __esm({
33286
+ "../../packages/agent-core/dist/capabilities-doc.generated.js"() {
33287
+ "use strict";
33288
+ CAPABILITIES_DOC = '# Tesseract \u2014 what the product can do\n\nOne-page map of **every user-reachable capability**, derived from the 80 area briefs in\n[`functional-testing/`](functional-testing/). It is the source for the `tesseract_capabilities`\ntool, so the agent can answer "what can you do?" about *Tesseract*, not just about its own toolbelt.\n\n> Read this before telling a user something is or isn\'t possible. \xA718 lists what is deliberately\n> **not** built \u2014 promising any of it is the most common way to be wrong about this product.\n\n---\n\n## 1. What Tesseract is\n\nA desktop-first AI workspace: an always-on agent that can drive your machine, your browser and your\nfiles; plus a fleet layer that makes every machine you own reachable from any of them; plus a set of\ncreative, communication and security products built on the same transport. It ships as **eight\nseparate clients** and is not a single app.\n\n## 2. Clients (each a distinct product)\n\n| Client | What it is |\n|---|---|\n| **Desktop** (Electron, mac/Win/Linux) | The full product. Tab workspace \u2014 **no router**: a recursive split-pane tree of tab groups, **24 tab kinds**, **10 dockable panels** (dock left/right/bottom, float, detach), multi-window, native menu, command + search palettes (**31 stable command ids**), \u23181\u20130 tab assign/recall, per-machine keybinding overrides, memory governor + layout restore. |\n| **Web** (`dist-web`) | Same React renderer over a relay transport. **No local agent, no local engine** \u2014 it always runs on a remote host. Terminals are all remote and pinned to the opening host. Documented stubs: undo-delete, engines, runner configs, window resize; voice keys sit in `localStorage`. Always auth-gated. |\n| **Mobile** (Flutter, iOS/Android/macOS) | A **roaming client** \u2014 it never hosts an agent. Chat with streaming, team chat, present-follower, full password vault (incl. TOTP + native autofill), realtime voice, rdesk viewer, clipboard sync, push. Uniquely: **phone-as-host** (serve your photo library and files to your desktop) and a create-video screen. |\n| **CLI** `tesseract-agent` (npm) | Headless daemon, ~18 commands: `login/logout/whoami`, `run`, service `install/start/stop/restart/status/logs`, `link add`, `runner \u2026`, `net \u2026`, self-update. `--instance <id>` namespaces state dir, service name and device id. Docker-friendly (`login --token` then `run` as PID 1). |\n| **Browser extension** (Chrome MV3) | Claude sidebar (sessions, slash commands, todos, sources, DOM tree, run modes), element picker, vault/autofill popup with biometrics, QR pairing, fullpage + plan views. Talks to the daemon through a native-messaging companion host. |\n| **Remote Desktop** (`apps/rdesk`) | Slim standalone binary, ~\xBC the size \u2014 ships **no AI/browser payload**. Server/client/chooser roles, coexists with the full daemon (demotes itself to client rather than quitting). |\n| **Mesh** (`apps/mesh`) | Slim standalone binary for the private network only \u2014 "moves packets, not pixels". Create a mesh, enrol, list peers. No role chooser by design. |\n| **Thin installer** (Flutter, ~20\u201340 MB) | The default download. Resolves the release feed at run time, picks the arch-correct asset, streams + sha512-verifies, installs, launches. Version-agnostic. |\n\nPlus the **admin/operations console** (`admin.tesseract.dev`: gateway credentials, users and keys,\nerror inspector, remediation trigger, audit log) and the **landing site** with its waitlist.\n\n## 3. The agent\n\n- **Turn lifecycle**: stream, interrupt, rapid-follow-up auto-cancel (first seconds only), stall\n watchdog, orphan-turn recovery, token compaction, usage-limit handling, cost ledger per session.\n- **Run modes**: **5 permission modes \xD7 6 effort levels**, thinking budget, per-turn engine/model pick.\n Default permission mode is `bypassPermissions`.\n- **Interactive cards** (~9): permission request, plan review, elicitation, confirm, verification,\n user dialog, skill UI, archive password, unattended report. "Deny & stop" also aborts the turn.\n- **Block renderers** (~9): mission, subagent, report, sources, provenance, event, notice, surface,\n image grid \u2014 plus tool-run cards joined at render time with 6/8-line clamping.\n- **Composer** (~25 actions): attach, slash commands, @-mention, reverse search, screen-region\n capture, element picker, voice, undo-last-turn, queued-message reorder.\n- **Sessions**: list, search (one host-side search across every engine\'s stored conversations),\n resume, summarize, **fork**, merge, delete-message, keep-alive, focus; session mirror + transfer;\n share a conversation by unguessable capability token.\n- **Subsystems**: reinforced verification, plan splitter, narrator (live status strip, capped at 24\n model calls/turn), hook bus (9 events), option ratings, unattended mode.\n- **Also**: a playable **chess** surface (5 tools) and chat slash toys (`/shrug`, `/tableflip`, \u2026).\n\n## 4. Engines, runners, gateway\n\n- **4 engines \xD7 3 auth modes** (`oauth` / `apiKey` / `localCli`), a 24-capability engine matrix,\n engine + model swap mid-session (model/permission-mode reconcile live; effort/cwd/MCP/system-prompt\n force a respawn).\n- **Runner config pool** with rotation, cooldown and failover; auto-retry is bounded and only fires\n when the turn did no work.\n- **Provider gateway** \u2014 a credential-injecting reverse proxy, **19 routes** across AGENT (anthropic,\n openai, google-ai, groq, xai, deepseek, moonshot, openrouter, custom-openai, customer-litellm,\n azure-openai), EMBEDDING, SEARCH (serpapi), VIDEO (heygen, gemini), VOICE, MEDIA (klipy, unsplash,\n pexels). Virtual keys `tsk_live_*`, budgets, LiteLLM price map, spend ledger, credential pools with\n health-based failover that never crosses a category. Model aliases: `tsr-low`, `tsr-high`,\n `tsr-vision`, `tsr-claude`, `tsr-claude-fast`, `tsr-image` ($0.10/image flat).\n- **Framework cross-product gate**: the user\'s self-updating `claude` CLI \xD7 the pinned Agent SDK,\n verified nightly across pinned CLI versions.\n\n## 5. Files, editors, viewers\n\n- **Finder-like browser**: list/grid/columns, breadcrumbs, bookmarks + OS favourites, **host\n switcher** (every op can be remote), new/rename (F2)/move/duplicate/cut-copy-paste/delete + undo,\n Get Info, send-to-machine, find-in-files, native drag in **and out** (macOS `NSFilePromiseProvider`\n \u2014 drag a not-yet-downloaded remote file to Finder).\n- **~20 typed viewers/editors**: Monaco source editor, PDF (view + edit), Word/docx, spreadsheets,\n presentations, markdown, data grid, hex, audio, video, font, HTML canvas, diff, info.\n- **Archives as folders**: ~20 formats (zip, 7z, tar, gz/bz2/xz/zst, rar, iso, wim, cab, cpio, lzh\u2026)\n browsed, extracted and **edited in place** from the same tree. `rar`/`iso`/`cab` are read-only.\n Encrypted archives prompt for a password and can remember it.\n- **Image editor**: layered canvas, 10 tools (marquee, lasso, move, crop, paint, eraser, fill,\n eyedropper, text, zoom), 25 rail controls, **200+ AI effect styles**, layers, adjust/resize, markup,\n and 3 AI ops (`prompt`/`inpaint`/`outpaint`), transparent-background output, animate\u2192video.\n- **Video editor**: EDL timeline (cut/trim/reorder/transitions/speed/crop/reframe/flip/reverse/colour\n effects/audio tracks), burned-in captions, **18 MCP tools** \u2014 probe, render, frame extract, silence\n detect, transcribe with diarization, extract/isolate audio, music generation, voice list/clone/TTS,\n avatars, caption export, effects, upscale (ffmpeg + local Real-ESRGAN free; fal-Topaz paid).\n- **Undo & time travel**: agent-driven **file rewind** (undo what a turn wrote), file-tree \u2318Z with a\n per-window trash, workspace snapshots and git checkpoints, canvas/design version history with\n diff-and-restore, and message deletion with uniform semantics across engines.\n- **Native inspectors**: **Flutter** widget inspector (tree, on-device tap-to-pick, properties,\n deterministic Dart source edits, hot reload) and **React Native** (source binding + AST edits).\n\n## 6. Create, Design Studio, Stage, Publish\n\n- **Create** \u2014 5 generative document formats (incl. infographic and poster), ~90 actions: outline\n ops, 10 insert kinds, card generation, AI layouts, export.\n- **Design Studio** \u2014 a 10-stage wizard (pick \u2192 launch/browse \u2192 preview \u2192 target \u2192 inspect \u2192 brief \u2192\n build \u2192 QA \u2192 deploy) over **254 templates across 19 industry verticals**, Figma import, in-studio\n theme/token editing.\n- **Stage** \u2014 presentation engine: decks, slides, transitions, **slide audio** (narration/music/sfx\n with music ducking under narration), a live assistant, presenter mode, zoom/blank. 8 present tools\n let the agent drive the deck; followers converge from absolute-state snapshots.\n- **Export**: PowerPoint (.pptx), deck and card export, PNG.\n- **Publish** \u2014 private/org/public visibility, server-rendered public pages on a 192-bit token, live\n data sources (SSRF-guarded), embeds; **community gallery** with explore/following/team/mine, like,\n follow and clone.\n- **Deploy** \u2014 one-click hosting to `*.tesseract.dev` in per-user hardened containers (cap-drop ALL,\n no-new-privileges, read-only, egress-locked), metered on compute/storage/egress. One of the few\n genuinely entitlement-gated features. A separate **per-user cloud agent** container is provisioned\n on demand so you have a machine in the fleet even with every laptop closed.\n\n## 7. Generative media\n\n| What | Notes |\n|---|---|\n| **Images** | 2 providers (OpenAI gpt-image-2, Google). Generate (4-up by default), edit, inpaint/outpaint, transparent background (inpaint + chroma-key). **$0.10/image**. Ungated, BYO key. |\n| **Stock images** | AI-ranked Unsplash/Pexels search as its own tab. Free tier, rate-limited. |\n| **Video** | Google Veo 3.1 \u2014 4/6/8s clips, 720p/1080p, 16:9 or 9:16, keyframe conditioning (first/last frame), native audio. Free on the bundled tier; otherwise cost-confirmed. Multi-scene storyboards chain clip-to-clip for longer videos. |\n| **Avatar video** | HeyGen talking presenter from a script. |\n| **Music / speech** | ElevenLabs music generation, TTS, and instant **voice cloning**. |\n| **Fonts & brand** | Prompt \u2192 AI glyph sheets \u2192 **vision QC** \u2192 vectorize \u2192 kerning/spacing \u2192 a real installable font. Plus logo/wordmark candidates, SVG lockups and a brand kit. Assembly/vectorize/build are local and free. |\n| **Trending effects** | One-tap video effects (hug/kiss, squish, bring-photo-to-life, age, anime restyle, 3D figurine, outfit change, consent-gated face-swap\u2026). |\n\n## 8. Voice\n\n- **3 realtime providers**: OpenAI Realtime, Gemini Live, ElevenLabs ConvAI \u2014 plus a non-realtime\n **STT\u2192LLM\u2192TTS pipeline cascade**. Audio goes **client\u2194provider direct**; the daemon only mints the\n ephemeral credential and runs the control loop.\n- Group calls, live captions, and the **\u2325Space voice widget**.\n- **Voice as a control modality**: the realtime model is the *operator*, Claude Code is the *worker*.\n It decomposes a spoken request into one small dispatched step at a time, all to the same agent\n session, and is explicitly forbidden from claiming an outcome the agent did not report \u2014 when\n unsure it dispatches a check. Its own screen view is a deliberately low-res ~1 fps grab.\n\n## 9. Computer control\n\n- **Two distinct MCP servers**: `tesseract-computer` (**26 tools** \u2014 raw control: move/click/drag/\n scroll/key/type, see_screen, read_text, read_ui (accessibility), clipboard, windows, apps, open\n url/path, notify, annotate, draw) and `tesseract-screen` (**15 tools** \u2014 a guided\n look \u2192 figure_out_next \u2192 perform_step \u2192 **verify** loop that can fail and retry).\n- Plus `tesseract-os-control`: processes, network, structured fs, AX tree + AX actions, preferred app\n and usage profile, screen overlays (arrow/bubble/highlight/text/popup/choice), and\n `request_user_input` / `confirm_action`.\n- **Click grounding** is AX-tree + a local Moondream vision sidecar (free); screenshots are\n Retina-aware and downscaled to point coordinates so clicks line up.\n- **Off by default** \u2014 `computerControlEnabled` must be turned on.\n- **\u2325Space co-pilot HUD** + **13 daemon-owned windows** (spotlight, pastebar, publisher, annotate,\n draw, overlay, input, choice, consent, control-bar, ai-cursor, rdesk call/client), the tray, screen-\n region capture with multi-display number flash, and `tesseract://` deep links. The HUD is\n deliberately **invisible to the AI\'s own screenshots**.\n\n## 10. Integrated browser\n\n- In-app browser tabs with a shared persist partition, per-tab zoom, OAuth popups, URL-safety and\n proxy routing.\n- **Agent control over CDP**: navigate, snapshot, screenshot (viewport + full page), click (synthetic\n and real trusted input), type, key, eval, cookies, raw CDP for any domain, buffered event draining.\n Drives the **user\'s real logged-in browser** via the companion extension, or the built-in engine.\n- **Inspectors**: DOM tree, properties, native inspector; markup pen; click-to-source (JSX elements\n resolve to their source line).\n- **6 Toolkit mission playbooks**: accessibility, GDPR/cookies, health, performance, SEO, UX review \u2014\n each configures, runs, and emits a structured report.\n- **`appbrowser` collaboration**: presence, cursors, comments and edits on a visible tab.\n\n## 11. Web search\n\n`serp_*` over **121 engines / 1,726 parameters**, ~19 tools grouped by family: web, news, images\n(incl. reverse-image and Google Lens), video (incl. YouTube transcripts), maps/local, shopping,\nmarketplaces (Amazon/eBay/Walmart/Home Depot/Zillow), travel (flights/hotels), places & reviews,\nacademic (Scholar/patents), jobs, finance & trends, app stores, ads transparency, social.\nBrokered free tier: **100 searches/month signed in, 10/month anonymous** \u2014 the only hard per-user\nquota in the product.\n\n## 12. Multi-machine\n\n- **Host registry & fleet**: every machine you sign into registers itself; pick one and run the turn\n there. Dual-role nodes are both client and host at once.\n- **Remote fs + terminals** over the relay (9 relayed channels: agent, signal, browser, frame, fs,\n term, editor, rdesk, present). A remote driver may only launch an enumerated interpreter.\n- **File Provider (macOS)**: remote machines appear as native **Finder locations** \u2014 browse, read,\n write, rename, move, Trash + Put Back, thumbnails, progress. Identity survives rename/move.\n- **Drops** \u2014 AirDrop-like sharing to your own devices or another account. E2E-sealed, blind fan-out\n (first device to accept wins; the sender never learns which), receive policies + receive codes,\n blocks and abuse reports that are deliberately **indistinguishable** from "nobody was there".\n- **Universal clipboard**: text/HTML/images fan out to your other machines; >512 KB syncs a thumbnail\n + reference and pulls the bytes on demand. A privacy gate blocks password-manager and secure-input\n content. Plus **paste history** (\u2318\u21E7V pastebar), clipboard over the rdesk channel, and mobile\n text-only sync.\n- **Peer-link**: agent\u2194agent direct transport with a 3-tier ladder (LAN TLS \u2192 observed public IP \u2192\n UDP hole-punch), cert-pinned, relayed only as a fallback.\n- **Mesh VPN**: own PKI with SAS verification, join tickets, recovery mnemonic, a policy bundle with a\n fail-closed freshness clock, an authenticated Noise data plane, disco/path selection, a 25-pair NAT\n punch matrix, and a privileged `netd` helper that **derives** every route itself (the IPC has no\n "add route" field, by design) with journal-based teardown after a crash or reboot.\n- **Remote desktop**: screen share and control. 3 grant paths (same-account registry, cross-account\n code, unattended password), 3 engines (native, VNC, RDP), display picker, quality, Ctrl+Alt+Del,\n in-session voice on the same peer connection, side chat, and a phone viewer.\n- **Present & collaboration**: a presenter drives followers across devices; join codes; live cursors,\n comments and captions.\n- **Co-view mirroring**: a second device with the same conversation open sees **26 allowlisted\n events**. Permission prompts are deliberately *not* mirrored \u2014 a card you can\'t answer is a dead\n button.\n- **Settings & session sync**: keys, tokens, active engine and model are account-wide; `localCli`\n paths are machine-local and must never cross. Plus continuous folder mirroring (active link).\n\n## 13. Team chat & conferencing\n\nSlack-class: spaces, categories, channels, DMs, threads, mentions, reactions, custom emoji, stickers,\nattachments, forum channels, pins, presence, search (Postgres **or** Elasticsearch), link unfurl,\nGIF/meme, bots + a bot API, incoming webhooks, automations, governance, retention and audit.\n**Huddles** over a self-hosted LiveKit SFU with captions and a summary posted back to the channel;\ntoken minting and webhooks work without the SFU, and captions still work when LiveKit is unconfigured.\n\n## 14. Vault (password manager)\n\nZero-knowledge, Argon2id + AES-GCM, server holds only opaque ciphertext. **5 item types** (logins,\npasskeys, payments, secure notes, personal info), ~28 daemon ops, **4 unlock paths** (master password,\ndevice, biometric, **QR from your phone**), sharing sealed to recipient device keys, transfer,\nimport/export (Chrome, Safari, zip), a generator, TOTP, the **macOS AutoFill provider**, and browser\nautofill through the extension.\n\n## 15. Extensions platform\n\nFamilies: **skills, code plugins, bundle plugins, MCP connectors** (3 transports: stdio/http/sse),\nplus **theme packs**. Marketplace discovery with a security verdict that gates install; enable/disable;\nper-extension encrypted secrets; updates; uninstall. Extensions contribute **tabs, panels, settings\npages, status items, menu items and reskins**. First-party skills are free; marketplace ones are\ngated \u2014 `plugins` is the one entitlement actually enforced.\n\n## 16. App Control \u2014 the AI operating Tesseract itself\n\n**144 capabilities across 19 domains** (`settings, navigation, engines, runners, runtimes,\nextensions, hosts, account, daemon, providers, image, voice, permissions, project, privacy, system,\nfonts, present, tour`), exposed as 2 meta-tools + one executor per domain, each input validated\nagainst its own schema. **There is no confirmation gate \u2014 actions happen immediately, by product\ndecision.**\n\n## 17. Platform\n\n- **Accounts**: invite-only (there is **no public sign-up**; Google sign-in on an unknown email is\n rejected), password + Google, password reset, device registration/claim/keys, a CLI device-code\n flow, and an offline-tolerance contract \u2014 only an explicit 401/403 signs you out.\n- **Plans**: `free`, `standard` (shown as **"Pro"**), `pro` (shown as **"Team"**) \u2014 17 feature flags,\n quotas on compute/storage/egress/video, seats, invites, orgs. Your effective plan is the best across\n every org you belong to.\n- **Missions, cron & unattended**: schedule agent turns that run with nobody present, with catch-up\n after downtime, background tasks and structured reports.\n- **Settings**: **23 sections, ~300 controls** \u2014 appearance, editor, general, runners, agent, voice,\n image editing, search, extensions, background tasks, account, team, sync, privacy, file drops,\n remote desktop, permissions, hooks (9 events), env, about, shortcuts (31 rebindable commands).\n- **Theming**: DTCG design tokens, theme packs with **8 independently switchable facets** (colors,\n shape, fonts, background, decorations, sounds, intro, extraCss) and a 3-entry-point **Safe Mode** to\n recover from a bad pack.\n- **Accessibility & motion**: light/dark \xD7 comfortable/compact density throughout, reduced-motion\n and sound-mute as separate switches, and labelled roles on every interactive element. Animation\n is a product requirement, not a preference \u2014 every show/hide, open/close and surface switch is\n animated (140\u2013220ms), and native unstyled controls are not used.\n- **OS permissions**: 7 tracked (microphone, camera, screen recording, full-disk access, accessibility,\n notifications, local network) with per-platform availability; three require a relaunch after grant.\n- **Observability**: crash/error reporting from 6 producers with separate crash and telemetry consent\n (**opt-out \u2014 on unless explicitly disabled**), fingerprinting and grouping, an error inspector, an\n alerting bridge, in-app issue submission, and a GDPR export-and-erase path.\n- **The always-on background service**: the desktop app *is* the daemon (merged process); the slim\n rdesk and mesh builds and the headless CLI keep the classic separate-service model, installed via\n launchd / systemd / Windows services. It self-heals silently, degrades from GUI to headless rather\n than dying, and only one daemon owns a machine at a time. This is why clipboard sync, remote\n access, scheduled turns and the \u2325Space HUD keep working with no app window open.\n- **Connectivity**: the relay is stateless and queues nothing \u2014 a dropped socket re-bootstraps rather\n than replaying. In-flight turns survive a disconnect and stay interruptible.\n- **Updates**: check \u2192 download \u2192 stage \u2192 apply \u2192 relaunch, with an external reaper, log rotation on\n version change and a What\'s New screen. System events (sleep/wake, lock, AC/battery, clock jump,\n displays, interfaces) drive recovery.\n\n## 18. Deliberately NOT built \u2014 do not promise these\n\n- **Create multiplayer co-editing / comments-in-Create** \u2014 deferred; the stores exist but are unwired.\n- **Marketplace** as a product \u2014 brainstorm only.\n- **Team chat**: voice messages, async video clips, keyword alerts, scheduled send, `/remind`, saved\n messages, guest/single-channel access, outgoing webhooks, AI catch-up/digest, public communities,\n nested threads, Block Kit, workflow builder, read receipts, E2E encryption for team chat.\n- **Create/Gamma parity gaps**: todo blocks, GIF/QR/illustrations, media library, form embeds, Google\n Slides export, theme gallery picker, share analytics, public generate API, custom domains, SEO\n toggle, password-protected publish, sites manager.\n- **Hosting**: the managed DB/auth/storage layer, secrets manager, git sync + 1-click Publish.\n- **Mobile**: file browsing, in-app browser, hosting an agent, rendering canvas/surface blocks.\n- **Web app**: undo-delete, local engines, runner configs, the errors panel, open/save-to-code for\n remote hosts.\n- **Mesh**: there is no VPN tunnel a user can turn on yet \u2014 PKI, protocol and data plane are built,\n route execution is not a shipped user feature. No Windows/macOS netd hosting.\n- **Other**: Homebrew formula, Chrome Web Store listing (the extension installs unpacked), Deepgram\n voice, Office (ZetaOffice) editing, AWS Bedrock and Google Vertex gateway routes, VNC clipboard,\n Wayland-specific clipboard handling, RN live widget tree (blocked upstream), the voice tutorial.\n\n## 19. Honest caveats\n\n- **Entitlements are almost entirely unenforced.** Only `plugins` is gated in production, and the\n desktop client is never gated at all. Both the API and the relay **fail open** on billing errors.\n- **Realtime voice has never run live against a provider** \u2014 treat first-run voice as unproven.\n- **HeyGen avatar video has no key configured** as shipped.\n- **The self-hosted web stack has no per-user data isolation** \u2014 auth there is a login wall over one\n shared workspace root.\n- `standard` = "Pro" and `pro` = "Team" \u2014 the plan id and the display name are inverted.\n';
33289
+ }
33290
+ });
33291
+
33292
+ // ../../packages/agent-core/dist/capabilities-tools.js
33293
+ import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
33294
+ function capabilitySections(doc = CAPABILITIES_DOC) {
33295
+ const out = [];
33296
+ const re = /^## (\d+)\. (.+)$/gm;
33297
+ const heads = [];
33298
+ for (let m = re.exec(doc); m; m = re.exec(doc)) {
33299
+ heads.push({ n: Number(m[1]), title: m[2].trim(), at: m.index, end: re.lastIndex });
33300
+ }
33301
+ for (let i = 0; i < heads.length; i++) {
33302
+ const h = heads[i];
33303
+ const stop2 = i + 1 < heads.length ? heads[i + 1].at : doc.length;
33304
+ out.push({ n: h.n, title: h.title, body: doc.slice(h.at, stop2).trimEnd() });
33305
+ }
33306
+ return out;
33307
+ }
33308
+ function findSections(query2, doc = CAPABILITIES_DOC) {
33309
+ const secs = capabilitySections(doc);
33310
+ const q = query2.trim().toLowerCase();
33311
+ if (!q)
33312
+ return [];
33313
+ const asNum = Number(q.replace(/^§/, ""));
33314
+ if (Number.isInteger(asNum) && asNum > 0) {
33315
+ const hit = secs.find((s) => s.n === asNum);
33316
+ if (hit)
33317
+ return [hit];
33318
+ }
33319
+ const byTitle = secs.filter((s) => s.title.toLowerCase().includes(q));
33320
+ if (byTitle.length > 0)
33321
+ return byTitle;
33322
+ return secs.filter((s) => s.body.toLowerCase().includes(q));
33323
+ }
33324
+ function buildCapabilitiesTools() {
33325
+ const index = capabilitySections().map((s) => `${s.n}. ${s.title}`).join(" \xB7 ");
33326
+ return [
33327
+ tool17("tesseract_capabilities", prompt("tool.capabilities.tesseract_capabilities", { sections: index }), {
33328
+ section: external_exports.string().optional().describe('Optional filter: a section number ("12"), a title word ("voice", "vault", "media"), or any keyword to search the body ("rdesk", "clipboard", "figma"). Omit for the full map.')
33329
+ }, async (args) => {
33330
+ const q = args?.section?.trim();
33331
+ if (!q)
33332
+ return text10(CAPABILITIES_DOC);
33333
+ const hits = findSections(q);
33334
+ if (hits.length === 0) {
33335
+ return text10(`No section of Tesseract's capability map matches "${q}". Sections: ${index}. Call tesseract_capabilities with no arguments for the whole map.`);
33336
+ }
33337
+ return text10(hits.map((s) => s.body).join("\n\n"));
33338
+ })
33339
+ ];
33340
+ }
33341
+ var text10;
33342
+ var init_capabilities_tools = __esm({
33343
+ "../../packages/agent-core/dist/capabilities-tools.js"() {
33344
+ "use strict";
33345
+ init_zod();
33346
+ init_prompts();
33347
+ init_capabilities_doc_generated();
33348
+ text10 = (t) => ({ content: [{ type: "text", text: t }] });
33349
+ }
33350
+ });
33351
+
32957
33352
  // ../../packages/serpapi-core/dist/types.js
32958
33353
  var init_types3 = __esm({
32959
33354
  "../../packages/serpapi-core/dist/types.js"() {
@@ -33480,8 +33875,8 @@ function isAbort(e) {
33480
33875
  const name = e?.name ?? "";
33481
33876
  return name === "AbortError" || name === "TimeoutError" || /aborted|abort/i.test(String(e?.message ?? ""));
33482
33877
  }
33483
- function redactSecrets(text12, secret) {
33484
- let out = text12.replace(/((?:api_key|apikey|token|access_token|key)=)[^&\s"'<>]{6,}/gi, "$1***");
33878
+ function redactSecrets(text13, secret) {
33879
+ let out = text13.replace(/((?:api_key|apikey|token|access_token|key)=)[^&\s"'<>]{6,}/gi, "$1***");
33485
33880
  if (secret && secret.length >= 8)
33486
33881
  out = out.split(secret).join("***");
33487
33882
  return out;
@@ -33588,13 +33983,13 @@ var init_client = __esm({
33588
33983
  */
33589
33984
  async searchMarkdown(engine, params, opts = {}) {
33590
33985
  const url2 = await this.buildUrl(engine, params, { ...opts, output: "md" });
33591
- const { res, text: text12, started: started2 } = await this.fetchWithRetry(url2, engine, opts, true);
33986
+ const { res, text: text13, started: started2 } = await this.fetchWithRetry(url2, engine, opts, true);
33592
33987
  this.opts.onUsage?.({ engine, cached: false, billed: true, ms: Date.now() - started2, status: res.status });
33593
- const asError = messageFromBody(text12) && redactSecrets(messageFromBody(text12), (await this.creds().catch(() => void 0))?.apiKey);
33594
- if (asError && text12.trimStart().startsWith("{")) {
33988
+ const asError = messageFromBody(text13) && redactSecrets(messageFromBody(text13), (await this.creds().catch(() => void 0))?.apiKey);
33989
+ if (asError && text13.trimStart().startsWith("{")) {
33595
33990
  throw new SerpApiError(asError, classifySerpError(res.status, asError), { status: res.status, engine });
33596
33991
  }
33597
- return { markdown: text12 };
33992
+ return { markdown: text13 };
33598
33993
  }
33599
33994
  /**
33600
33995
  * Follow a fully-formed SerpApi URL — specifically `serpapi_pagination.next`.
@@ -33632,8 +34027,8 @@ var init_client = __esm({
33632
34027
  const u = _SerpClient.join(baseUrl ?? DEFAULT_BASE_URL, "locations.json");
33633
34028
  u.searchParams.set("q", q);
33634
34029
  u.searchParams.set("limit", String(Math.min(Math.max(1, limit), 10)));
33635
- const { text: text12 } = await this.fetchWithRetry(u.toString(), "locations", {}, false);
33636
- const body = JSON.parse(text12 || "[]");
34030
+ const { text: text13 } = await this.fetchWithRetry(u.toString(), "locations", {}, false);
34031
+ const body = JSON.parse(text13 || "[]");
33637
34032
  return Array.isArray(body) ? body : [];
33638
34033
  }
33639
34034
  /** Retrieve a previously-run search by `search_metadata.id`. 31-day retention; also how you
@@ -33657,8 +34052,8 @@ var init_client = __esm({
33657
34052
  throw new SerpApiError(`Invalid search id "${searchId}".`, "bad_request");
33658
34053
  const u = _SerpClient.join(baseUrl ?? DEFAULT_BASE_URL, `searches/${safe2}.md`);
33659
34054
  u.searchParams.set("api_key", apiKey);
33660
- const { text: text12 } = await this.fetchWithRetry(u.toString(), "archive", {}, false);
33661
- return { markdown: text12 };
34055
+ const { text: text13 } = await this.fetchWithRetry(u.toString(), "archive", {}, false);
34056
+ return { markdown: text13 };
33662
34057
  }
33663
34058
  /** Upload an image (≤500 KB, JPG/PNG/WebP) for use with Lens / reverse-image engines.
33664
34059
  * The returned `image_id` expires after ~10 minutes. */
@@ -33757,9 +34152,9 @@ var init_client = __esm({
33757
34152
  this.opts.onUsage?.({ engine, cached: false, billed, ms: Date.now() - started2, error: msg });
33758
34153
  throw lastErr;
33759
34154
  }
33760
- const text12 = await res.text();
34155
+ const text13 = await res.text();
33761
34156
  if (!res.ok) {
33762
- const bodyMsg = redactSecrets(messageFromBody(text12), secret);
34157
+ const bodyMsg = redactSecrets(messageFromBody(text13), secret);
33763
34158
  const err2 = new SerpApiError(bodyMsg || `SerpApi ${engine} failed (${res.status}).`, classifySerpError(res.status, bodyMsg), { status: res.status, engine });
33764
34159
  if (err2.retryable && attempt < maxRetries && Date.now() < deadline) {
33765
34160
  lastErr = err2;
@@ -33769,7 +34164,7 @@ var init_client = __esm({
33769
34164
  this.opts.onUsage?.({ engine, cached: false, billed, ms: Date.now() - started2, status: res.status, error: bodyMsg.slice(0, 200) });
33770
34165
  throw err2;
33771
34166
  }
33772
- return { res, text: text12, started: started2 };
34167
+ return { res, text: text13, started: started2 };
33773
34168
  }
33774
34169
  throw lastErr ?? new SerpApiError(`SerpApi ${engine} failed.`, "unknown", { engine });
33775
34170
  }
@@ -33786,10 +34181,10 @@ var init_client = __esm({
33786
34181
  if (hit)
33787
34182
  this.cache.delete(key2);
33788
34183
  }
33789
- const { res, text: text12, started: started2 } = await this.fetchWithRetry(url2, engine, opts, billed);
34184
+ const { res, text: text13, started: started2 } = await this.fetchWithRetry(url2, engine, opts, billed);
33790
34185
  let body;
33791
34186
  try {
33792
- body = JSON.parse(text12);
34187
+ body = JSON.parse(text13);
33793
34188
  } catch {
33794
34189
  throw new SerpApiError(`SerpApi ${engine} returned a non-JSON body.`, "unknown", { status: res.status, engine });
33795
34190
  }
@@ -34505,7 +34900,7 @@ var serpapi_tools_exports = {};
34505
34900
  __export(serpapi_tools_exports, {
34506
34901
  buildSerpTools: () => buildSerpTools
34507
34902
  });
34508
- import { tool as tool17 } from "@anthropic-ai/claude-agent-sdk";
34903
+ import { tool as tool18 } from "@anthropic-ai/claude-agent-sdk";
34509
34904
  import { openSync as openSync3, readSync as readSync2, closeSync as closeSync3, statSync as statSync3, readFileSync as readFileSync6 } from "node:fs";
34510
34905
  import { basename as basename2 } from "node:path";
34511
34906
  function imageKind(path6) {
@@ -34539,14 +34934,14 @@ function catalogOnce() {
34539
34934
  }
34540
34935
  function buildSerpTools(deps) {
34541
34936
  const tools = [];
34542
- tools.push(tool17("serp_find", 'Search the SerpApi catalog of 121 search engines to find which one answers a question. Describe what you want in plain language ("hotel reviews", "who is advertising against my brand", "stock price") \u2014 engine ids are not guessable, so START HERE unless you already know the engine. Returns ranked engines with the family tool that runs them and their required parameters.', {
34937
+ tools.push(tool18("serp_find", 'Search the SerpApi catalog of 121 search engines to find which one answers a question. Describe what you want in plain language ("hotel reviews", "who is advertising against my brand", "stock price") \u2014 engine ids are not guessable, so START HERE unless you already know the engine. Returns ranked engines with the family tool that runs them and their required parameters.', {
34543
34938
  query: external_exports.string().describe("What you are trying to find out, in plain language."),
34544
34939
  family: external_exports.string().optional().describe(`Restrict to one family: ${Object.keys(SERP_FAMILIES).join(", ")}.`),
34545
34940
  limit: external_exports.number().int().min(1).max(30).optional().describe("How many engines to return (default 8).")
34546
34941
  }, async (a) => {
34547
34942
  const { query: query2, family, limit } = a ?? {};
34548
34943
  if (family && !(family in SERP_FAMILIES)) {
34549
- return text10(`Unknown family "${family}". Valid families: ${Object.keys(SERP_FAMILIES).join(", ")}.`, true);
34944
+ return text11(`Unknown family "${family}". Valid families: ${Object.keys(SERP_FAMILIES).join(", ")}.`, true);
34550
34945
  }
34551
34946
  const r = await findEngines(query2 ?? "", catalogOnce().filter((e) => !UNAVAILABLE.has(e.id)), {
34552
34947
  limit,
@@ -34561,7 +34956,7 @@ function buildSerpTools(deps) {
34561
34956
  next: "Call serp_describe with an engine id for its full parameter list, then run it with the named family tool."
34562
34957
  });
34563
34958
  }));
34564
- tools.push(tool17("serp_describe", "Describe one or more SerpApi engines in full: every parameter with its meaning, which are required, allowed values, how the engine paginates, and which result keys it returns. Call this before running an engine whose parameters you are unsure about.", {
34959
+ tools.push(tool18("serp_describe", "Describe one or more SerpApi engines in full: every parameter with its meaning, which are required, allowed values, how the engine paginates, and which result keys it returns. Call this before running an engine whose parameters you are unsure about.", {
34565
34960
  engines: external_exports.array(external_exports.string()).min(1).max(MAX_DESCRIBE_ENGINES).describe(`Engine ids, e.g. ["google_hotels", "google_maps"]. At most ${MAX_DESCRIBE_ENGINES} per call \u2014 a full schema is thousands of tokens, so ask for the ones you are actually about to use.`),
34566
34961
  group: external_exports.string().optional().describe('Only describe parameters in this registry group (e.g. "search_query", "localization", "advanced_filters"), to keep the answer short.')
34567
34962
  }, async (a) => {
@@ -34606,10 +35001,10 @@ function buildSerpTools(deps) {
34606
35001
  }
34607
35002
  return json2(out);
34608
35003
  }));
34609
- tools.push(tool17("serp_account", "Check that SerpApi is configured and how many searches remain this month. This call is free and never consumes a search credit. Call it before a long series of searches, or when a search fails with a quota error.", {}, async () => {
35004
+ tools.push(tool18("serp_account", "Check that SerpApi is configured and how many searches remain this month. This call is free and never consumes a search credit. Call it before a long series of searches, or when a search fails with a quota error.", {}, async () => {
34610
35005
  const st = await deps.status();
34611
35006
  if (!st.configured)
34612
- return text10(NO_KEY, true);
35007
+ return text11(NO_KEY, true);
34613
35008
  try {
34614
35009
  const acct = await deps.client.account();
34615
35010
  return json2({
@@ -34625,10 +35020,10 @@ function buildSerpTools(deps) {
34625
35020
  ...st.session ? { this_session: st.session } : {}
34626
35021
  });
34627
35022
  } catch (e) {
34628
- return text10(`Could not read the SerpApi account: ${e instanceof Error ? e.message : String(e)}`, true);
35023
+ return text11(`Could not read the SerpApi account: ${e instanceof Error ? e.message : String(e)}`, true);
34629
35024
  }
34630
35025
  }));
34631
- tools.push(tool17("serp_locations", 'Resolve a place name to the canonical `location` string Google\'s engines expect (e.g. "Austin" \u2192 "Austin, Texas, United States"). Free \u2014 it does not consume a search credit. Use it before passing `location` to a Google engine; an unrecognised location is silently ignored and you get results for the wrong place.', {
35026
+ tools.push(tool18("serp_locations", 'Resolve a place name to the canonical `location` string Google\'s engines expect (e.g. "Austin" \u2192 "Austin, Texas, United States"). Free \u2014 it does not consume a search credit. Use it before passing `location` to a Google engine; an unrecognised location is silently ignored and you get results for the wrong place.', {
34632
35027
  query: external_exports.string().describe('Place name to resolve, e.g. "Chisinau" or "Austin".'),
34633
35028
  limit: external_exports.number().int().min(1).max(10).optional().describe("Maximum matches (SerpApi caps this at 10).")
34634
35029
  }, async (a) => {
@@ -34637,10 +35032,10 @@ function buildSerpTools(deps) {
34637
35032
  const hits = await deps.client.locations(query2, limit ?? 5);
34638
35033
  return json2(hits.map((h) => ({ canonical_name: h.canonical_name, country: h.country_code, type: h.target_type, reach: h.reach })));
34639
35034
  } catch (e) {
34640
- return text10(`Location lookup failed: ${e instanceof Error ? e.message : String(e)}`, true);
35035
+ return text11(`Location lookup failed: ${e instanceof Error ? e.message : String(e)}`, true);
34641
35036
  }
34642
35037
  }));
34643
- tools.push(tool17("serp_archive", "Re-read a search that was already run, by its `search_id` (returned as `_search_id` on every result, and by a `submit_async` submission). FREE \u2014 retrieval never re-runs the search and never spends a credit, and SerpApi keeps results for 31 days. Use it to collect an async submission, to re-read a large result you truncated, or to get the markdown rendering of something you already fetched as JSON \u2014 all without paying again.", {
35038
+ tools.push(tool18("serp_archive", "Re-read a search that was already run, by its `search_id` (returned as `_search_id` on every result, and by a `submit_async` submission). FREE \u2014 retrieval never re-runs the search and never spends a credit, and SerpApi keeps results for 31 days. Use it to collect an async submission, to re-read a large result you truncated, or to get the markdown rendering of something you already fetched as JSON \u2014 all without paying again.", {
34644
35039
  search_id: external_exports.string().describe("The id from `_search_id`, or from a `submit_async` submission."),
34645
35040
  format: external_exports.enum(["json", "markdown"]).optional().describe("`json` (default) is structured; `markdown` is roughly half the tokens but carries no chaining tokens."),
34646
35041
  max_results: external_exports.number().int().min(1).max(100).optional().describe("How many items to keep per result list before truncating (default 10).")
@@ -34648,50 +35043,50 @@ function buildSerpTools(deps) {
34648
35043
  const { search_id, format, max_results } = a ?? {};
34649
35044
  const st = await deps.status();
34650
35045
  if (!st.configured)
34651
- return text10(NO_KEY, true);
35046
+ return text11(NO_KEY, true);
34652
35047
  try {
34653
35048
  if (format === "markdown") {
34654
35049
  const { markdown } = await deps.client.archiveMarkdown(search_id);
34655
- return text10(markdown);
35050
+ return text11(markdown);
34656
35051
  }
34657
35052
  const body = await deps.client.archive(search_id);
34658
35053
  const status2 = body.search_metadata?.status;
34659
35054
  if (status2 && status2 !== "Success") {
34660
- return text10(`Search ${search_id} is ${status2}. This call was free \u2014 call serp_archive again in a moment.`);
35055
+ return text11(`Search ${search_id} is ${status2}. This call was free \u2014 call serp_archive again in a moment.`);
34661
35056
  }
34662
35057
  return json2(compactResponse(body, { maxResults: max_results ?? 10 }));
34663
35058
  } catch (e) {
34664
- return text10(`Could not read search ${search_id}: ${e instanceof Error ? e.message : String(e)}`, true);
35059
+ return text11(`Could not read search ${search_id}: ${e instanceof Error ? e.message : String(e)}`, true);
34665
35060
  }
34666
35061
  }));
34667
- tools.push(tool17("serp_upload_image", "Upload a local image and get back an `image_id` for the engines that search BY an image rather than by text \u2014 `google_lens`, `google_reverse_image`, `bing_reverse_image`, `yandex_images`. Free; it spends no search credit. The id expires after about ten minutes, so upload immediately before the search that uses it. SerpApi accepts JPG, PNG and WebP up to 500 KB.", {
35062
+ tools.push(tool18("serp_upload_image", "Upload a local image and get back an `image_id` for the engines that search BY an image rather than by text \u2014 `google_lens`, `google_reverse_image`, `bing_reverse_image`, `yandex_images`. Free; it spends no search credit. The id expires after about ten minutes, so upload immediately before the search that uses it. SerpApi accepts JPG, PNG and WebP up to 500 KB.", {
34668
35063
  path: external_exports.string().describe("Absolute path to the image file on this machine.")
34669
35064
  }, async (a) => {
34670
35065
  const { path: path6 } = a ?? {};
34671
35066
  const st = await deps.status();
34672
35067
  if (!st.configured)
34673
- return text10(NO_KEY, true);
35068
+ return text11(NO_KEY, true);
34674
35069
  let bytes3;
34675
35070
  try {
34676
35071
  const size = statSync3(path6).size;
34677
35072
  if (size === 0)
34678
- return text10(`${path6} is empty.`, true);
35073
+ return text11(`${path6} is empty.`, true);
34679
35074
  if (size > MAX_IMAGE_BYTES) {
34680
- return text10(`${path6} is ${Math.round(size / 1024)} KB; SerpApi accepts images up to 500 KB. Resize it first.`, true);
35075
+ return text11(`${path6} is ${Math.round(size / 1024)} KB; SerpApi accepts images up to 500 KB. Resize it first.`, true);
34681
35076
  }
34682
35077
  const kind = imageKind(path6);
34683
35078
  if (!kind)
34684
- return text10(`${path6} is not a JPEG, PNG or WebP image \u2014 those are the formats SerpApi accepts, and this tool only uploads images.`, true);
35079
+ return text11(`${path6} is not a JPEG, PNG or WebP image \u2014 those are the formats SerpApi accepts, and this tool only uploads images.`, true);
34685
35080
  bytes3 = readFileSync6(path6);
34686
35081
  } catch (e) {
34687
- return text10(`Could not read ${path6}: ${e instanceof Error ? e.message : String(e)}`, true);
35082
+ return text11(`Could not read ${path6}: ${e instanceof Error ? e.message : String(e)}`, true);
34688
35083
  }
34689
35084
  try {
34690
35085
  const { image_id } = await deps.client.uploadImage(bytes3, basename2(path6));
34691
- return text10(`image_id: ${image_id}
35086
+ return text11(`image_id: ${image_id}
34692
35087
  Pass it as the image parameter of a reverse-image engine (google_lens uses \`url\`; call serp_describe for the exact field). It expires in about ten minutes.`);
34693
35088
  } catch (e) {
34694
- return text10(`Image upload failed: ${e instanceof Error ? e.message : String(e)}`, true);
35089
+ return text11(`Image upload failed: ${e instanceof Error ? e.message : String(e)}`, true);
34695
35090
  }
34696
35091
  }));
34697
35092
  for (const [family, info] of Object.entries(SERP_FAMILIES)) {
@@ -34701,7 +35096,7 @@ Pass it as the image parameter of a reverse-image engine (google_lens uses \`url
34701
35096
  const ids = [...engines2];
34702
35097
  const listing = engines2.map((id) => `${id} \u2014 ${ENGINE_INFO[id]?.purpose ?? ""}`).join("; ");
34703
35098
  const trapped = engines2.filter((id) => ENGINE_INFO[id]?.traps?.length);
34704
- tools.push(tool17(info.tool, `${info.blurb} Pick an \`engine\` and pass its \`params\` (use serp_describe for the exact fields, or serp_find if you are not sure which engine). Each call spends one paid search credit. Engines: ${listing}${trapped.length ? ` \u2014 QUIRKS (serp_describe first): ${trapped.join(", ")}. Some reject the call; others accept a parameter and ignore it, returning confident results for the wrong place.` : ""}`, {
35099
+ tools.push(tool18(info.tool, `${info.blurb} Pick an \`engine\` and pass its \`params\` (use serp_describe for the exact fields, or serp_find if you are not sure which engine). Each call spends one paid search credit. Engines: ${listing}${trapped.length ? ` \u2014 QUIRKS (serp_describe first): ${trapped.join(", ")}. Some reject the call; others accept a parameter and ignore it, returning confident results for the wrong place.` : ""}`, {
34705
35100
  engine: external_exports.enum(ids).describe("Which engine to run."),
34706
35101
  params: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("The engine's own parameters. Names differ per engine \u2014 Yahoo uses `p`, Yandex `text`, Naver `query`, Amazon `k`, eBay `_nkw`. Call serp_describe if unsure."),
34707
35102
  ...PAGE_ARGS,
@@ -34716,13 +35111,13 @@ Pass it as the image parameter of a reverse-image engine (google_lens uses \`url
34716
35111
  async function runEngine(deps, args) {
34717
35112
  const st = await deps.status();
34718
35113
  if (!st.configured)
34719
- return text10(NO_KEY, true);
35114
+ return text11(NO_KEY, true);
34720
35115
  const spec = SERP_SPEC.engines[args.engine];
34721
35116
  if (!spec)
34722
- return text10(`Unknown engine "${args.engine}". Call serp_find to locate the right one.`, true);
35117
+ return text11(`Unknown engine "${args.engine}". Call serp_find to locate the right one.`, true);
34723
35118
  const info = ENGINE_INFO[args.engine];
34724
35119
  if (info?.status) {
34725
- return text10(`\`${args.engine}\` is ${info.status.state} and cannot be used: ${info.status.detail} No credit was spent. ${info.purpose}`, true);
35120
+ return text11(`\`${args.engine}\` is ${info.status.state} and cannot be used: ${info.status.detail} No credit was spent. ${info.purpose}`, true);
34726
35121
  }
34727
35122
  const strategy = pageStrategy(spec);
34728
35123
  const rawParams = { ...args.params ?? {} };
@@ -34737,7 +35132,7 @@ async function runEngine(deps, args) {
34737
35132
  v.warnings.push(`Both \`page_size\` (${args.page_size}) and \`${sizeParam}\` (${explicitSize}) were given; \`page_size\` wins.`);
34738
35133
  }
34739
35134
  if (!v.ok) {
34740
- return text10(`Cannot run \`${args.engine}\` \u2014 no credit spent.
35135
+ return text11(`Cannot run \`${args.engine}\` \u2014 no credit spent.
34741
35136
  ${v.errors.map((e) => `\u2022 ${e}`).join("\n")}
34742
35137
 
34743
35138
  Call serp_describe({engines:["${args.engine}"]}) for the full parameter list.`, true);
@@ -34755,8 +35150,8 @@ Call serp_describe({engines:["${args.engine}"]}) for the full parameter list.`,
34755
35150
  const submitted = await deps.client.search(args.engine, { ...v.params, ...paging }, opts);
34756
35151
  const id = submitted.search_metadata?.id;
34757
35152
  if (!id)
34758
- return text10(`\`${args.engine}\` was submitted but SerpApi returned no search id, so the result cannot be collected. Re-run it without submit_async.`, true);
34759
- return text10(`Submitted \`${args.engine}\` \u2014 search_id: ${id}
35153
+ return text11(`\`${args.engine}\` was submitted but SerpApi returned no search id, so the result cannot be collected. Re-run it without submit_async.`, true);
35154
+ return text11(`Submitted \`${args.engine}\` \u2014 search_id: ${id}
34760
35155
  The credit is already spent. Collect it with serp_archive({ search_id: "${id}" }); that call is free, so retry it until the status is Success.`);
34761
35156
  }
34762
35157
  if (args.format === "markdown") {
@@ -34764,7 +35159,7 @@ The credit is already spent. Collect it with serp_archive({ search_id: "${id}" }
34764
35159
  const warn = v.warnings.length ? `${v.warnings.map((w) => `\u26A0 ${w}`).join("\n")}
34765
35160
 
34766
35161
  ` : "";
34767
- return text10(`${warn}${markdown}`);
35162
+ return text11(`${warn}${markdown}`);
34768
35163
  }
34769
35164
  const body = await deps.client.search(args.engine, { ...v.params, ...paging }, opts);
34770
35165
  const compact2 = compactResponse(body, { maxResults: args.max_results ?? 10 });
@@ -34775,27 +35170,27 @@ The credit is already spent. Collect it with serp_archive({ search_id: "${id}" }
34775
35170
  if (body._page?.hasNext && supportsPaging(spec)) {
34776
35171
  parts.push(body._page.cursor ? `More results available \u2014 call again with cursor: "${body._page.cursor}".` : `More results available \u2014 call again with page: ${(args.page ?? 1) + 1}.`);
34777
35172
  }
34778
- return text10(parts.join("\n\n"));
35173
+ return text11(parts.join("\n\n"));
34779
35174
  } catch (e) {
34780
35175
  const msg = e instanceof Error ? e.message : String(e);
34781
- return text10(`\`${args.engine}\` failed: ${msg}`, true);
35176
+ return text11(`\`${args.engine}\` failed: ${msg}`, true);
34782
35177
  }
34783
35178
  }
34784
- var text10, json2, UNAVAILABLE, MAX_DESCRIBE_ENGINES, MAX_PARAM_DESC, clip, MAX_IMAGE_BYTES, NO_KEY, PAGE_ARGS, OUTPUT_ARGS, cachedCatalog, brief;
35179
+ var text11, json2, UNAVAILABLE, MAX_DESCRIBE_ENGINES, MAX_PARAM_DESC, clip, MAX_IMAGE_BYTES, NO_KEY, PAGE_ARGS, OUTPUT_ARGS, cachedCatalog, brief;
34785
35180
  var init_serpapi_tools = __esm({
34786
35181
  "../../packages/agent-core/dist/serpapi/serpapi-tools.js"() {
34787
35182
  "use strict";
34788
35183
  init_zod();
34789
35184
  init_dist4();
34790
- text10 = (t, isError = false) => ({
35185
+ text11 = (t, isError = false) => ({
34791
35186
  content: [{ type: "text", text: t }],
34792
35187
  ...isError ? { isError: true } : {}
34793
35188
  });
34794
- json2 = (v) => text10(JSON.stringify(v, null, 2));
35189
+ json2 = (v) => text11(JSON.stringify(v, null, 2));
34795
35190
  UNAVAILABLE = new Set(UNAVAILABLE_ENGINES);
34796
35191
  MAX_DESCRIBE_ENGINES = 3;
34797
35192
  MAX_PARAM_DESC = 400;
34798
- clip = (text12, full) => !full && text12 && text12.length > MAX_PARAM_DESC ? `${text12.slice(0, MAX_PARAM_DESC)}\u2026 (truncated \u2014 ask for this engine on its own for the full text)` : text12;
35193
+ clip = (text13, full) => !full && text13 && text13.length > MAX_PARAM_DESC ? `${text13.slice(0, MAX_PARAM_DESC)}\u2026 (truncated \u2014 ask for this engine on its own for the full text)` : text13;
34799
35194
  MAX_IMAGE_BYTES = 500 * 1024;
34800
35195
  NO_KEY = "SerpApi is not configured. Ask the user to add a SerpApi key in Settings \u2192 Search, or to sign in to use the bundled free tier.";
34801
35196
  PAGE_ARGS = {
@@ -34822,13 +35217,13 @@ var init_serpapi_tools = __esm({
34822
35217
  });
34823
35218
 
34824
35219
  // ../../packages/agent-core/dist/report-tools.js
34825
- import { tool as tool18 } from "@anthropic-ai/claude-agent-sdk";
35220
+ import { tool as tool19 } from "@anthropic-ai/claude-agent-sdk";
34826
35221
  function slug(s) {
34827
35222
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "mission";
34828
35223
  }
34829
35224
  function buildReportTools(deps) {
34830
35225
  return [
34831
- tool18("emit_report", prompt("tool.report.emit_report"), {
35226
+ tool19("emit_report", prompt("tool.report.emit_report"), {
34832
35227
  id: external_exports.string().optional().describe("Stable report id to UPDATE an existing report; omit on the first call to create one."),
34833
35228
  playbookId: external_exports.string().describe("The playbook id this report belongs to."),
34834
35229
  title: external_exports.string().describe("Report title (usually the playbook name)."),
@@ -34882,10 +35277,10 @@ var init_report_tools = __esm({
34882
35277
  });
34883
35278
 
34884
35279
  // ../../packages/agent-core/dist/interactive-ui-tools.js
34885
- import { tool as tool19 } from "@anthropic-ai/claude-agent-sdk";
35280
+ import { tool as tool20 } from "@anthropic-ai/claude-agent-sdk";
34886
35281
  function buildInteractiveUiTools(deps) {
34887
35282
  return [
34888
- tool19("ask_form", prompt("tool.interactive-ui.ask_form"), {
35283
+ tool20("ask_form", prompt("tool.interactive-ui.ask_form"), {
34889
35284
  title: external_exports.string().describe("Form heading (plain language)."),
34890
35285
  description: external_exports.string().optional().describe("Short explanation shown above the fields."),
34891
35286
  submitLabel: external_exports.string().optional().describe('Submit button label (default "Submit").'),
@@ -34899,7 +35294,7 @@ function buildInteractiveUiTools(deps) {
34899
35294
  return { content: [{ type: "text", text: `The user submitted the form:
34900
35295
  ${JSON.stringify(res.values ?? {}, null, 2)}` }] };
34901
35296
  }),
34902
- tool19("present_choices", prompt("tool.interactive-ui.present_choices"), {
35297
+ tool20("present_choices", prompt("tool.interactive-ui.present_choices"), {
34903
35298
  title: external_exports.string().describe("The question / heading."),
34904
35299
  description: external_exports.string().optional(),
34905
35300
  options: external_exports.array(choiceOption).min(1),
@@ -34912,7 +35307,7 @@ ${JSON.stringify(res.values ?? {}, null, 2)}` }] };
34912
35307
  return { content: [{ type: "text", text: "The user cancelled the choice (nothing selected)." }] };
34913
35308
  return { content: [{ type: "text", text: `The user selected: ${JSON.stringify(res.selected ?? [])}` }] };
34914
35309
  }),
34915
- tool19("update_progress", prompt("tool.interactive-ui.update_progress"), {
35310
+ tool20("update_progress", prompt("tool.interactive-ui.update_progress"), {
34916
35311
  stages: external_exports.array(external_exports.object({
34917
35312
  label: external_exports.string(),
34918
35313
  status: external_exports.enum(["pending", "active", "done"])
@@ -35037,8 +35432,8 @@ async function exportSession(sessionId, fromCwd) {
35037
35432
  function escapeRe(s) {
35038
35433
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35039
35434
  }
35040
- function rekey(text12, fromCwd, toCwd) {
35041
- return text12.replace(new RegExp(escapeRe(fromCwd) + '(?=["/\\\\]|$)', "g"), () => toCwd);
35435
+ function rekey(text13, fromCwd, toCwd) {
35436
+ return text13.replace(new RegExp(escapeRe(fromCwd) + '(?=["/\\\\]|$)', "g"), () => toCwd);
35042
35437
  }
35043
35438
  async function importSession(exp, toCwd) {
35044
35439
  const destDir = sessionDir(toCwd);
@@ -35311,13 +35706,13 @@ var init_narrator = __esm({
35311
35706
  prompt("side-llm.narrator.activity", { activity }),
35312
35707
  prompt("side-llm.narrator.ask")
35313
35708
  ].filter(Boolean).join("\n\n");
35314
- const text12 = (await cfg2.complete({ system, prompt: promptText, maxTokens: 48 }))?.trim().replace(/^["']|["']$/g, "");
35315
- if (text12 && text12 !== s.lastText) {
35316
- s.lastText = text12;
35317
- s.log.push(text12);
35709
+ const text13 = (await cfg2.complete({ system, prompt: promptText, maxTokens: 48 }))?.trim().replace(/^["']|["']$/g, "");
35710
+ if (text13 && text13 !== s.lastText) {
35711
+ s.lastText = text13;
35712
+ s.log.push(text13);
35318
35713
  if (s.log.length > LOG_CAP)
35319
35714
  s.log.shift();
35320
- this.emit(id, text12, cfg2.engineLabel);
35715
+ this.emit(id, text13, cfg2.engineLabel);
35321
35716
  }
35322
35717
  } catch {
35323
35718
  } finally {
@@ -35459,15 +35854,15 @@ function recordVerificationDecision(entry) {
35459
35854
  function readUnattendedDecisions(limit = 200, opts) {
35460
35855
  const out = [];
35461
35856
  for (const file2 of [rolledPath(), unattendedLogPath()]) {
35462
- let text12;
35857
+ let text13;
35463
35858
  try {
35464
35859
  if (!existsSync2(file2))
35465
35860
  continue;
35466
- text12 = readFileSync7(file2, "utf8");
35861
+ text13 = readFileSync7(file2, "utf8");
35467
35862
  } catch {
35468
35863
  continue;
35469
35864
  }
35470
- for (const line of text12.split("\n")) {
35865
+ for (const line of text13.split("\n")) {
35471
35866
  if (!line.trim())
35472
35867
  continue;
35473
35868
  try {
@@ -36230,10 +36625,10 @@ function assistantContextOverflow(content, isContextOverflowFn) {
36230
36625
  const blocks = content;
36231
36626
  if (blocks.some((block2) => block2?.type !== "text"))
36232
36627
  return false;
36233
- const text12 = blocks.map((b) => typeof b.text === "string" ? b.text : "").join("\n").trim();
36234
- if (!text12 || text12.length > 400)
36628
+ const text13 = blocks.map((b) => typeof b.text === "string" ? b.text : "").join("\n").trim();
36629
+ if (!text13 || text13.length > 400)
36235
36630
  return false;
36236
- return isContextOverflowFn(text12);
36631
+ return isContextOverflowFn(text13);
36237
36632
  }
36238
36633
  var OVERFLOW_MAX_TRIES, OVERFLOW_COMPACT_MESSAGE, OVERFLOW_RESUME_MESSAGE;
36239
36634
  var init_context_recovery = __esm({
@@ -36343,8 +36738,8 @@ function pendingQueueRows(rows) {
36343
36738
  }
36344
36739
  return keep;
36345
36740
  }
36346
- function stripHostText(text12) {
36347
- return stripRunDirective(text12).trimEnd();
36741
+ function stripHostText(text13) {
36742
+ return stripRunDirective(text13).trimEnd();
36348
36743
  }
36349
36744
  function rejectedByApi(b) {
36350
36745
  if (!b || typeof b !== "object")
@@ -36653,10 +37048,10 @@ function filterHistory(transcript, opts = {}) {
36653
37048
  const parent = resolveParent2(r.parentUuid);
36654
37049
  out.push(JSON.stringify(parent === (r.parentUuid ?? null) ? r : { ...r, parentUuid: parent }));
36655
37050
  }
36656
- const text12 = out.join("\n") + (out.length ? "\n" : "");
37051
+ const text13 = out.join("\n") + (out.length ? "\n" : "");
36657
37052
  stats.rowsOut = out.length;
36658
- stats.bytesOut = Buffer.byteLength(text12, "utf8");
36659
- return { text: text12, stats, spills };
37053
+ stats.bytesOut = Buffer.byteLength(text13, "utf8");
37054
+ return { text: text13, stats, spills };
36660
37055
  }
36661
37056
  var DEFAULT_DROP_TYPES, DEFAULT_KEEP_LAST_TYPES, DEFAULT_KEEP_SYSTEM_SUBTYPES, DEFAULT_KEEP_LAST_ATTACHMENT, DEFAULT_OFFLOAD_RECENT_CALLS, MAX_INLINE_INPUT_BYTES, OFFLOAD_MARK, human;
36662
37057
  var init_history_filter = __esm({
@@ -36689,9 +37084,9 @@ function archiveMetaPath(transcript) {
36689
37084
  function offloadDirFor(transcript) {
36690
37085
  return transcript.replace(/\.jsonl$/, "") + ".offload";
36691
37086
  }
36692
- function contentBytes(text12, liveOnly = true) {
37087
+ function contentBytes(text13, liveOnly = true) {
36693
37088
  let n = 0;
36694
- const all = text12.split("\n");
37089
+ const all = text13.split("\n");
36695
37090
  for (const line of liveOnly ? all.slice(liveRegionStart(all)) : all) {
36696
37091
  if (!line.trim())
36697
37092
  continue;
@@ -36750,9 +37145,9 @@ async function slimTranscriptFile(transcript, opts = {}) {
36750
37145
  const liveText = await readFile4(transcript, "utf8");
36751
37146
  const liveLines = liveText.split("\n").filter((l) => l.trim().length > 0);
36752
37147
  const offloadDir = offloadDirFor(transcript);
36753
- const { text: text12, stats, spills } = filterHistory(liveText, { spillDir: offloadDir, ...opts });
37148
+ const { text: text13, stats, spills } = filterHistory(liveText, { spillDir: offloadDir, ...opts });
36754
37149
  const before = contentBytes(liveText);
36755
- const after = contentBytes(text12);
37150
+ const after = contentBytes(text13);
36756
37151
  const changed = stats.droppedRows > 0 || stats.droppedThinking > 0 || stats.droppedToolUse > 0 || stats.droppedToolResult > 0 || stats.offloaded > 0 || stats.supersededAttachments > 0 || stats.repairedBlocks > 0;
36757
37152
  if (!changed) {
36758
37153
  return { slimmed: false, reason: "nothing to strip", contentBefore: before, contentAfter: after, stats };
@@ -36777,9 +37172,9 @@ async function slimTranscriptFile(transcript, opts = {}) {
36777
37172
  return { slimmed: false, reason: "the transcript changed during the strip \u2014 left as is", contentBefore: before, contentAfter: after, stats };
36778
37173
  }
36779
37174
  const tmp = join11(dirname7(transcript), `.slim-${randomUUID2().slice(0, 8)}.tmp`);
36780
- await writeFile4(tmp, text12, { mode: 384 });
37175
+ await writeFile4(tmp, text13, { mode: 384 });
36781
37176
  await rename(tmp, transcript);
36782
- const outLines = text12.split("\n").filter((l) => l.trim().length > 0);
37177
+ const outLines = text13.split("\n").filter((l) => l.trim().length > 0);
36783
37178
  await writeFile4(archiveMetaPath(transcript), JSON.stringify({ rows: outLines.length, tail: outLines[outLines.length - 1] ?? "" }), { mode: 384 });
36784
37179
  return { slimmed: true, stats, contentBefore: before, contentAfter: after, archive, offloadDir };
36785
37180
  }
@@ -36920,8 +37315,8 @@ async function labelFromTranscript(file2) {
36920
37315
  if (row.type !== "user" && row.message?.role !== "user")
36921
37316
  continue;
36922
37317
  const c = row.message?.content;
36923
- const text12 = typeof c === "string" ? c : Array.isArray(c) ? c.find((b) => b.type === "text")?.text ?? "" : "";
36924
- const s = oneLine(text12);
37318
+ const text13 = typeof c === "string" ? c : Array.isArray(c) ? c.find((b) => b.type === "text")?.text ?? "" : "";
37319
+ const s = oneLine(text13);
36925
37320
  if (s)
36926
37321
  return s;
36927
37322
  }
@@ -37390,33 +37785,33 @@ function annotationIsOrphaned(e, removedUuids) {
37390
37785
  return false;
37391
37786
  return clearDanglingPointers(e, removedUuids) !== e;
37392
37787
  }
37393
- function isPromptEchoOf(e, removedUuids, text12) {
37788
+ function isPromptEchoOf(e, removedUuids, text13) {
37394
37789
  if (e.type !== "last-prompt")
37395
37790
  return false;
37396
37791
  const leaf = typeof e.leafUuid === "string" ? e.leafUuid : void 0;
37397
37792
  if (leaf && removedUuids.has(leaf))
37398
37793
  return true;
37399
- if (!text12)
37794
+ if (!text13)
37400
37795
  return false;
37401
37796
  const echo = typeof e.lastPrompt === "string" ? e.lastPrompt : void 0;
37402
- return echo === text12;
37797
+ return echo === text13;
37403
37798
  }
37404
37799
  function messageText(e) {
37405
37800
  const content = e.message?.content;
37406
37801
  if (typeof content === "string")
37407
37802
  return content;
37408
37803
  if (Array.isArray(content)) {
37409
- const text12 = content.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
37410
- return text12 || void 0;
37804
+ const text13 = content.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
37805
+ return text13 || void 0;
37411
37806
  }
37412
37807
  return void 0;
37413
37808
  }
37414
- function findUserEntryByText(entries, text12) {
37809
+ function findUserEntryByText(entries, text13) {
37415
37810
  for (let i = entries.length - 1; i >= 0; i--) {
37416
37811
  const e = entries[i];
37417
37812
  if (e.type !== "user" || !e.uuid)
37418
37813
  continue;
37419
- if (messageText(e) === text12)
37814
+ if (messageText(e) === text13)
37420
37815
  return e;
37421
37816
  }
37422
37817
  return void 0;
@@ -37985,10 +38380,10 @@ async function toSessionInfo(s) {
37985
38380
  const spoken = s.cwd ? await lastConversationTime(sessionFile(s.cwd, s.sessionId)) : null;
37986
38381
  return { id: s.sessionId, title, lastModified: spoken ?? s.lastModified, cwd: s.cwd };
37987
38382
  }
37988
- function stripPromptPrefix(text12) {
37989
- text12 = stripRunDirective(text12);
38383
+ function stripPromptPrefix(text13) {
38384
+ text13 = stripRunDirective(text13);
37990
38385
  const isMetadata = (l) => l.startsWith("I'm working on the page:") || l.startsWith("Selected elements:") || l.startsWith("Element ") || l.startsWith(" DOM Path:") || l.startsWith(" XPath:") || l.startsWith(" CSS:") || l.startsWith(" HTML:") || l.startsWith(" Text:") || l.startsWith(" Position:") || l.startsWith("Use the xpath") || l.startsWith("[") && l.includes("image(s) attached");
37991
- const userLines = text12.split("\n").filter((l) => !isMetadata(l));
38386
+ const userLines = text13.split("\n").filter((l) => !isMetadata(l));
37992
38387
  return userLines.join("\n").trim();
37993
38388
  }
37994
38389
  function extractBlocks(message) {
@@ -38007,13 +38402,13 @@ function extractBlocks(message) {
38007
38402
  continue;
38008
38403
  const b = block2;
38009
38404
  if (b.type === "thinking" && typeof b.thinking === "string") {
38010
- const text12 = b.thinking.trim();
38011
- if (text12)
38012
- blocks.push({ kind: "thinking", content: text12 });
38405
+ const text13 = b.thinking.trim();
38406
+ if (text13)
38407
+ blocks.push({ kind: "thinking", content: text13 });
38013
38408
  } else if (b.type === "text" && typeof b.text === "string") {
38014
- const text12 = b.text.trim();
38015
- if (text12 && !text12.startsWith("<ide_") && !text12.startsWith("<system-reminder>")) {
38016
- blocks.push({ kind: "text", content: text12 });
38409
+ const text13 = b.text.trim();
38410
+ if (text13 && !text13.startsWith("<ide_") && !text13.startsWith("<system-reminder>")) {
38411
+ blocks.push({ kind: "text", content: text13 });
38017
38412
  }
38018
38413
  } else if (b.type === "tool_use") {
38019
38414
  const name = b.name ?? "tool";
@@ -38059,9 +38454,9 @@ ${input.command}
38059
38454
  blocks.push({ kind: "tool_use", content: detail, name });
38060
38455
  } else if (b.type === "advisor_tool_result") {
38061
38456
  const c = b.content;
38062
- const text12 = c?.type === "advisor_result" ? (c.text ?? "").trim() : "";
38063
- if (text12)
38064
- blocks.push({ kind: "advisor", content: text12 });
38457
+ const text13 = c?.type === "advisor_result" ? (c.text ?? "").trim() : "";
38458
+ if (text13)
38459
+ blocks.push({ kind: "advisor", content: text13 });
38065
38460
  }
38066
38461
  }
38067
38462
  return blocks;
@@ -38403,8 +38798,8 @@ async function anthropicCall(args) {
38403
38798
  });
38404
38799
  if (res.ok) {
38405
38800
  const j = await res.json();
38406
- const text12 = (j.content ?? []).filter((b) => b.type === "text").map((b) => b.text ?? "").join("").trim() || null;
38407
- return { text: text12, rateLimited: false };
38801
+ const text13 = (j.content ?? []).filter((b) => b.type === "text").map((b) => b.text ?? "").join("").trim() || null;
38802
+ return { text: text13, rateLimited: false };
38408
38803
  }
38409
38804
  let errBody = "";
38410
38805
  try {
@@ -38728,8 +39123,8 @@ var init_model_switch = __esm({
38728
39123
 
38729
39124
  // ../../packages/agent-core/dist/usage-limit.js
38730
39125
  import { USAGE_LIMIT_ERROR_PREFIXES, USAGE_WARNING_PREFIXES, USAGE_TRANSITION_PREFIXES, ORG_POLICY_LIMIT_PREFIXES } from "@anthropic-ai/claude-agent-sdk";
38731
- function classifyUsageMessage(text12) {
38732
- const t = (text12 ?? "").trim();
39126
+ function classifyUsageMessage(text13) {
39127
+ const t = (text13 ?? "").trim();
38733
39128
  if (!t)
38734
39129
  return void 0;
38735
39130
  const hits = (list) => list.some((p) => t.startsWith(p));
@@ -38765,16 +39160,16 @@ function assistantUsageLimitFallback(content) {
38765
39160
  const blocks = content;
38766
39161
  if (blocks.some((block2) => block2?.type !== "text"))
38767
39162
  return void 0;
38768
- const text12 = blocks.map((block2) => typeof block2.text === "string" ? block2.text : "").join("\n").trim();
38769
- if (!text12 || text12.length > 600)
39163
+ const text13 = blocks.map((block2) => typeof block2.text === "string" ? block2.text : "").join("\n").trim();
39164
+ if (!text13 || text13.length > 600)
38770
39165
  return void 0;
38771
- const signal2 = classifyUsageMessage(text12);
39166
+ const signal2 = classifyUsageMessage(text13);
38772
39167
  if (signal2 !== "limit" && signal2 !== "org-policy")
38773
39168
  return void 0;
38774
- const isoReset = text12.match(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\b/i)?.[0];
38775
- const resetMatch = text12.match(/(?:reset|resets|available again)(?:s| at| on| in)?\s+([^\n]+)/i);
39169
+ const isoReset = text13.match(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\b/i)?.[0];
39170
+ const resetMatch = text13.match(/(?:reset|resets|available again)(?:s| at| on| in)?\s+([^\n]+)/i);
38776
39171
  const parsed = Date.parse(isoReset ?? resetMatch?.[1].trim() ?? "");
38777
- return { retryAt: Number.isFinite(parsed) ? parsed : void 0, signal: signal2, scopeText: text12 };
39172
+ return { retryAt: Number.isFinite(parsed) ? parsed : void 0, signal: signal2, scopeText: text13 };
38778
39173
  }
38779
39174
  function usageLimitErrorMessage(rejection) {
38780
39175
  if (rejection.signal === "org-policy") {
@@ -38789,16 +39184,30 @@ function usageLimitIsRotatable(rejection) {
38789
39184
  function usageLimitScopedModel(rejection, current) {
38790
39185
  if (rejection.signal === "org-policy")
38791
39186
  return void 0;
38792
- const text12 = (rejection.scopeText ?? "").trim();
38793
- if (!text12)
39187
+ const text13 = (rejection.scopeText ?? "").trim();
39188
+ if (!text13)
38794
39189
  return void 0;
38795
- const named = /reached your ([A-Za-z0-9.\- ]{1,30}?) limit/i.exec(text12)?.[1]?.trim().toLowerCase();
39190
+ const named = /reached your ([A-Za-z0-9.\- ]{1,30}?) limit/i.exec(text13)?.[1]?.trim().toLowerCase();
38796
39191
  if (!named)
38797
39192
  return void 0;
38798
39193
  if (current && !current.toLowerCase().includes(named))
38799
39194
  return void 0;
38800
39195
  return named;
38801
39196
  }
39197
+ function providerIsServing(content) {
39198
+ if (typeof content === "string")
39199
+ return content.trim().length > 0;
39200
+ if (!Array.isArray(content))
39201
+ return false;
39202
+ return content.some((blk) => {
39203
+ const b = blk;
39204
+ if (!b || typeof b !== "object")
39205
+ return false;
39206
+ if (b.type === "text")
39207
+ return typeof b.text === "string" && b.text.trim().length > 0;
39208
+ return b.type === "tool_use" || b.type === "server_tool_use" || b.type === "thinking";
39209
+ });
39210
+ }
38802
39211
  var init_usage_limit = __esm({
38803
39212
  "../../packages/agent-core/dist/usage-limit.js"() {
38804
39213
  "use strict";
@@ -38829,8 +39238,14 @@ function findUserInstalledClaude(binName) {
38829
39238
  const home = homedir3();
38830
39239
  const abs = [
38831
39240
  join17(home, ".claude", "local", binName),
38832
- // official installer (claude.ai/code)
39241
+ // legacy local npm install (older Claude Code)
38833
39242
  join17(home, ".local", "bin", binName),
39243
+ // the native installer's launcher — where we install
39244
+ // A copy Tesseract itself installed back when it used `npm install -g --prefix`. The probe has
39245
+ // always searched here, this list never did, so such an install read as "installed" in Settings
39246
+ // while the spawn said "not installed". AFTER ~/.local/bin deliberately: a current native
39247
+ // install must win over a stale npm one.
39248
+ join17(home, ".tesseract", "runtimes", "bin", binName),
38834
39249
  join17(home, ".bun", "bin", binName),
38835
39250
  join17(home, ".volta", "bin", binName),
38836
39251
  join17(home, "Library", "pnpm", binName),
@@ -38885,7 +39300,7 @@ function resolveClaudeCodeExecutable() {
38885
39300
  return cachedClaudeExe = bundled;
38886
39301
  }
38887
39302
  }
38888
- throw new Error("Claude Code isn\u2019t installed. Tesseract runs your own agent framework rather than bundling one \u2014 install Claude Code (https://claude.ai/code, or `npm i -g @anthropic-ai/claude-code`), or pick a different engine in Settings.");
39303
+ throw new Error("Claude Code isn\u2019t installed. Tesseract runs the agent framework you have installed rather than bundling one \u2014 install it with `curl -fsSL https://claude.ai/install.sh | bash` (Windows: `irm https://claude.ai/install.ps1 | iex`), or pick a different engine in Settings.");
38889
39304
  }
38890
39305
  function identityEnv() {
38891
39306
  const out = {};
@@ -39262,8 +39677,8 @@ function traceFrame(message) {
39262
39677
  const k = b?.type ?? "?";
39263
39678
  kinds.set(k, (kinds.get(k) ?? 0) + 1);
39264
39679
  }
39265
- const text12 = c.find((b) => b?.type === "text")?.text;
39266
- what = [...kinds].map(([k, n]) => n > 1 ? `${k}\xD7${n}` : k).join(",") + (text12 ? ` text:${JSON.stringify(text12.slice(0, 60))}` : "");
39680
+ const text13 = c.find((b) => b?.type === "text")?.text;
39681
+ what = [...kinds].map(([k, n]) => n > 1 ? `${k}\xD7${n}` : k).join(",") + (text13 ? ` text:${JSON.stringify(text13.slice(0, 60))}` : "");
39267
39682
  }
39268
39683
  log(`frame user${parent}${flags.length ? " [" + flags.join(",") + "]" : ""}${what ? " \u2014 " + what : ""}`);
39269
39684
  return;
@@ -39354,8 +39769,8 @@ function describeSystemSubtype(subtype, m) {
39354
39769
  const updateKey = category === "hook" ? `hook:${String(m.hook_name ?? m.hook_event_name ?? subtype)}` : category === "task" ? `task:${String(m.task_id ?? subtype)}` : category === "telemetry" ? `sys:${subtype}` : void 0;
39355
39770
  return { category, name: subtype, title: humanizeName(subtype), fields: salientFields(m), ...updateKey ? { updateKey } : {} };
39356
39771
  }
39357
- const text12 = extractHumanReadable(m);
39358
- return { category: "unknown", name: subtype, title: `system/${subtype}`, ...text12 ? { detail: text12 } : {}, raw: rawFramePreview(m) };
39772
+ const text13 = extractHumanReadable(m);
39773
+ return { category: "unknown", name: subtype, title: `system/${subtype}`, ...text13 ? { detail: text13 } : {}, raw: rawFramePreview(m) };
39359
39774
  }
39360
39775
  function sstr(v) {
39361
39776
  return typeof v === "string" ? v : "";
@@ -39375,24 +39790,24 @@ function prettifyModel(id) {
39375
39790
  return known[id] ?? id.replace(/^claude-/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
39376
39791
  }
39377
39792
  function taskNotificationNotices(content) {
39378
- let text12 = "";
39793
+ let text13 = "";
39379
39794
  if (typeof content === "string")
39380
- text12 = content;
39795
+ text13 = content;
39381
39796
  else if (Array.isArray(content)) {
39382
39797
  for (const b of content) {
39383
39798
  if (b && typeof b === "object" && b.type === "text")
39384
- text12 += `${sstr(b.text)}
39799
+ text13 += `${sstr(b.text)}
39385
39800
  `;
39386
39801
  }
39387
39802
  }
39388
- if (!text12.includes("<task-notification>"))
39803
+ if (!text13.includes("<task-notification>"))
39389
39804
  return [];
39390
39805
  const tag = (xml3, name) => {
39391
39806
  const m = new RegExp(`<${name}>([\\s\\S]*?)</${name}>`).exec(xml3);
39392
39807
  return m ? m[1].trim() : "";
39393
39808
  };
39394
39809
  const specs = [];
39395
- for (const m of text12.matchAll(/<task-notification>([\s\S]*?)<\/task-notification>/g)) {
39810
+ for (const m of text13.matchAll(/<task-notification>([\s\S]*?)<\/task-notification>/g)) {
39396
39811
  const status2 = (tag(m[1], "status") || "completed").toLowerCase();
39397
39812
  const summary = tag(m[1], "summary") || "Background task finished.";
39398
39813
  const failed = /fail|error/.test(status2);
@@ -39406,10 +39821,10 @@ function taskNotificationNotices(content) {
39406
39821
  function buildNotice(subtype, m) {
39407
39822
  switch (subtype) {
39408
39823
  case "notification": {
39409
- const text12 = sstr(m.text) || "Notification";
39824
+ const text13 = sstr(m.text) || "Notification";
39410
39825
  const priority = ["low", "medium", "high", "immediate"].includes(sstr(m.priority)) ? m.priority : "medium";
39411
39826
  const level = priority === "immediate" ? "error" : priority === "high" ? "warning" : priority === "low" ? "info" : "notice";
39412
- return { category: "notification", level, priority, title: text12, toast: true, inline: true, ...typeof m.timeout_ms === "number" ? { timeoutMs: m.timeout_ms } : {} };
39827
+ return { category: "notification", level, priority, title: text13, toast: true, inline: true, ...typeof m.timeout_ms === "number" ? { timeoutMs: m.timeout_ms } : {} };
39413
39828
  }
39414
39829
  case "informational": {
39415
39830
  const content = sstr(m.content).trim();
@@ -39440,9 +39855,9 @@ function buildNotice(subtype, m) {
39440
39855
  return { category: "memory", level: "info", title: `Recalled ${memories.length} ${memories.length === 1 ? "memory" : "memories"}`, ...names.length ? { body: names.join(" \xB7 ") } : {}, toast: false, inline: true };
39441
39856
  }
39442
39857
  case "permission_denied": {
39443
- const tool21 = sstr(m.tool_name) || "a tool";
39858
+ const tool22 = sstr(m.tool_name) || "a tool";
39444
39859
  const reason = sstr(m.decision_reason) || sstr(m.message);
39445
- return { category: "permission", level: "warning", title: `Blocked: ${tool21}`, ...reason ? { body: reason } : {}, ...sstr(m.tool_use_id) ? { toolUseId: sstr(m.tool_use_id) } : {}, toast: true, inline: true };
39860
+ return { category: "permission", level: "warning", title: `Blocked: ${tool22}`, ...reason ? { body: reason } : {}, ...sstr(m.tool_use_id) ? { toolUseId: sstr(m.tool_use_id) } : {}, toast: true, inline: true };
39446
39861
  }
39447
39862
  case "model_refusal_fallback": {
39448
39863
  const orig = sstr(m.original_model);
@@ -39556,9 +39971,9 @@ ${plan}`;
39556
39971
  return name;
39557
39972
  }
39558
39973
  }
39559
- function computerToolPhrase(tool21, input) {
39974
+ function computerToolPhrase(tool22, input) {
39560
39975
  const s = (v) => typeof v === "string" ? v : "";
39561
- switch (tool21) {
39976
+ switch (tool22) {
39562
39977
  case "see_screen":
39563
39978
  return input.x1 !== void 0 ? "Looking closely at part of the screen" : "Looking at the screen";
39564
39979
  case "click":
@@ -39601,15 +40016,15 @@ function assistantText(content) {
39601
40016
  return content.filter((b) => !!b && typeof b === "object").filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
39602
40017
  }
39603
40018
  function flattenToolResultContent(content) {
39604
- let text12 = "";
40019
+ let text13 = "";
39605
40020
  if (typeof content === "string")
39606
- text12 = content;
40021
+ text13 = content;
39607
40022
  else if (Array.isArray(content)) {
39608
- text12 = content.filter((b) => b && typeof b === "object" && b.type === "text").map((b) => String(b.text ?? "")).join("\n");
40023
+ text13 = content.filter((b) => b && typeof b === "object" && b.type === "text").map((b) => String(b.text ?? "")).join("\n");
39609
40024
  }
39610
- if (text12.length > TOOL_RESULT_CAP)
39611
- return { text: text12.slice(0, TOOL_RESULT_CAP), truncated: true };
39612
- return { text: text12, truncated: false };
40025
+ if (text13.length > TOOL_RESULT_CAP)
40026
+ return { text: text13.slice(0, TOOL_RESULT_CAP), truncated: true };
40027
+ return { text: text13, truncated: false };
39613
40028
  }
39614
40029
  function extractImages(content) {
39615
40030
  if (!Array.isArray(content))
@@ -39791,6 +40206,7 @@ var init_agent_manager = __esm({
39791
40206
  init_screen_control_tools();
39792
40207
  init_computer_control_tools();
39793
40208
  init_app_control_tools();
40209
+ init_capabilities_tools();
39794
40210
  init_serpapi_tools();
39795
40211
  init_report_tools();
39796
40212
  init_interactive_ui_tools();
@@ -39920,7 +40336,9 @@ var init_agent_manager = __esm({
39920
40336
  "await_jobs",
39921
40337
  "job_output",
39922
40338
  "job_list",
39923
- "stop_job"
40339
+ "stop_job",
40340
+ // Returns an inlined constant. There is nothing to wait for.
40341
+ "tesseract_capabilities"
39924
40342
  ]);
39925
40343
  STOP_HARD_KILL_AFTER_MS = 1200;
39926
40344
  STREAM_STALL_MS = 20 * 6e4;
@@ -40186,8 +40604,37 @@ var init_agent_manager = __esm({
40186
40604
  * (then recorded into cronTaskSessions). */
40187
40605
  cronRequestTask = /* @__PURE__ */ new Map();
40188
40606
  /** What each conversation last ran with for Unattended — its own choice, which a scheduled fire into it must
40189
- * honour rather than the machine-wide switch (applyTurnPolicy records it; the cron fire above reads it). */
40607
+ * honour rather than the machine-wide switch (applyTurnPolicy records it; the cron fire above reads it).
40608
+ * Bounded: a daemon that runs for months must not grow a row per conversation forever, and forgetting the
40609
+ * oldest is safe — an unknown conversation falls back to 'safe' rather than to the machine switch. */
40190
40610
  unattendedByConversation = /* @__PURE__ */ new Map();
40611
+ /** How many conversations' Unattended choices are remembered. Generous — one small row each — and bounded
40612
+ * so a long-lived daemon cannot grow this without limit. */
40613
+ static MAX_REMEMBERED_UNATTENDED = 2e3;
40614
+ /**
40615
+ * Everything keyed by a conversation's id that must follow it when the CLI re-ids it mid-flight
40616
+ * (`conversation_reset`). It is the same conversation: the scheduler must keep firing into it rather than
40617
+ * opening a fresh overlapping one, and the Unattended choice made in it still stands — without this, a
40618
+ * reset quietly downgrades the next 3am fire to 'safe' in a conversation the person set up to run alone.
40619
+ */
40620
+ carryConversationTo(prev, next) {
40621
+ for (const [taskId, mapped] of this.cronTaskSessions) {
40622
+ if (mapped === prev)
40623
+ this.cronTaskSessions.set(taskId, next);
40624
+ }
40625
+ const chosen = this.unattendedByConversation.get(prev);
40626
+ if (chosen !== void 0) {
40627
+ this.unattendedByConversation.delete(prev);
40628
+ this.rememberUnattended(next, chosen);
40629
+ }
40630
+ }
40631
+ rememberUnattended(sessionId, unattended) {
40632
+ this.unattendedByConversation.delete(sessionId);
40633
+ this.unattendedByConversation.set(sessionId, unattended);
40634
+ while (this.unattendedByConversation.size > _AgentManager.MAX_REMEMBERED_UNATTENDED) {
40635
+ this.unattendedByConversation.delete(this.unattendedByConversation.keys().next().value);
40636
+ }
40637
+ }
40191
40638
  /** Set (or clear) the skills local-plugin exposed to NEW conversations (Epic H). */
40192
40639
  setSkillsPlugin(config2) {
40193
40640
  this.skillsPlugin = config2;
@@ -40383,6 +40830,12 @@ var init_agent_manager = __esm({
40383
40830
  // never sent to in this daemon's life — is left ABSENT, which for a cron turn means 'safe':
40384
40831
  // unanswerable prompts are declined rather than parked, and nothing is approved on anyone's behalf.
40385
40832
  unattended: targetSessionId ? this.unattendedByConversation.get(targetSessionId) : cfg2.isUnattended?.(),
40833
+ // THE PERSON'S SETTINGS — their block rules, their always-ask rules, their hooks. A scheduled turn
40834
+ // carried NONE of them: a session this fire creates was built without the one layer that refuses
40835
+ // things, on the run nobody is watching. Read at fire time like the switches above, so a rule
40836
+ // written at 23:00 is in force at 03:00. (A conversation already live keeps the settings its
40837
+ // query was built with; this is what a cold or new one starts from.)
40838
+ settings: cfg2.settings?.(),
40386
40839
  // Only for a conversation this fire is CREATING. Verification is per conversation and
40387
40840
  // the machine-wide switch is merely the default a new one starts with, so stamping it
40388
40841
  // onto an existing one overrode the choice the person made there — disarming a loop the
@@ -40449,7 +40902,7 @@ var init_agent_manager = __esm({
40449
40902
  this.onBrowserRequest = onBrowserRequest;
40450
40903
  const __dirname2 = dirname9(fileURLToPath(import.meta.url));
40451
40904
  this.browserCliPath = join17(__dirname2, "browser-cli.js");
40452
- this.narrator = new Narrator((sessionId, text12, engineLabel) => this.send({ type: "agent:narration", sessionId, text: text12, ...engineLabel ? { engineLabel } : {} }, this.liveSessions.get(sessionId)?.origin));
40905
+ this.narrator = new Narrator((sessionId, text13, engineLabel) => this.send({ type: "agent:narration", sessionId, text: text13, ...engineLabel ? { engineLabel } : {} }, this.liveSessions.get(sessionId)?.origin));
40453
40906
  }
40454
40907
  /** Inject (or clear) the cheap model used for the live status narration. The
40455
40908
  * host resolves creds and builds the completion fn (keeping secrets host-side). */
@@ -40619,9 +41072,9 @@ var init_agent_manager = __esm({
40619
41072
  const file2 = await resolveSessionFile(sid, live.cwd).catch(() => null);
40620
41073
  if (!file2)
40621
41074
  return false;
40622
- let text12;
41075
+ let text13;
40623
41076
  try {
40624
- text12 = await readFile8(file2, "utf8");
41077
+ text13 = await readFile8(file2, "utf8");
40625
41078
  } catch {
40626
41079
  return false;
40627
41080
  }
@@ -40629,7 +41082,7 @@ var init_agent_manager = __esm({
40629
41082
  const v = leanReloadVerdict({
40630
41083
  fillPct: usage2.percentage,
40631
41084
  thresholdPct,
40632
- leanBytes: contentBytes(text12),
41085
+ leanBytes: contentBytes(text13),
40633
41086
  strippedBytes: live.strippedContentBytes ?? 0
40634
41087
  });
40635
41088
  if (v.action === "skip") {
@@ -41584,7 +42037,7 @@ ${full.slice(-8e3)}`;
41584
42037
  applyTurnPolicy(live, p) {
41585
42038
  live.unattended = unattendedModeFor({ optedIn: p.unattended, origin: p.origin });
41586
42039
  if (p.unattended !== void 0 && live.sessionId)
41587
- this.unattendedByConversation.set(live.sessionId, p.unattended);
42040
+ this.rememberUnattended(live.sessionId, p.unattended);
41588
42041
  if (p.autoCompactPct !== void 0)
41589
42042
  live.autoCompactPct = p.autoCompactPct;
41590
42043
  live.autoDecisions = 0;
@@ -41763,10 +42216,10 @@ ${full.slice(-8e3)}`;
41763
42216
  parts.push(`The user's current request:
41764
42217
  ${live.lastPrompt.slice(0, 1400)}`);
41765
42218
  if (Array.isArray(live.lastAssistantContent)) {
41766
- const text12 = live.lastAssistantContent.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
41767
- if (text12)
42219
+ const text13 = live.lastAssistantContent.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
42220
+ if (text13)
41768
42221
  parts.push(`What the assistant just said (leading to this question):
41769
- ${text12.slice(0, 1600)}`);
42222
+ ${text13.slice(0, 1600)}`);
41770
42223
  }
41771
42224
  return parts.join("\n\n") || "No additional context available beyond the question itself.";
41772
42225
  }
@@ -42310,9 +42763,9 @@ ${lines}`;
42310
42763
  const transcripts = [];
42311
42764
  for (const sid of sessionIds) {
42312
42765
  const msgs = await this.sessionStore.getSessionMessages(sid).catch(() => []);
42313
- const text12 = transcriptToText(msgs);
42314
- if (text12)
42315
- transcripts.push(text12);
42766
+ const text13 = transcriptToText(msgs);
42767
+ if (text13)
42768
+ transcripts.push(text13);
42316
42769
  }
42317
42770
  if (transcripts.length === 0)
42318
42771
  return;
@@ -43399,6 +43852,7 @@ ${prompt("agent.system.long-running")}`;
43399
43852
  if (this.computerControlProvider) {
43400
43853
  mcp["tesseract-computer"] = createSdkMcpServer2({ name: "tesseract-computer", version: "0.1.0", tools: buildComputerControlSdkTools(this.computerControlProvider) });
43401
43854
  }
43855
+ mcp["tesseract-capabilities"] = createSdkMcpServer2({ name: "tesseract-capabilities", version: "0.1.0", tools: buildCapabilitiesTools() });
43402
43856
  if (this.appControlProvider) {
43403
43857
  const appProvider = this.appControlProvider;
43404
43858
  mcp["tesseract-app"] = createSdkMcpServer2({
@@ -43870,11 +44324,11 @@ ${prompt("agent.system.long-running")}`;
43870
44324
  });
43871
44325
  let errSeen = 0;
43872
44326
  child.stderr?.on("data", (chunk) => {
43873
- const text12 = String(chunk);
44327
+ const text13 = String(chunk);
43874
44328
  if (errSeen >= 8e3)
43875
44329
  return;
43876
- errSeen += text12.length;
43877
- for (const line of text12.split("\n")) {
44330
+ errSeen += text13.length;
44331
+ for (const line of text13.split("\n")) {
43878
44332
  const t = line.trim();
43879
44333
  if (!t)
43880
44334
  continue;
@@ -44153,9 +44607,9 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44153
44607
  emit2({ type: "chat:stream", sessionId: sid, delta: "[thinking redacted]", messageId: message.uuid, kind: "thinking", ...parentField });
44154
44608
  } else if (cbt === "advisor_tool_result") {
44155
44609
  const res = event.content_block.content;
44156
- const text12 = res?.type === "advisor_result" ? (res.text ?? "").trim() : "";
44157
- if (text12 && parentToolUseId === void 0)
44158
- emit2({ type: "agent:advisor", sessionId: sid, text: text12 });
44610
+ const text13 = res?.type === "advisor_result" ? (res.text ?? "").trim() : "";
44611
+ if (text13 && parentToolUseId === void 0)
44612
+ emit2({ type: "agent:advisor", sessionId: sid, text: text13 });
44159
44613
  }
44160
44614
  }
44161
44615
  if (event.type === "content_block_delta") {
@@ -44170,26 +44624,26 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44170
44624
  }
44171
44625
  }
44172
44626
  if (event.delta?.type === "text_delta" && event.delta.text) {
44173
- const text12 = event.delta.text;
44174
- if (/<(?:tool_call|tool_result|function_calls|antml:function_calls)[\s>]/i.test(text12)) {
44627
+ const text13 = event.delta.text;
44628
+ if (/<(?:tool_call|tool_result|function_calls|antml:function_calls)[\s>]/i.test(text13)) {
44175
44629
  insideXmlByKey.set(key2, true);
44176
44630
  break;
44177
44631
  }
44178
44632
  if (insideXmlByKey.get(key2)) {
44179
- if (/<\/(?:tool_call|tool_result|function_calls|antml:function_calls)\s*>/i.test(text12)) {
44633
+ if (/<\/(?:tool_call|tool_result|function_calls|antml:function_calls)\s*>/i.test(text13)) {
44180
44634
  insideXmlByKey.set(key2, false);
44181
44635
  }
44182
44636
  break;
44183
44637
  }
44184
- if (/<\/?(?:invoke|antml:invoke|antml:parameter)[\s>]/.test(text12))
44638
+ if (/<\/?(?:invoke|antml:invoke|antml:parameter)[\s>]/.test(text13))
44185
44639
  break;
44186
- if (/<parameter\s+name=/.test(text12))
44640
+ if (/<parameter\s+name=/.test(text13))
44187
44641
  break;
44188
- if (/^\s*<\/(?:parameter|invoke|function_calls)>\s*$/.test(text12))
44642
+ if (/^\s*<\/(?:parameter|invoke|function_calls)>\s*$/.test(text13))
44189
44643
  break;
44190
44644
  if (parentToolUseId === void 0)
44191
44645
  turnStreamed = true;
44192
- const d = this.applyDeltaHook(text12, "text", sid);
44646
+ const d = this.applyDeltaHook(text13, "text", sid);
44193
44647
  if (d !== null)
44194
44648
  emit2({ type: "chat:stream", sessionId: sid, delta: d, messageId: message.uuid, kind: "text", ...parentField });
44195
44649
  }
@@ -44257,10 +44711,8 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44257
44711
  live.sessionId = next;
44258
44712
  this.rememberSessionModel(next, live.model);
44259
44713
  this.liveSessions.set(next, live);
44260
- for (const [taskId, mapped] of this.cronTaskSessions) {
44261
- if (mapped === prev)
44262
- this.cronTaskSessions.set(taskId, next);
44263
- }
44714
+ if (prev)
44715
+ this.carryConversationTo(prev, next);
44264
44716
  this.send({ type: "session:created", sessionId: next, clientRequestId: live.clientRequestId }, live.origin);
44265
44717
  emitNotice({ category: "info", level: "notice", title: "This conversation was reset", body: "The agent started a fresh conversation; earlier messages are no longer in its context.", inline: true, toast: false });
44266
44718
  break;
@@ -44310,7 +44762,9 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44310
44762
  ...typeof uc?.output_tokens === "number" ? { outputTokens: uc.output_tokens } : {}
44311
44763
  });
44312
44764
  }
44313
- const limitRejection = live.usageLimitRejection ?? (r.subtype === "success" ? assistantUsageLimitFallback(live.lastAssistantContent) : void 0);
44765
+ const limitFromReply = r.subtype === "success" ? assistantUsageLimitFallback(live.lastAssistantContent) : void 0;
44766
+ const turnAnswered = r.subtype === "success" && r.is_error !== true && !limitFromReply;
44767
+ const limitRejection = turnAnswered ? void 0 : live.usageLimitRejection ?? limitFromReply;
44314
44768
  const resultText = typeof r.result === "string" ? r.result : "";
44315
44769
  const textOverflow = r.is_error === true && isContextOverflow(resultText) || live.lastAssistantSynthetic === true && assistantContextOverflow(live.lastAssistantContent, isContextOverflow);
44316
44770
  const apiFailure = textOverflow ? null : classifyEngineApiError(resultText);
@@ -44780,6 +45234,10 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44780
45234
  });
44781
45235
  }
44782
45236
  }
45237
+ if (live.usageLimitRejection && am.message?.model !== "<synthetic>" && providerIsServing(am.message?.content)) {
45238
+ log("Usage limit cleared by a reply that followed it:", sid);
45239
+ live.usageLimitRejection = void 0;
45240
+ }
44783
45241
  if (am.uuid && parentToolUseId === void 0) {
44784
45242
  live.lastAssistantUuid = am.uuid;
44785
45243
  live.lastAssistantContent = am.message?.content;
@@ -44824,20 +45282,20 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44824
45282
  const tn = toolNameByUseId.get(toolUseId);
44825
45283
  if (tn === "Edit" || tn === "Write" || tn === "MultiEdit" || tn === "NotebookEdit")
44826
45284
  continue;
44827
- const { text: text12, truncated } = flattenToolResultContent(blk.content);
45285
+ const { text: text13, truncated } = flattenToolResultContent(blk.content);
44828
45286
  const images = extractImages(blk.content);
44829
45287
  const isError = blk.is_error === true;
44830
- if (!text12 && images.length === 0 && !isError)
45288
+ if (!text13 && images.length === 0 && !isError)
44831
45289
  continue;
44832
- emit2({ type: "agent:tool_result", sessionId: sid, toolUseId, isError, output: text12, images, truncated, ...agentId ? { agentId } : {} });
44833
- const bg = parseBackgroundTaskResult(text12);
45290
+ emit2({ type: "agent:tool_result", sessionId: sid, toolUseId, isError, output: text13, images, truncated, ...agentId ? { agentId } : {} });
45291
+ const bg = parseBackgroundTaskResult(text13);
44834
45292
  if (bg && !agentId) {
44835
45293
  live.taskToolUse.set(bg.taskId, toolUseId);
44836
45294
  emit2({ type: "agent:task", sessionId: sid, taskId: bg.taskId, phase: "progress", status: "running", ...this.taskIdentity(live, bg.taskId), toolUseId, outputPath: bg.outputPath, isBackgrounded: true });
44837
45295
  this.startTaskTail(live, bg.taskId, bg.outputPath, toolUseId);
44838
45296
  }
44839
45297
  if (isError && !agentId) {
44840
- const why = (text12 || "").replace(/```[\s\S]*?```/g, " ").replace(/[`*]/g, "").replace(/\s+/g, " ").trim().slice(0, 160);
45298
+ const why = (text13 || "").replace(/```[\s\S]*?```/g, " ").replace(/[`*]/g, "").replace(/\s+/g, " ").trim().slice(0, 160);
44841
45299
  this.narrator.observe(sid, `\u2192 that didn't work${why ? `: ${why}` : ""}`);
44842
45300
  }
44843
45301
  } else if (blk?.type === "web_search_tool_result") {
@@ -44869,12 +45327,12 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
44869
45327
  }
44870
45328
  default: {
44871
45329
  const m = message;
44872
- const text12 = extractHumanReadable(m);
45330
+ const text13 = extractHumanReadable(m);
44873
45331
  emitEvent({
44874
45332
  category: "unknown",
44875
45333
  name: m.type || "unknown",
44876
45334
  title: m.type || "unknown frame",
44877
- ...text12 ? { detail: text12 } : {},
45335
+ ...text13 ? { detail: text13 } : {},
44878
45336
  raw: rawFramePreview(m)
44879
45337
  }, parentToolUseId);
44880
45338
  log("unknown SDK message type surfaced with raw data:", m.type || "?");
@@ -46208,11 +46666,11 @@ Element ${i + 1}:`);
46208
46666
  });
46209
46667
 
46210
46668
  // ../../packages/agent-core/dist/present-tools.js
46211
- import { tool as tool20 } from "@anthropic-ai/claude-agent-sdk";
46669
+ import { tool as tool21 } from "@anthropic-ai/claude-agent-sdk";
46212
46670
  function buildPresentSdkTools(p) {
46213
- return PRESENT_TOOL_SPECS.map((spec) => tool20(spec.name, spec.description, zodShapeFromParams(spec.params), async (args) => {
46214
- const text12 = await p.exec(spec.name, args ?? {});
46215
- return { content: [{ type: "text", text: text12 || "Done." }] };
46671
+ return PRESENT_TOOL_SPECS.map((spec) => tool21(spec.name, spec.description, zodShapeFromParams(spec.params), async (args) => {
46672
+ const text13 = await p.exec(spec.name, args ?? {});
46673
+ return { content: [{ type: "text", text: text13 || "Done." }] };
46216
46674
  }));
46217
46675
  }
46218
46676
  function buildPresentPrompt(opts) {
@@ -46325,6 +46783,7 @@ __export(dist_exports2, {
46325
46783
  APP_CONTROL_DOMAINS: () => APP_CONTROL_DOMAINS,
46326
46784
  APP_CONTROL_GENERATED_MARKER: () => APP_CONTROL_GENERATED_MARKER,
46327
46785
  AgentManager: () => AgentManager,
46786
+ CAPABILITIES_DOC: () => CAPABILITIES_DOC,
46328
46787
  DEFAULT_OFFLOAD_RECENT_CALLS: () => DEFAULT_OFFLOAD_RECENT_CALLS,
46329
46788
  DESIGN_CLAUDE_MD: () => DESIGN_CLAUDE_MD,
46330
46789
  DESIGN_DIR: () => DESIGN_DIR,
@@ -46357,6 +46816,7 @@ __export(dist_exports2, {
46357
46816
  archivePathFor: () => archivePathFor,
46358
46817
  autoCompactEnvFor: () => autoCompactEnvFor,
46359
46818
  buildAppControlTools: () => buildAppControlTools,
46819
+ buildCapabilitiesTools: () => buildCapabilitiesTools,
46360
46820
  buildClaudeNarrationLlm: () => buildClaudeNarrationLlm,
46361
46821
  buildClaudeSmartLlm: () => buildClaudeSmartLlm,
46362
46822
  buildClaudeVisionLlm: () => buildClaudeVisionLlm,
@@ -46374,6 +46834,7 @@ __export(dist_exports2, {
46374
46834
  bumpVersion: () => bumpVersion,
46375
46835
  capabilitiesByDomain: () => capabilitiesByDomain,
46376
46836
  capabilityById: () => capabilityById,
46837
+ capabilitySections: () => capabilitySections,
46377
46838
  computerControlSpecs: () => computerControlSpecs,
46378
46839
  computerOperatorPrompt: () => computerOperatorPrompt,
46379
46840
  computerSpecParametersJsonSchema: () => computerSpecParametersJsonSchema,
@@ -46385,6 +46846,7 @@ __export(dist_exports2, {
46385
46846
  engineMessageTokens: () => engineMessageTokens,
46386
46847
  exportSession: () => exportSession,
46387
46848
  filterHistory: () => filterHistory,
46849
+ findSections: () => findSections,
46388
46850
  flattenForDisplay: () => flattenForDisplay,
46389
46851
  getModelCatalog: () => getModelCatalog,
46390
46852
  getTokenPath: () => getTokenPath,
@@ -46478,6 +46940,8 @@ var init_dist5 = __esm({
46478
46940
  init_present_tools();
46479
46941
  init_computer_control_tools();
46480
46942
  init_support_tools();
46943
+ init_capabilities_tools();
46944
+ init_capabilities_doc_generated();
46481
46945
  init_app_control_tools();
46482
46946
  init_registry();
46483
46947
  init_docs2();
@@ -47590,9 +48054,9 @@ function rememberedPath(engineId) {
47590
48054
  }
47591
48055
  }
47592
48056
  function which(bin, path6) {
47593
- const tool21 = process.platform === "win32" ? "where" : "which";
48057
+ const tool22 = process.platform === "win32" ? "where" : "which";
47594
48058
  try {
47595
- const out = execFileSync2(tool21, [bin], { env: { ...process.env, PATH: path6 }, timeout: 4e3, encoding: "utf-8" });
48059
+ const out = execFileSync2(tool22, [bin], { env: { ...process.env, PATH: path6 }, timeout: 4e3, encoding: "utf-8" });
47596
48060
  return out.split(/\r?\n/).map((l) => l.trim()).find(Boolean) || void 0;
47597
48061
  } catch {
47598
48062
  return void 0;
@@ -47864,9 +48328,9 @@ function readBody2(req, limitBytes = 4 * 1024 * 1024) {
47864
48328
  });
47865
48329
  }
47866
48330
  function send2(res, code, body, headers = {}) {
47867
- const text12 = JSON.stringify(body);
47868
- res.writeHead(code, { "content-type": "application/json", "content-length": Buffer.byteLength(text12), ...headers });
47869
- res.end(text12);
48331
+ const text13 = JSON.stringify(body);
48332
+ res.writeHead(code, { "content-type": "application/json", "content-length": Buffer.byteLength(text13), ...headers });
48333
+ res.end(text13);
47870
48334
  }
47871
48335
  function portFile() {
47872
48336
  return join28(daemonDir2(), "mcp-endpoint.json");
@@ -47939,13 +48403,13 @@ async function startTesseractMcpEndpoint() {
47939
48403
  return;
47940
48404
  case "tools/call": {
47941
48405
  const name = String(msg.params?.name ?? "");
47942
- const tool21 = byName.get(name);
47943
- if (!tool21) {
48406
+ const tool22 = byName.get(name);
48407
+ if (!tool22) {
47944
48408
  fail4(-32602, `Unknown tool "${name}"`);
47945
48409
  return;
47946
48410
  }
47947
48411
  try {
47948
- const out = await tool21.handler(msg.params?.arguments ?? {}, { sessionId: grant?.sessionId, nested: grant?.extensionId === NESTED_HOLDER });
48412
+ const out = await tool22.handler(msg.params?.arguments ?? {}, { sessionId: grant?.sessionId, nested: grant?.extensionId === NESTED_HOLDER });
47949
48413
  reply3({ content: out.content, ...out.isError ? { isError: true } : {} });
47950
48414
  } catch (e) {
47951
48415
  reply3({ content: [{ type: "text", text: `${name} failed: ${e instanceof Error ? e.message : String(e)}` }], isError: true });
@@ -48099,11 +48563,11 @@ function wrapHostedAsyncTool(t, opts) {
48099
48563
  }
48100
48564
  if (raced.kind === "done") {
48101
48565
  callEnded();
48102
- return text11(raced.out);
48566
+ return text12(raced.out);
48103
48567
  }
48104
48568
  if (raced.kind === "error") {
48105
48569
  callEnded();
48106
- return text11(raced.err instanceof Error ? raced.err.message : String(raced.err), true);
48570
+ return text12(raced.err instanceof Error ? raced.err.message : String(raced.err), true);
48107
48571
  }
48108
48572
  opts.learnedSlow?.add(t.name);
48109
48573
  const sessionId = ctx2?.sessionId;
@@ -48116,7 +48580,7 @@ function wrapHostedAsyncTool(t, opts) {
48116
48580
  });
48117
48581
  opts.noteSession?.(job.id, sessionId, claim);
48118
48582
  callEnded();
48119
- return text11([
48583
+ return text12([
48120
48584
  `Started in the background: job \`${job.id}\`.`,
48121
48585
  `Live output: ${job.logPath}`,
48122
48586
  willNotify ? `You will be notified when it finishes \u2014 do not poll. Continue with other work, or call await_jobs(["${job.id}"]) if you cannot proceed without the result.` : `Call await_jobs(["${job.id}"]) when you need the result, or job_output("${job.id}") to read what it has produced so far.`
@@ -48153,12 +48617,12 @@ function buildHostedJobTools(registry3, opts = {}) {
48153
48617
  handler: async (args, ctx2) => {
48154
48618
  const ids = Array.isArray(args["job_ids"]) ? args["job_ids"].map(String) : [];
48155
48619
  if (ids.length === 0)
48156
- return text11("await_jobs needs at least one job id.", true);
48620
+ return text12("await_jobs needs at least one job id.", true);
48157
48621
  const unknown2 = ids.filter((id) => !registry3.list().some((j) => j.id === id) || !mine(id, ctx2?.sessionId));
48158
48622
  if (unknown2.length)
48159
- return text11(`no such job: ${unknown2.join(", ")}`, true);
48623
+ return text12(`no such job: ${unknown2.join(", ")}`, true);
48160
48624
  const jobs = await registry3.await(ids, typeof args["timeout_ms"] === "number" ? args["timeout_ms"] : 12e4);
48161
- return text11(jobs.map((j) => `${fmt(j)}
48625
+ return text12(jobs.map((j) => `${fmt(j)}
48162
48626
  ${registry3.output(j.id) || "(no output)"}`).join("\n\n"));
48163
48627
  }
48164
48628
  },
@@ -48179,10 +48643,10 @@ ${registry3.output(j.id) || "(no output)"}`).join("\n\n"));
48179
48643
  const id = String(args["job_id"] ?? "");
48180
48644
  const job = registry3.list().find((j) => j.id === id);
48181
48645
  if (!job || !mine(id, ctx2?.sessionId))
48182
- return text11(`no such job: ${id}`, true);
48646
+ return text12(`no such job: ${id}`, true);
48183
48647
  const from = typeof args["from_offset"] === "number" ? args["from_offset"] : 0;
48184
48648
  const out = registry3.output(id, from);
48185
- return text11(`${fmt(job)}
48649
+ return text12(`${fmt(job)}
48186
48650
  ${out || "(nothing yet)"}`);
48187
48651
  }
48188
48652
  },
@@ -48193,7 +48657,7 @@ ${out || "(nothing yet)"}`);
48193
48657
  annotations: { readOnlyHint: true },
48194
48658
  handler: async (_args, ctx2) => {
48195
48659
  const jobs = registry3.list().filter((j) => mine(j.id, ctx2?.sessionId)).sort((a, b) => Number(b.status === "running") - Number(a.status === "running"));
48196
- return text11(jobs.length ? jobs.map(fmt).join("\n\n") : "no jobs.");
48660
+ return text12(jobs.length ? jobs.map(fmt).join("\n\n") : "no jobs.");
48197
48661
  }
48198
48662
  },
48199
48663
  {
@@ -48203,8 +48667,8 @@ ${out || "(nothing yet)"}`);
48203
48667
  handler: async (args, ctx2) => {
48204
48668
  const id = String(args["job_id"] ?? "");
48205
48669
  if (!mine(id, ctx2?.sessionId))
48206
- return text11(`job ${id} was not running.`);
48207
- return text11(registry3.stop(id) ? `stopped job ${id}.` : `job ${id} was not running.`);
48670
+ return text12(`job ${id} was not running.`);
48671
+ return text12(registry3.stop(id) ? `stopped job ${id}.` : `job ${id} was not running.`);
48208
48672
  }
48209
48673
  }
48210
48674
  ];
@@ -48435,12 +48899,12 @@ function createNoticeDispatcher(opts) {
48435
48899
  }
48436
48900
  };
48437
48901
  }
48438
- var text11, NEVER_ASYNC;
48902
+ var text12, NEVER_ASYNC;
48439
48903
  var init_hosted_jobs = __esm({
48440
48904
  "../../packages/host-core/dist/engines/hosted-jobs.js"() {
48441
48905
  "use strict";
48442
48906
  init_jobs();
48443
- text11 = (t, isError = false) => ({ content: [{ type: "text", text: t }], ...isError ? { isError: true } : {} });
48907
+ text12 = (t, isError = false) => ({ content: [{ type: "text", text: t }], ...isError ? { isError: true } : {} });
48444
48908
  NEVER_ASYNC = /* @__PURE__ */ new Set(["await_jobs", "job_output", "job_list", "stop_job"]);
48445
48909
  }
48446
48910
  });
@@ -48719,7 +49183,7 @@ async function startHostedToolset(deps) {
48719
49183
  return { endpoint: endpoint2, sendBrowserResponse, tasks, ...stopOwnedJob ? { stopJob: stopOwnedJob } : {}, ...rekeyOwnedJobs ? { rekeyJobs: rekeyOwnedJobs } : {}, ...jobProbe ? { jobProbe } : {}, refresh: build };
48720
49184
  }
48721
49185
  function buildHostTaskTools(registry3, run9) {
48722
- const text12 = (t, isError = false) => ({ content: [{ type: "text", text: t }], ...isError ? { isError: true } : {} });
49186
+ const text13 = (t, isError = false) => ({ content: [{ type: "text", text: t }], ...isError ? { isError: true } : {} });
48723
49187
  return [
48724
49188
  {
48725
49189
  name: "tesseract_spawn_subagent",
@@ -48736,25 +49200,25 @@ function buildHostTaskTools(registry3, run9) {
48736
49200
  const description = String(args.description ?? "sub-task");
48737
49201
  const prompt2 = String(args.prompt ?? "");
48738
49202
  if (!prompt2.trim())
48739
- return text12("prompt is required", true);
49203
+ return text13("prompt is required", true);
48740
49204
  if (ctx2?.nested) {
48741
- return text12("You are already a subagent: delegating again is not allowed. Do this work yourself, or report back to the agent that spawned you.", true);
49205
+ return text13("You are already a subagent: delegating again is not allowed. Do this work yourself, or report back to the agent that spawned you.", true);
48742
49206
  }
48743
49207
  const task = registry3.start("subagent", description);
48744
49208
  task.messages.push({ role: "user", content: prompt2 });
48745
49209
  try {
48746
49210
  const out = await run9(prompt2, { signal: task.abort.signal, sessionId: ctx2?.sessionId });
48747
49211
  if (task.status === "stopped")
48748
- return text12(`Subagent "${description}" was stopped.`, true);
49212
+ return text13(`Subagent "${description}" was stopped.`, true);
48749
49213
  task.status = "done";
48750
49214
  task.result = out;
48751
49215
  task.messages.push({ role: "assistant", content: out });
48752
- return text12(out || "(the subagent returned nothing)");
49216
+ return text13(out || "(the subagent returned nothing)");
48753
49217
  } catch (e) {
48754
49218
  task.status = task.status === "stopped" ? "stopped" : "failed";
48755
49219
  const why = e instanceof Error ? e.message : String(e);
48756
49220
  task.result = why;
48757
- return text12(`Subagent "${description}" failed: ${why}`, true);
49221
+ return text13(`Subagent "${description}" failed: ${why}`, true);
48758
49222
  }
48759
49223
  }
48760
49224
  },
@@ -48765,8 +49229,8 @@ function buildHostTaskTools(registry3, run9) {
48765
49229
  handler: async () => {
48766
49230
  const rows = registry3.list();
48767
49231
  if (!rows.length)
48768
- return text12("No sub-tasks have been started.");
48769
- return text12(rows.map((t) => `${t.id} ${t.status.padEnd(7)} ${t.description}`).join("\n"));
49232
+ return text13("No sub-tasks have been started.");
49233
+ return text13(rows.map((t) => `${t.id} ${t.status.padEnd(7)} ${t.description}`).join("\n"));
48770
49234
  }
48771
49235
  },
48772
49236
  {
@@ -48779,7 +49243,7 @@ function buildHostTaskTools(registry3, run9) {
48779
49243
  },
48780
49244
  handler: async (args) => {
48781
49245
  const id = String(args.taskId ?? "");
48782
- return registry3.stop(id) ? text12(`Stopped ${id}.`) : text12(`${id} is not a running task (unknown, or already finished).`, true);
49246
+ return registry3.stop(id) ? text13(`Stopped ${id}.`) : text13(`${id} is not a running task (unknown, or already finished).`, true);
48783
49247
  }
48784
49248
  }
48785
49249
  ];
@@ -54120,24 +54584,24 @@ var require_text2 = __commonJS({
54120
54584
  }
54121
54585
  }
54122
54586
  var latin1Regex = /^[\u0000-\u00FF]*$/;
54123
- function validateLatin1(text12) {
54124
- if (!latin1Regex.test(text12)) {
54587
+ function validateLatin1(text13) {
54588
+ if (!latin1Regex.test(text13)) {
54125
54589
  throw new Error("invalid latin1 text");
54126
54590
  }
54127
54591
  }
54128
- function decodetEXt(text12, buffer, length) {
54592
+ function decodetEXt(text13, buffer, length) {
54129
54593
  const keyword = readKeyword(buffer);
54130
- text12[keyword] = readLatin1(buffer, length - keyword.length - 1);
54594
+ text13[keyword] = readLatin1(buffer, length - keyword.length - 1);
54131
54595
  }
54132
- function encodetEXt(buffer, keyword, text12) {
54596
+ function encodetEXt(buffer, keyword, text13) {
54133
54597
  validateKeyword(keyword);
54134
- validateLatin1(text12);
54135
- const length = keyword.length + 1 + text12.length;
54598
+ validateLatin1(text13);
54599
+ const length = keyword.length + 1 + text13.length;
54136
54600
  buffer.writeUint32(length);
54137
54601
  buffer.writeChars(exports.textChunkName);
54138
54602
  buffer.writeChars(keyword);
54139
54603
  buffer.writeByte(NULL);
54140
- buffer.writeChars(text12);
54604
+ buffer.writeChars(text13);
54141
54605
  (0, crc_1.writeCrc)(buffer, length + 4);
54142
54606
  }
54143
54607
  function readKeyword(buffer) {
@@ -54721,8 +55185,8 @@ var require_PngEncoder = __commonJS({
54721
55185
  }
54722
55186
  this.encodeData();
54723
55187
  if (this._png.text) {
54724
- for (const [keyword, text12] of Object.entries(this._png.text)) {
54725
- (0, text_1.encodetEXt)(this, keyword, text12);
55188
+ for (const [keyword, text13] of Object.entries(this._png.text)) {
55189
+ (0, text_1.encodetEXt)(this, keyword, text13);
54726
55190
  }
54727
55191
  }
54728
55192
  this.encodeIEND();
@@ -58908,8 +59372,8 @@ var require_build = __commonJS({
58908
59372
  var BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
58909
59373
  var BASE64URL_REGEX = /^[a-zA-Z0-9-_]+$/;
58910
59374
  var Utf8Converter = class {
58911
- static fromString(text12) {
58912
- const s = unescape(encodeURIComponent(text12));
59375
+ static fromString(text13) {
59376
+ const s = unescape(encodeURIComponent(text13));
58913
59377
  const uintArray = new Uint8Array(s.length);
58914
59378
  for (let i = 0; i < s.length; i++) {
58915
59379
  uintArray[i] = s.charCodeAt(i);
@@ -58937,11 +59401,11 @@ var require_build = __commonJS({
58937
59401
  }
58938
59402
  return res;
58939
59403
  }
58940
- static fromString(text12, littleEndian = false) {
58941
- const res = new ArrayBuffer(text12.length * 2);
59404
+ static fromString(text13, littleEndian = false) {
59405
+ const res = new ArrayBuffer(text13.length * 2);
58942
59406
  const dataView = new DataView(res);
58943
- for (let i = 0; i < text12.length; i++) {
58944
- dataView.setUint16(i * 2, text12.charCodeAt(i), littleEndian);
59407
+ for (let i = 0; i < text13.length; i++) {
59408
+ dataView.setUint16(i * 2, text13.charCodeAt(i), littleEndian);
58945
59409
  }
58946
59410
  return res;
58947
59411
  }
@@ -59038,18 +59502,18 @@ var require_build = __commonJS({
59038
59502
  static ToBase64Url(data) {
59039
59503
  return this.ToBase64(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/\=/g, "");
59040
59504
  }
59041
- static FromUtf8String(text12, encoding = _Convert.DEFAULT_UTF8_ENCODING) {
59505
+ static FromUtf8String(text13, encoding = _Convert.DEFAULT_UTF8_ENCODING) {
59042
59506
  switch (encoding) {
59043
59507
  case "ascii":
59044
- return this.FromBinary(text12);
59508
+ return this.FromBinary(text13);
59045
59509
  case "utf8":
59046
- return Utf8Converter.fromString(text12);
59510
+ return Utf8Converter.fromString(text13);
59047
59511
  case "utf16":
59048
59512
  case "utf16be":
59049
- return Utf16Converter.fromString(text12);
59513
+ return Utf16Converter.fromString(text13);
59050
59514
  case "utf16le":
59051
59515
  case "usc2":
59052
- return Utf16Converter.fromString(text12, true);
59516
+ return Utf16Converter.fromString(text13, true);
59053
59517
  default:
59054
59518
  throw new Error(`Unknown type of encoding '${encoding}'`);
59055
59519
  }
@@ -59070,11 +59534,11 @@ var require_build = __commonJS({
59070
59534
  throw new Error(`Unknown type of encoding '${encoding}'`);
59071
59535
  }
59072
59536
  }
59073
- static FromBinary(text12) {
59074
- const stringLength = text12.length;
59537
+ static FromBinary(text13) {
59538
+ const stringLength = text13.length;
59075
59539
  const resultView = new Uint8Array(stringLength);
59076
59540
  for (let i = 0; i < stringLength; i++) {
59077
- resultView[i] = text12.charCodeAt(i);
59541
+ resultView[i] = text13.charCodeAt(i);
59078
59542
  }
59079
59543
  return resultView.buffer;
59080
59544
  }
@@ -59120,8 +59584,8 @@ var require_build = __commonJS({
59120
59584
  static ToUtf16String(buffer, littleEndian = false) {
59121
59585
  return Utf16Converter.toString(buffer, littleEndian);
59122
59586
  }
59123
- static FromUtf16String(text12, littleEndian = false) {
59124
- return Utf16Converter.fromString(text12, littleEndian);
59587
+ static FromUtf16String(text13, littleEndian = false) {
59588
+ return Utf16Converter.fromString(text13, littleEndian);
59125
59589
  }
59126
59590
  static Base64Padding(base643) {
59127
59591
  const padCount = 4 - base643.length % 4;
@@ -62882,15 +63346,15 @@ var require_sequence = __commonJS({
62882
63346
  }
62883
63347
  return normalized;
62884
63348
  }
62885
- function encodeAscii(text12) {
62886
- const bytes3 = new Uint8Array(text12.length);
62887
- for (let i = 0; i < text12.length; i++) {
62888
- bytes3[i] = text12.charCodeAt(i) & 255;
63349
+ function encodeAscii(text13) {
63350
+ const bytes3 = new Uint8Array(text13.length);
63351
+ for (let i = 0; i < text13.length; i++) {
63352
+ bytes3[i] = text13.charCodeAt(i) & 255;
62889
63353
  }
62890
63354
  return bytes3;
62891
63355
  }
62892
- function encodeUtf8(text12) {
62893
- return new TextEncoder().encode(text12);
63356
+ function encodeUtf8(text13) {
63357
+ return new TextEncoder().encode(text13);
62894
63358
  }
62895
63359
  function toPatternBytes(pattern, options) {
62896
63360
  if (typeof pattern === "string") {
@@ -64265,15 +64729,15 @@ var require_binary = __commonJS({
64265
64729
  }
64266
64730
  return result;
64267
64731
  }
64268
- function decode3(text12) {
64269
- const result = new Uint8Array(text12.length);
64270
- for (let i = 0; i < text12.length; i++) {
64271
- result[i] = text12.charCodeAt(i) & 255;
64732
+ function decode3(text13) {
64733
+ const result = new Uint8Array(text13.length);
64734
+ for (let i = 0; i < text13.length; i++) {
64735
+ result[i] = text13.charCodeAt(i) & 255;
64272
64736
  }
64273
64737
  return result;
64274
64738
  }
64275
- function is(text12) {
64276
- return typeof text12 === "string";
64739
+ function is(text13) {
64740
+ return typeof text13 === "string";
64277
64741
  }
64278
64742
  exports.binary = { encode: encode3, decode: decode3, is };
64279
64743
  }
@@ -64308,31 +64772,31 @@ var require_hex = __commonJS({
64308
64772
  throw new TypeError("Hex separators must be non-empty strings");
64309
64773
  }
64310
64774
  }
64311
- function matchSeparator(text12, index, separators) {
64775
+ function matchSeparator(text13, index, separators) {
64312
64776
  for (const separator of separators) {
64313
- if (text12.startsWith(separator, index)) {
64777
+ if (text13.startsWith(separator, index)) {
64314
64778
  return separator;
64315
64779
  }
64316
64780
  }
64317
64781
  return void 0;
64318
64782
  }
64319
- function detectCase(text12) {
64320
- const hasUpper = /[A-F]/.test(text12);
64321
- const hasLower = /[a-f]/.test(text12);
64783
+ function detectCase(text13) {
64784
+ const hasUpper = /[A-F]/.test(text13);
64785
+ const hasLower = /[a-f]/.test(text13);
64322
64786
  return hasUpper && !hasLower ? "upper" : "lower";
64323
64787
  }
64324
- function detectLineSeparator(text12) {
64325
- const match = /\r\n|\n/.exec(text12);
64788
+ function detectLineSeparator(text13) {
64789
+ const match = /\r\n|\n/.exec(text13);
64326
64790
  if (!match) {
64327
64791
  return void 0;
64328
64792
  }
64329
64793
  return match[0] === "\r\n" ? "\r\n" : "\n";
64330
64794
  }
64331
- function compactForDetection(text12) {
64332
- return text12.replace(/[^0-9a-f]/gi, "");
64795
+ function compactForDetection(text13) {
64796
+ return text13.replace(/[^0-9a-f]/gi, "");
64333
64797
  }
64334
- function detectGroup(text12) {
64335
- const segments = text12.match(/[0-9A-Fa-f]+|[^0-9A-Fa-f]+/g) ?? [];
64798
+ function detectGroup(text13) {
64799
+ const segments = text13.match(/[0-9A-Fa-f]+|[^0-9A-Fa-f]+/g) ?? [];
64336
64800
  if (segments.length < 3) {
64337
64801
  return void 0;
64338
64802
  }
@@ -64360,8 +64824,8 @@ var require_hex = __commonJS({
64360
64824
  separator
64361
64825
  };
64362
64826
  }
64363
- function detectFormat(text12) {
64364
- const trimmed = text12.trim();
64827
+ function detectFormat(text13) {
64828
+ const trimmed = text13.trim();
64365
64829
  const prefix = /^0x/i.test(trimmed) ? "0x" : "";
64366
64830
  const body = prefix ? trimmed.slice(2) : trimmed;
64367
64831
  const lineSeparator = detectLineSeparator(body);
@@ -64386,13 +64850,13 @@ var require_hex = __commonJS({
64386
64850
  }
64387
64851
  return format2;
64388
64852
  }
64389
- function normalizeText(text12, options) {
64853
+ function normalizeText(text13, options) {
64390
64854
  const allowPrefix = options.allowPrefix ?? true;
64391
64855
  const separators = [...resolveSeparators(options)].sort((left, right) => right.length - left.length);
64392
64856
  for (const separator of separators) {
64393
64857
  validateSeparator(separator);
64394
64858
  }
64395
- let working = text12.trim();
64859
+ let working = text13.trim();
64396
64860
  if (/^0x/i.test(working)) {
64397
64861
  if (!allowPrefix) {
64398
64862
  throw new TypeError("Hexadecimal text must not include a 0x prefix");
@@ -64443,15 +64907,15 @@ var require_hex = __commonJS({
64443
64907
  }
64444
64908
  return chunks.join(group.separator);
64445
64909
  }
64446
- function normalize3(text12, options = {}) {
64447
- return normalizeText(text12, options);
64910
+ function normalize3(text13, options = {}) {
64911
+ return normalizeText(text13, options);
64448
64912
  }
64449
- function is(text12, options = {}) {
64450
- if (typeof text12 !== "string") {
64913
+ function is(text13, options = {}) {
64914
+ if (typeof text13 !== "string") {
64451
64915
  return false;
64452
64916
  }
64453
64917
  try {
64454
- normalize3(text12, options);
64918
+ normalize3(text13, options);
64455
64919
  return true;
64456
64920
  } catch {
64457
64921
  return false;
@@ -64461,8 +64925,8 @@ var require_hex = __commonJS({
64461
64925
  const bytes3 = (0, index_js_1.toUint8Array)(data);
64462
64926
  const casing = options.case ?? "lower";
64463
64927
  const pairs = Array.from(bytes3, (byte) => {
64464
- const text12 = byte.toString(16).padStart(2, "0");
64465
- return casing === "upper" ? text12.toUpperCase() : text12;
64928
+ const text13 = byte.toString(16).padStart(2, "0");
64929
+ return casing === "upper" ? text13.toUpperCase() : text13;
64466
64930
  });
64467
64931
  let body = "";
64468
64932
  if (options.line) {
@@ -64481,19 +64945,19 @@ var require_hex = __commonJS({
64481
64945
  }
64482
64946
  return `${options.prefix ?? ""}${body}`;
64483
64947
  }
64484
- function decode3(text12, options = {}) {
64485
- const normalized = normalize3(text12, options);
64948
+ function decode3(text13, options = {}) {
64949
+ const normalized = normalize3(text13, options);
64486
64950
  const result = new Uint8Array(normalized.length / 2);
64487
64951
  for (let i = 0; i < normalized.length; i += 2) {
64488
64952
  result[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16);
64489
64953
  }
64490
64954
  return result;
64491
64955
  }
64492
- function parse4(text12, options = {}) {
64493
- const normalized = normalize3(text12, options);
64956
+ function parse4(text13, options = {}) {
64957
+ const normalized = normalize3(text13, options);
64494
64958
  return {
64495
64959
  bytes: decode3(normalized),
64496
- format: detectFormat(text12),
64960
+ format: detectFormat(text13),
64497
64961
  normalized
64498
64962
  };
64499
64963
  }
@@ -64521,8 +64985,8 @@ var require_utf8 = __commonJS({
64521
64985
  exports.encode = encode3;
64522
64986
  exports.decode = decode3;
64523
64987
  var index_js_1 = require_bytes();
64524
- function encode3(text12) {
64525
- return new TextEncoder().encode(text12);
64988
+ function encode3(text13) {
64989
+ return new TextEncoder().encode(text13);
64526
64990
  }
64527
64991
  function decode3(data) {
64528
64992
  return new TextDecoder("utf-8", { fatal: false }).decode((0, index_js_1.toUint8Array)(data));
@@ -64540,11 +65004,11 @@ var require_utf16 = __commonJS({
64540
65004
  exports.encode = encode3;
64541
65005
  exports.decode = decode3;
64542
65006
  var index_js_1 = require_bytes();
64543
- function encode3(text12, options = {}) {
64544
- const result = new ArrayBuffer(text12.length * 2);
65007
+ function encode3(text13, options = {}) {
65008
+ const result = new ArrayBuffer(text13.length * 2);
64545
65009
  const view = new DataView(result);
64546
- for (let i = 0; i < text12.length; i++) {
64547
- view.setUint16(i * 2, text12.charCodeAt(i), options.littleEndian ?? false);
65010
+ for (let i = 0; i < text13.length; i++) {
65011
+ view.setUint16(i * 2, text13.charCodeAt(i), options.littleEndian ?? false);
64548
65012
  }
64549
65013
  return new Uint8Array(result);
64550
65014
  }
@@ -64578,18 +65042,18 @@ var require_base64 = __commonJS({
64578
65042
  function nodeBuffer() {
64579
65043
  return globalThis.Buffer;
64580
65044
  }
64581
- function normalize3(text12) {
64582
- return text12.replace(/[\n\r\t ]/g, "");
65045
+ function normalize3(text13) {
65046
+ return text13.replace(/[\n\r\t ]/g, "");
64583
65047
  }
64584
- function pad2(text12) {
64585
- const remainder = text12.length % 4;
64586
- return remainder ? text12 + "=".repeat(4 - remainder) : text12;
65048
+ function pad2(text13) {
65049
+ const remainder = text13.length % 4;
65050
+ return remainder ? text13 + "=".repeat(4 - remainder) : text13;
64587
65051
  }
64588
- function is(text12) {
64589
- if (typeof text12 !== "string") {
65052
+ function is(text13) {
65053
+ if (typeof text13 !== "string") {
64590
65054
  return false;
64591
65055
  }
64592
- const normalized = normalize3(text12);
65056
+ const normalized = normalize3(text13);
64593
65057
  return normalized === "" || BASE64_REGEX.test(normalized);
64594
65058
  }
64595
65059
  function encode3(data, _options) {
@@ -64600,8 +65064,8 @@ var require_base64 = __commonJS({
64600
65064
  }
64601
65065
  return btoa((0, binary_js_1.encode)(bytes3));
64602
65066
  }
64603
- function decode3(text12, _options) {
64604
- const normalized = normalize3(text12);
65067
+ function decode3(text13, _options) {
65068
+ const normalized = normalize3(text13);
64605
65069
  if (!is(normalized)) {
64606
65070
  throw new TypeError("Input is not valid Base64 text");
64607
65071
  }
@@ -64627,17 +65091,17 @@ var require_base64url = __commonJS({
64627
65091
  exports.decode = decode3;
64628
65092
  var base64_js_1 = require_base64();
64629
65093
  var BASE64URL_REGEX = /^[A-Za-z0-9_-]*$/;
64630
- function normalize3(text12) {
64631
- return text12.replace(/[\n\r\t ]/g, "");
65094
+ function normalize3(text13) {
65095
+ return text13.replace(/[\n\r\t ]/g, "");
64632
65096
  }
64633
- function is(text12) {
64634
- return typeof text12 === "string" && BASE64URL_REGEX.test(normalize3(text12));
65097
+ function is(text13) {
65098
+ return typeof text13 === "string" && BASE64URL_REGEX.test(normalize3(text13));
64635
65099
  }
64636
65100
  function encode3(data, _options) {
64637
65101
  return base64_js_1.base64.encode(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
64638
65102
  }
64639
- function decode3(text12, _options) {
64640
- const normalized = normalize3(text12);
65103
+ function decode3(text13, _options) {
65104
+ const normalized = normalize3(text13);
64641
65105
  if (!is(normalized)) {
64642
65106
  throw new TypeError("Input is not valid Base64Url text");
64643
65107
  }
@@ -64758,8 +65222,8 @@ var require_ip_converter = __commonJS({
64758
65222
  }
64759
65223
  return ip;
64760
65224
  }
64761
- static parseCIDR(text12) {
64762
- const [addr, prefixStr] = text12.split("/");
65225
+ static parseCIDR(text13) {
65226
+ const [addr, prefixStr] = text13.split("/");
64763
65227
  const prefix = parseInt(prefixStr, 10);
64764
65228
  if (this.isIPv4(addr)) {
64765
65229
  if (prefix < 0 || prefix > 32) {
@@ -64812,9 +65276,9 @@ var require_ip_converter = __commonJS({
64812
65276
  }
64813
65277
  return this.decodeIP(encoding_1.hex.encode(buf));
64814
65278
  }
64815
- static fromString(text12) {
64816
- if (text12.includes("/")) {
64817
- const [addr, prefix] = this.parseCIDR(text12);
65279
+ static fromString(text13) {
65280
+ if (text13.includes("/")) {
65281
+ const [addr, prefix] = this.parseCIDR(text13);
64818
65282
  const maskBytes = new Uint8Array(addr.length);
64819
65283
  let bitsLeft = prefix;
64820
65284
  for (let i = 0; i < maskBytes.length; i++) {
@@ -64831,7 +65295,7 @@ var require_ip_converter = __commonJS({
64831
65295
  out.set(maskBytes, addr.length);
64832
65296
  return out.buffer;
64833
65297
  }
64834
- const bytes3 = this.isIPv4(text12) ? this.parseIPv4(text12) : this.parseIPv6(text12);
65298
+ const bytes3 = this.isIPv4(text13) ? this.parseIPv4(text13) : this.parseIPv6(text13);
64835
65299
  return new Uint8Array(bytes3).buffer;
64836
65300
  }
64837
65301
  };
@@ -72511,24 +72975,24 @@ var require_x509_cjs = __commonJS({
72511
72975
  names.register("I", "2.5.4.43");
72512
72976
  names.register("SN", "2.5.4.4");
72513
72977
  names.register("T", "2.5.4.12");
72514
- function replaceUnknownCharacter(text12, char) {
72978
+ function replaceUnknownCharacter(text13, char) {
72515
72979
  return `\\${pvtsutils.Convert.ToHex(pvtsutils.Convert.FromUtf8String(char)).toUpperCase()}`;
72516
72980
  }
72517
72981
  function escape2(data) {
72518
72982
  return data.replace(/([,+"\\<>;])/g, "\\$1").replace(/^([ #])/, "\\$1").replace(/([ ]$)/, "\\$1").replace(/([\r\n\t])/, replaceUnknownCharacter);
72519
72983
  }
72520
72984
  var Name = class _Name {
72521
- static isASCII(text12) {
72522
- for (let i = 0; i < text12.length; i++) {
72523
- const code = text12.charCodeAt(i);
72985
+ static isASCII(text13) {
72986
+ for (let i = 0; i < text13.length; i++) {
72987
+ const code = text13.charCodeAt(i);
72524
72988
  if (code > 255) {
72525
72989
  return false;
72526
72990
  }
72527
72991
  }
72528
72992
  return true;
72529
72993
  }
72530
- static isPrintableString(text12) {
72531
- return /^[A-Za-z0-9 '()+,-./:=?]*$/g.test(text12);
72994
+ static isPrintableString(text13) {
72995
+ return /^[A-Za-z0-9 '()+,-./:=?]*$/g.test(text13);
72532
72996
  }
72533
72997
  constructor(data, extraNames = {}) {
72534
72998
  this.extraNames = new NameIdentifier();
@@ -77825,12 +78289,12 @@ function formatAddr(afi, addr) {
77825
78289
  s = s + ":";
77826
78290
  return s;
77827
78291
  }
77828
- function parsePrefix(text12) {
77829
- const slash = text12.lastIndexOf("/");
78292
+ function parsePrefix(text13) {
78293
+ const slash = text13.lastIndexOf("/");
77830
78294
  if (slash < 0)
77831
- throw new MeshCodecError("bad-value", `missing / in ${text12}`);
77832
- const host = text12.slice(0, slash);
77833
- const lenStr = text12.slice(slash + 1);
78295
+ throw new MeshCodecError("bad-value", `missing / in ${text13}`);
78296
+ const host = text13.slice(0, slash);
78297
+ const lenStr = text13.slice(slash + 1);
77834
78298
  if (!/^\d{1,3}$/.test(lenStr))
77835
78299
  throw new MeshCodecError("bad-value", `prefix len ${lenStr}`);
77836
78300
  const len = Number(lenStr);
@@ -77839,15 +78303,15 @@ function parsePrefix(text12) {
77839
78303
  throw new MeshCodecError("bad-value", `len ${len} for v${afi}`);
77840
78304
  return makePrefix(afi, mask(addr, len), len);
77841
78305
  }
77842
- function parseAddr(text12) {
77843
- if (text12.includes(":"))
77844
- return { afi: AFI_V6, addr: parseV6(text12) };
77845
- return { afi: AFI_V4, addr: parseV4(text12) };
78306
+ function parseAddr(text13) {
78307
+ if (text13.includes(":"))
78308
+ return { afi: AFI_V6, addr: parseV6(text13) };
78309
+ return { afi: AFI_V4, addr: parseV4(text13) };
77846
78310
  }
77847
- function parseV4(text12) {
77848
- const parts = text12.split(".");
78311
+ function parseV4(text13) {
78312
+ const parts = text13.split(".");
77849
78313
  if (parts.length !== 4)
77850
- throw new MeshCodecError("bad-value", `bad IPv4 ${text12}`);
78314
+ throw new MeshCodecError("bad-value", `bad IPv4 ${text13}`);
77851
78315
  const out = new Uint8Array(4);
77852
78316
  for (let i = 0; i < 4; i++) {
77853
78317
  if (!/^(0|[1-9]\d{0,2})$/.test(parts[i]))
@@ -77859,22 +78323,22 @@ function parseV4(text12) {
77859
78323
  }
77860
78324
  return out;
77861
78325
  }
77862
- function parseV6(text12) {
77863
- if (text12.includes("::")) {
77864
- const halves = text12.split("::");
78326
+ function parseV6(text13) {
78327
+ if (text13.includes("::")) {
78328
+ const halves = text13.split("::");
77865
78329
  if (halves.length !== 2)
77866
- throw new MeshCodecError("bad-value", `bad IPv6 ${text12}`);
78330
+ throw new MeshCodecError("bad-value", `bad IPv6 ${text13}`);
77867
78331
  const head = halves[0] ? halves[0].split(":") : [];
77868
78332
  const tail3 = halves[1] ? halves[1].split(":") : [];
77869
78333
  if (head.length + tail3.length > 7)
77870
- throw new MeshCodecError("bad-value", `bad IPv6 ${text12}`);
78334
+ throw new MeshCodecError("bad-value", `bad IPv6 ${text13}`);
77871
78335
  const zeros = new Array(8 - head.length - tail3.length).fill("0");
77872
- return groupsToBytes([...head, ...zeros, ...tail3], text12);
78336
+ return groupsToBytes([...head, ...zeros, ...tail3], text13);
77873
78337
  }
77874
- const g = text12.split(":");
78338
+ const g = text13.split(":");
77875
78339
  if (g.length !== 8)
77876
- throw new MeshCodecError("bad-value", `bad IPv6 ${text12}`);
77877
- return groupsToBytes(g, text12);
78340
+ throw new MeshCodecError("bad-value", `bad IPv6 ${text13}`);
78341
+ return groupsToBytes(g, text13);
77878
78342
  }
77879
78343
  function groupsToBytes(groups, orig) {
77880
78344
  if (groups.length !== 8)
@@ -85802,13 +86266,18 @@ function scheduledTurn(a) {
85802
86266
  // Absent ⇒ for a cron turn that is 'safe' (unattendedModeFor): declined rather than parked, and nothing
85803
86267
  // approved on anyone's behalf.
85804
86268
  unattended: a.target ? a.prev?.unattended : a.policy.unattended ? true : void 0,
85805
- ...a.target ? {} : { reinforcedVerification: a.policy.reinforcedVerification ? true : void 0 }
86269
+ ...a.target ? {} : { reinforcedVerification: a.policy.reinforcedVerification ? true : void 0 },
86270
+ // THE PERSON'S SETTINGS — their block rules above all — on a fire that OPENS a conversation and so has
86271
+ // no last send to replay them from. Without this the 3am run, the one nobody is watching, is the one
86272
+ // turn their rules do not reach (permission-rules.ts enforces them from exactly this field).
86273
+ ...a.target ? {} : { settings: a.policy.settings }
85806
86274
  };
85807
86275
  }
85808
86276
  function createHostedScheduling(fire) {
85809
86277
  let store = null;
85810
86278
  let isUnattended;
85811
86279
  let isVerified;
86280
+ let settings;
85812
86281
  let stopScheduler = null;
85813
86282
  let cronDeps = { pushEnabled: false };
85814
86283
  let cron = null;
@@ -85832,6 +86301,7 @@ function createHostedScheduling(fire) {
85832
86301
  stopScheduler = null;
85833
86302
  isUnattended = cfg2.isUnattended ?? isUnattended;
85834
86303
  isVerified = cfg2.isVerified ?? isVerified;
86304
+ settings = cfg2.settings ?? settings;
85835
86305
  if (!cfg2.enabled) {
85836
86306
  store = null;
85837
86307
  cronTools = [];
@@ -85871,7 +86341,11 @@ function createHostedScheduling(fire) {
85871
86341
  })();
85872
86342
  },
85873
86343
  policy() {
85874
- return { unattended: isUnattended?.() === true, reinforcedVerification: isVerified?.() === true };
86344
+ return {
86345
+ unattended: isUnattended?.() === true,
86346
+ reinforcedVerification: isVerified?.() === true,
86347
+ settings: settings?.()
86348
+ };
85875
86349
  },
85876
86350
  beginFire(taskId) {
85877
86351
  const id = `cron:${taskId}:${randomUUID12().slice(0, 8)}`;
@@ -85974,8 +86448,8 @@ function listOf(items) {
85974
86448
  const words = items.map((i) => i.text);
85975
86449
  return words.length === 1 ? words[0] : `${words.slice(0, -1).join(", ")} and ${words[words.length - 1]}`;
85976
86450
  }
85977
- function sentence(text12) {
85978
- return `${text12[0].toUpperCase()}${text12.slice(1)}`;
86451
+ function sentence(text13) {
86452
+ return `${text13[0].toUpperCase()}${text13.slice(1)}`;
85979
86453
  }
85980
86454
  function unappliedSettingsNotice(engineLabel, settings, at, inForce = {}) {
85981
86455
  const { items, protective } = unappliedSettings(settings, inForce);
@@ -86011,24 +86485,24 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell", "Shell"]);
86011
86485
  var WRITE_TOOLS = /* @__PURE__ */ new Set(["Edit", "MultiEdit", "Write", "NotebookEdit"]);
86012
86486
  var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "NotebookRead"]);
86013
86487
  var PATH_TOOLS = /* @__PURE__ */ new Set([...WRITE_TOOLS, ...READ_TOOLS]);
86014
- function indexOfUnescaped(text12, char) {
86015
- for (let i = 0; i < text12.length; i++) {
86016
- if (text12[i] !== char)
86488
+ function indexOfUnescaped(text13, char) {
86489
+ for (let i = 0; i < text13.length; i++) {
86490
+ if (text13[i] !== char)
86017
86491
  continue;
86018
86492
  let back = 0;
86019
- for (let j = i - 1; j >= 0 && text12[j] === "\\"; j--)
86493
+ for (let j = i - 1; j >= 0 && text13[j] === "\\"; j--)
86020
86494
  back++;
86021
86495
  if (back % 2 === 0)
86022
86496
  return i;
86023
86497
  }
86024
86498
  return -1;
86025
86499
  }
86026
- function lastIndexOfUnescaped(text12, char) {
86027
- for (let i = text12.length - 1; i >= 0; i--) {
86028
- if (text12[i] !== char)
86500
+ function lastIndexOfUnescaped(text13, char) {
86501
+ for (let i = text13.length - 1; i >= 0; i--) {
86502
+ if (text13[i] !== char)
86029
86503
  continue;
86030
86504
  let back = 0;
86031
- for (let j = i - 1; j >= 0 && text12[j] === "\\"; j--)
86505
+ for (let j = i - 1; j >= 0 && text13[j] === "\\"; j--)
86032
86506
  back++;
86033
86507
  if (back % 2 === 0)
86034
86508
  return i;
@@ -86092,12 +86566,12 @@ function rulesOutOfReach(rules, reach) {
86092
86566
  return true;
86093
86567
  });
86094
86568
  }
86095
- function ruleRequestFor(tool21, detail) {
86569
+ function ruleRequestFor(tool22, detail) {
86096
86570
  const input = detail.input ?? {};
86097
86571
  const command = commandText(input.command);
86098
86572
  const changed = Array.isArray(input.changes) ? input.changes.map((c) => typeof c?.path === "string" ? c.path : void 0) : [];
86099
86573
  const paths = [...detail.paths ?? [], detail.path, typeof input.path === "string" ? input.path : void 0, ...changed].filter((p) => typeof p === "string" && p.length > 0);
86100
- return { tool: tool21, ...command ? { command } : {}, ...paths.length ? { paths } : {} };
86574
+ return { tool: tool22, ...command ? { command } : {}, ...paths.length ? { paths } : {} };
86101
86575
  }
86102
86576
  function commandText(command) {
86103
86577
  if (typeof command === "string")
@@ -86111,9 +86585,9 @@ function describeRuleRequest(request2) {
86111
86585
  return `\`${request2.command}\``;
86112
86586
  const paths = request2.paths ?? [];
86113
86587
  if (paths.length === 1)
86114
- return paths[0];
86588
+ return `\`${paths[0]}\``;
86115
86589
  if (paths.length > 1)
86116
- return `${paths[0]} and ${paths.length - 1} more`;
86590
+ return `\`${paths[0]}\` and ${paths.length - 1} more`;
86117
86591
  return `a ${request2.tool} call`;
86118
86592
  }
86119
86593
  function toolNameForRules(kind, name) {
@@ -86127,6 +86601,8 @@ function toolNameForRules(kind, name) {
86127
86601
  case "write":
86128
86602
  case "file":
86129
86603
  case "filechange":
86604
+ case "delete":
86605
+ case "move":
86130
86606
  return "Edit";
86131
86607
  case "read":
86132
86608
  return "Read";
@@ -86157,12 +86633,13 @@ function commandCandidates(command, depth = 0) {
86157
86633
  add2(command);
86158
86634
  for (const part of splitOnOperators(command)) {
86159
86635
  add2(part);
86160
- const bare = stripPrefixes(part);
86161
- add2(bare);
86162
- if (depth < 3)
86163
- for (const inner of unwrapped(bare))
86164
- for (const c of commandCandidates(inner, depth + 1))
86165
- add2(c);
86636
+ for (const bare of [part, ...wrapperPeels(part)]) {
86637
+ add2(bare);
86638
+ if (depth < 3)
86639
+ for (const inner of unwrapped(bare))
86640
+ for (const c of commandCandidates(inner, depth + 1))
86641
+ add2(c);
86642
+ }
86166
86643
  }
86167
86644
  return [...out];
86168
86645
  }
@@ -86208,18 +86685,60 @@ function splitOnOperators(command) {
86208
86685
  parts.push(current);
86209
86686
  return parts.map((p) => p.trim()).filter(Boolean);
86210
86687
  }
86211
- function stripPrefixes(command) {
86212
- let out = command.trim();
86213
- for (let i = 0; i < 8; i++) {
86214
- const before = out;
86215
- out = out.replace(/^[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|\S*)\s+/, "");
86216
- out = out.replace(/^(?:sudo|doas|command|builtin|exec|nohup|stdbuf|nice|ionice|env|xargs|time|timeout)\b\s*/, "");
86217
- out = out.replace(/^-[A-Za-z-]+[= ]?\S*\s*/, "");
86218
- out = out.replace(/^\d+(?:\.\d+)?[smhd]?\s+/, "");
86219
- if (out === before)
86220
- break;
86688
+ var WRAPPERS = /* @__PURE__ */ new Set([
86689
+ "sudo",
86690
+ "doas",
86691
+ "command",
86692
+ "builtin",
86693
+ "exec",
86694
+ "nohup",
86695
+ "stdbuf",
86696
+ "nice",
86697
+ "ionice",
86698
+ "env",
86699
+ "xargs",
86700
+ "time",
86701
+ "timeout"
86702
+ ]);
86703
+ function wrapperPeels(command) {
86704
+ const ENV = /^[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|\S*)\s+/;
86705
+ const WORD = /^(\S+)\s+/;
86706
+ const out = [];
86707
+ const seen = /* @__PURE__ */ new Set();
86708
+ const queue3 = [command.trim()];
86709
+ while (queue3.length && out.length < 24) {
86710
+ const rest = queue3.shift();
86711
+ if (!rest || seen.has(rest))
86712
+ continue;
86713
+ seen.add(rest);
86714
+ const keep = (next2) => {
86715
+ if (next2) {
86716
+ out.push(next2);
86717
+ queue3.push(next2);
86718
+ }
86719
+ };
86720
+ const env = ENV.exec(rest);
86721
+ if (env) {
86722
+ keep(rest.slice(env[0].length));
86723
+ continue;
86724
+ }
86725
+ const word = WORD.exec(rest);
86726
+ if (!word)
86727
+ continue;
86728
+ const token4 = word[1];
86729
+ const isFlag = token4.startsWith("-");
86730
+ const isDuration = /^\d+(?:\.\d+)?[smhd]?$/.test(token4);
86731
+ if (!WRAPPERS.has(token4) && !isFlag && !isDuration)
86732
+ continue;
86733
+ const next = rest.slice(word[0].length);
86734
+ keep(next);
86735
+ if (isFlag) {
86736
+ const value = WORD.exec(next);
86737
+ if (value && !value[1].startsWith("-"))
86738
+ keep(next.slice(value[0].length));
86739
+ }
86221
86740
  }
86222
- return out.trim() || command.trim();
86741
+ return out;
86223
86742
  }
86224
86743
  function unwrapped(command) {
86225
86744
  const out = [];
@@ -86231,8 +86750,8 @@ function unwrapped(command) {
86231
86750
  out.push(stripQuotes(evaled[1]));
86232
86751
  return out;
86233
86752
  }
86234
- function stripQuotes(text12) {
86235
- const t = text12.trim();
86753
+ function stripQuotes(text13) {
86754
+ const t = text13.trim();
86236
86755
  if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))
86237
86756
  return t.slice(1, -1);
86238
86757
  return t;
@@ -86259,14 +86778,14 @@ function hasWildcard(pattern) {
86259
86778
  }
86260
86779
  return false;
86261
86780
  }
86262
- function matchesWildcard(pattern, text12) {
86781
+ function matchesWildcard(pattern, text13) {
86263
86782
  const STAR = `${MARK}S${MARK}`;
86264
86783
  const BACK = `${MARK}B${MARK}`;
86265
86784
  const processed = pattern.trim().replace(/\\\*/g, STAR).replace(/\\\\/g, BACK);
86266
86785
  let regex = processed.replace(/[.+?^${}()|[\]\\'"]/g, "\\$&").replace(/\*/g, ".*").split(STAR).join("\\*").split(BACK).join("\\\\");
86267
86786
  if (regex.endsWith(" .*") && (processed.match(/\*/g) ?? []).length === 1)
86268
86787
  regex = `${regex.slice(0, -3)}( .*)?`;
86269
- return new RegExp(`^${regex}$`, "s").test(text12.trim());
86788
+ return new RegExp(`^${regex}$`, "s").test(text13.trim());
86270
86789
  }
86271
86790
  var BLOCKED_BY_RULE_NOTICE_ID = "blocked-by-rule";
86272
86791
  function blockedByRuleNotice(engineLabel, at, blocked) {
@@ -86303,10 +86822,6 @@ function opencodeBashDeny(rules) {
86303
86822
  }
86304
86823
  const prefix = /^(.+):\*$/.exec(content)?.[1];
86305
86824
  const pattern = prefix !== void 0 ? `${prefix} *` : content;
86306
- if (prefix === void 0 && !hasWildcard(content) && content.includes("*")) {
86307
- untranslated.push(rule);
86308
- continue;
86309
- }
86310
86825
  patterns.push(pattern, `sudo ${pattern}`, `xargs ${pattern}`);
86311
86826
  expressed.push(rule);
86312
86827
  }
@@ -86321,16 +86836,16 @@ function matchesPath(pattern, path6) {
86321
86836
  }
86322
86837
 
86323
86838
  // ../../packages/host-core/dist/engines/message-locator.js
86324
- function newestSentMessageWithText(messages, text12) {
86325
- if (!text12)
86839
+ function newestSentMessageWithText(messages, text13) {
86840
+ if (!text13)
86326
86841
  return void 0;
86327
86842
  for (let i = messages.length - 1; i >= 0; i--) {
86328
86843
  const m = messages[i];
86329
86844
  if (m.role !== "user" || !m.id)
86330
86845
  continue;
86331
- if (m.text === text12 || m.text.endsWith(`
86846
+ if (m.text === text13 || m.text.endsWith(`
86332
86847
 
86333
- ${text12}`))
86848
+ ${text13}`))
86334
86849
  return m.id;
86335
86850
  }
86336
86851
  return void 0;
@@ -86394,9 +86909,9 @@ async function mergeSessionsVia(deps, sessionIds, opts) {
86394
86909
  const transcripts = [];
86395
86910
  for (const sid of sessionIds) {
86396
86911
  const msgs = await deps.getMessages(sid, opts.projectDir).catch(() => []);
86397
- const text12 = transcriptToText(msgs);
86398
- if (text12)
86399
- transcripts.push(text12);
86912
+ const text13 = transcriptToText(msgs);
86913
+ if (text13)
86914
+ transcripts.push(text13);
86400
86915
  }
86401
86916
  if (transcripts.length === 0)
86402
86917
  return false;
@@ -86419,11 +86934,11 @@ import { homedir as homedir5 } from "node:os";
86419
86934
  function geminiChatsRoot(home = process.env.GEMINI_CLI_HOME || homedir5()) {
86420
86935
  return join29(home, ".gemini", "tmp");
86421
86936
  }
86422
- function parseGeminiSessionFile(file2, text12) {
86937
+ function parseGeminiSessionFile(file2, text13) {
86423
86938
  let header2 = null;
86424
86939
  let seeded = false;
86425
86940
  const messages = [];
86426
- for (const line of text12.split("\n")) {
86941
+ for (const line of text13.split("\n")) {
86427
86942
  if (!line.trim())
86428
86943
  continue;
86429
86944
  let o;
@@ -86468,8 +86983,8 @@ var fileForSession = /* @__PURE__ */ new Map();
86468
86983
  async function findGeminiSessionFile(sessionId, root = geminiChatsRoot()) {
86469
86984
  const known = fileForSession.get(sessionId);
86470
86985
  if (known) {
86471
- const text12 = await readFile10(known, "utf8").catch(() => null);
86472
- const t = text12 === null ? null : parseGeminiSessionFile(known, text12);
86986
+ const text13 = await readFile10(known, "utf8").catch(() => null);
86987
+ const t = text13 === null ? null : parseGeminiSessionFile(known, text13);
86473
86988
  if (t?.header.sessionId === sessionId)
86474
86989
  return t;
86475
86990
  fileForSession.delete(sessionId);
@@ -86484,10 +86999,10 @@ async function findGeminiSessionFile(sessionId, root = geminiChatsRoot()) {
86484
86999
  if (!f.endsWith(".jsonl"))
86485
87000
  continue;
86486
87001
  const full = join29(chats, f);
86487
- const text12 = await readFile10(full, "utf8").catch(() => null);
86488
- if (text12 === null)
87002
+ const text13 = await readFile10(full, "utf8").catch(() => null);
87003
+ if (text13 === null)
86489
87004
  continue;
86490
- const t = parseGeminiSessionFile(full, text12);
87005
+ const t = parseGeminiSessionFile(full, text13);
86491
87006
  if (t?.header.sessionId === sessionId) {
86492
87007
  fileForSession.set(sessionId, full);
86493
87008
  return t;
@@ -86558,14 +87073,14 @@ function createHostedVerification(engineId) {
86558
87073
  const anchor = plan?.trim() ? plan : finalText;
86559
87074
  if (anchor.trim())
86560
87075
  state3 = { ...state3, plan: anchor.slice(0, 4e3) };
86561
- let text12 = finalText;
87076
+ let text13 = finalText;
86562
87077
  let reviewText = "";
86563
87078
  let phaseText = "";
86564
87079
  for (let i = 0; i < rv.RV_MAX_CYCLES * 5 + 4; i++) {
86565
87080
  const gate = state3.phase === "review" ? "review" : state3.phase === "phase-review" ? "phase" : "completeness";
86566
87081
  const still = outstanding?.() ?? [];
86567
87082
  const pending2 = still.length ? still : void 0;
86568
- const call3 = gate === "review" ? rv.reviewGrade(state3, reviewText || text12, todos, pending2) : gate === "phase" ? rv.phaseGrade(state3, state3.lastVerdict?.phaseName ?? "", phaseText || text12, todos, pending2) : rv.completenessGrade(state3, text12, todos, pending2);
87083
+ const call3 = gate === "review" ? rv.reviewGrade(state3, reviewText || text13, todos, pending2) : gate === "phase" ? rv.phaseGrade(state3, state3.lastVerdict?.phaseName ?? "", phaseText || text13, todos, pending2) : rv.completenessGrade(state3, text13, todos, pending2);
86569
87084
  let verdict = null;
86570
87085
  try {
86571
87086
  verdict = rv.parseVerdict(await grade({ model, system: call3.system, prompt: call3.prompt, maxTokens: 16e3, schema: call3.schema }));
@@ -86573,16 +87088,16 @@ function createHostedVerification(engineId) {
86573
87088
  verdict = null;
86574
87089
  }
86575
87090
  if (gone())
86576
- return text12;
87091
+ return text13;
86577
87092
  if (!verdict) {
86578
87093
  standDown(state3.cycles, "The verifier did not return a usable verdict, so this run was not verified.");
86579
- return text12;
87094
+ return text13;
86580
87095
  }
86581
87096
  {
86582
87097
  const why = halt();
86583
87098
  if (why) {
86584
87099
  standDown(state3.cycles, why);
86585
- return text12;
87100
+ return text13;
86586
87101
  }
86587
87102
  }
86588
87103
  const action = rv.rvDecision(verdict, state3);
@@ -86603,45 +87118,45 @@ function createHostedVerification(engineId) {
86603
87118
  summary: verdict.summary
86604
87119
  });
86605
87120
  if (action.kind === "done" || action.kind === "capped")
86606
- return text12;
87121
+ return text13;
86607
87122
  try {
86608
87123
  if (gone())
86609
- return text12;
87124
+ return text13;
86610
87125
  {
86611
87126
  const why = halt();
86612
87127
  if (why) {
86613
87128
  standDown(state3.cycles, why);
86614
- return text12;
87129
+ return text13;
86615
87130
  }
86616
87131
  }
86617
- const before = action.kind === "review" ? reviewText : action.kind === "phase-review" ? phaseText : text12;
87132
+ const before = action.kind === "review" ? reviewText : action.kind === "phase-review" ? phaseText : text13;
86618
87133
  const reply3 = await reprompt(action.prompt);
86619
87134
  if (gone())
86620
- return text12;
87135
+ return text13;
86621
87136
  {
86622
87137
  const why = halt();
86623
87138
  if (why) {
86624
87139
  standDown(state3.cycles, why);
86625
- return text12;
87140
+ return text13;
86626
87141
  }
86627
87142
  }
86628
87143
  if (!reply3 || reply3 === before) {
86629
87144
  standDown(state3.cycles, "The agent did not produce anything new when sent back, so this run was not verified.");
86630
- return text12;
87145
+ return text13;
86631
87146
  }
86632
87147
  if (action.kind === "review")
86633
87148
  reviewText = reply3;
86634
87149
  else if (action.kind === "phase-review")
86635
87150
  phaseText = reply3;
86636
87151
  else
86637
- text12 = reply3;
87152
+ text13 = reply3;
86638
87153
  } catch {
86639
87154
  standDown(state3.cycles, "Verification could not be completed, so this run was not verified.");
86640
- return text12;
87155
+ return text13;
86641
87156
  }
86642
87157
  }
86643
87158
  emit2({ ...base, action: "capped", gate: "completeness", cycle: rv.RV_MAX_CYCLES });
86644
- return text12;
87159
+ return text13;
86645
87160
  }
86646
87161
  };
86647
87162
  }
@@ -86952,7 +87467,7 @@ function createRunTracker(opts) {
86952
87467
  }
86953
87468
  return token4;
86954
87469
  },
86955
- turnEnded(handle3, text12) {
87470
+ turnEnded(handle3, text13) {
86956
87471
  const token4 = handle3;
86957
87472
  const c = convs.get(resolve16(token4.key));
86958
87473
  if (!c)
@@ -86960,14 +87475,14 @@ function createRunTracker(opts) {
86960
87475
  c.live.delete(token4.id);
86961
87476
  const run9 = c.run;
86962
87477
  if (run9 && !run9.closed && token4.request === run9.request) {
86963
- if (text12 === null && token4.carried && run9.waiters.length > 0) {
87478
+ if (text13 === null && token4.carried && run9.waiters.length > 0) {
86964
87479
  opts.log?.(`[run] ${c.key}: the turn delivering a result failed \u2014 the run is not verified`);
86965
87480
  closeRun(c, run9, { reason: NOT_DELIVERED });
86966
87481
  gc(c);
86967
87482
  return;
86968
87483
  }
86969
- if (token4.id !== run9.gradedTurn && text12 !== null)
86970
- run9.lastText = text12;
87484
+ if (token4.id !== run9.gradedTurn && text13 !== null)
87485
+ run9.lastText = text13;
86971
87486
  if (!holdsRun(run9) && !ownTurnRunning(run9)) {
86972
87487
  run9.finishedIdle = true;
86973
87488
  finish2(c, { outcome: "finished", text: run9.lastText });
@@ -87012,7 +87527,7 @@ function createRunTracker(opts) {
87012
87527
  return items.map((i) => describe3(c, i, t, ""));
87013
87528
  return run9.waiters.length > 0 && ownTurnRunning(run9) ? ["the turn reporting the background work is still running"] : void 0;
87014
87529
  },
87015
- openRun(sessionId, task, turn, text12) {
87530
+ openRun(sessionId, task, turn, text13) {
87016
87531
  const c = conv(resolve16(sessionId));
87017
87532
  const token4 = turn;
87018
87533
  const run9 = {
@@ -87023,7 +87538,7 @@ function createRunTracker(opts) {
87023
87538
  conv: c,
87024
87539
  closed: null,
87025
87540
  waiters: [],
87026
- lastText: text12,
87541
+ lastText: text13,
87027
87542
  finishedIdle: false
87028
87543
  };
87029
87544
  if (c.lastPersonTurn > token4.id) {
@@ -88159,25 +88674,25 @@ function validate(schema, value, path6 = "$", out = []) {
88159
88674
  }
88160
88675
  return out;
88161
88676
  }
88162
- function extractJson(text12) {
88163
- const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(text12);
88164
- const candidates = [fenced?.[1], text12].filter((v) => typeof v === "string");
88677
+ function extractJson(text13) {
88678
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(text13);
88679
+ const candidates = [fenced?.[1], text13].filter((v) => typeof v === "string");
88165
88680
  for (const c of candidates) {
88166
88681
  try {
88167
88682
  return JSON.parse(c.trim());
88168
88683
  } catch {
88169
88684
  }
88170
88685
  }
88171
- for (let start3 = 0; start3 < text12.length; start3++) {
88172
- const open10 = text12[start3];
88686
+ for (let start3 = 0; start3 < text13.length; start3++) {
88687
+ const open10 = text13[start3];
88173
88688
  if (open10 !== "{" && open10 !== "[")
88174
88689
  continue;
88175
88690
  const close = open10 === "{" ? "}" : "]";
88176
88691
  let depth = 0;
88177
88692
  let inStr = false;
88178
88693
  let esc3 = false;
88179
- for (let i = start3; i < text12.length; i++) {
88180
- const ch = text12[i];
88694
+ for (let i = start3; i < text13.length; i++) {
88695
+ const ch = text13[i];
88181
88696
  if (inStr) {
88182
88697
  if (esc3)
88183
88698
  esc3 = false;
@@ -88197,7 +88712,7 @@ function extractJson(text12) {
88197
88712
  depth--;
88198
88713
  if (depth === 0) {
88199
88714
  try {
88200
- return JSON.parse(text12.slice(start3, i + 1));
88715
+ return JSON.parse(text13.slice(start3, i + 1));
88201
88716
  } catch {
88202
88717
  break;
88203
88718
  }
@@ -88296,11 +88811,11 @@ function createEngineHttpApi(port, host = "127.0.0.1") {
88296
88811
  });
88297
88812
  if (!res.ok)
88298
88813
  return null;
88299
- const text12 = await res.text();
88300
- if (!text12.trim())
88814
+ const text13 = await res.text();
88815
+ if (!text13.trim())
88301
88816
  return true;
88302
88817
  try {
88303
- const parsed = JSON.parse(text12);
88818
+ const parsed = JSON.parse(text13);
88304
88819
  return parsed === null ? true : parsed;
88305
88820
  } catch {
88306
88821
  return true;
@@ -88549,7 +89064,7 @@ function createEngineTranscript(engineId) {
88549
89064
  return blocks.length ? [row(sid, "assistant", blocks)] : [];
88550
89065
  }
88551
89066
  return {
88552
- userTurn(sid, cwd, text12) {
89067
+ userTurn(sid, cwd, text13) {
88553
89068
  if (!cwd)
88554
89069
  return;
88555
89070
  cwdOf.set(sid, cwd);
@@ -88557,23 +89072,23 @@ function createEngineTranscript(engineId) {
88557
89072
  if (prev)
88558
89073
  void write2(sid, flushAssistant(sid, prev, true));
88559
89074
  open10.set(sid, { cwd, blocks: [], prose: "", reasoning: "", pending: /* @__PURE__ */ new Map() });
88560
- void write2(sid, [row(sid, "user", text12)]);
89075
+ void write2(sid, [row(sid, "user", text13)]);
88561
89076
  },
88562
- assistantText(sid, text12) {
89077
+ assistantText(sid, text13) {
88563
89078
  const t = open10.get(sid);
88564
- if (!t || !text12)
89079
+ if (!t || !text13)
88565
89080
  return;
88566
89081
  if (t.reasoning)
88567
89082
  fold(t);
88568
- t.prose += text12;
89083
+ t.prose += text13;
88569
89084
  },
88570
- thinking(sid, text12) {
89085
+ thinking(sid, text13) {
88571
89086
  const t = open10.get(sid);
88572
- if (!t || !text12)
89087
+ if (!t || !text13)
88573
89088
  return;
88574
89089
  if (t.prose)
88575
89090
  fold(t);
88576
- t.reasoning += text12;
89091
+ t.reasoning += text13;
88577
89092
  },
88578
89093
  toolUse(sid, id, name, input) {
88579
89094
  const t = open10.get(sid);
@@ -88680,13 +89195,13 @@ function createEngineTranscript(engineId) {
88680
89195
  if (!file2)
88681
89196
  return false;
88682
89197
  const run9 = (chain2.get(sid) ?? Promise.resolve()).then(async () => {
88683
- let text12;
89198
+ let text13;
88684
89199
  try {
88685
- text12 = await readFile12(file2, "utf8");
89200
+ text13 = await readFile12(file2, "utf8");
88686
89201
  } catch {
88687
89202
  return false;
88688
89203
  }
88689
- const lines = text12.split("\n");
89204
+ const lines = text13.split("\n");
88690
89205
  const cut = cutLine(lines, at);
88691
89206
  if (cut < 0)
88692
89207
  return false;
@@ -88735,8 +89250,8 @@ ${blk.text.trim()}`);
88735
89250
  else if (blk.type === "tool_use")
88736
89251
  parts.push(`[tool call: ${blk.name ?? "tool"}] ${JSON.stringify(blk.input ?? {})}`);
88737
89252
  else if (blk.type === "tool_result") {
88738
- const text12 = typeof blk.content === "string" ? blk.content : Array.isArray(blk.content) ? blk.content.map((x) => x?.text ?? "").join("\n") : "";
88739
- parts.push(`[tool result${blk.is_error ? " (error)" : ""}] ${text12.trim()}`);
89253
+ const text13 = typeof blk.content === "string" ? blk.content : Array.isArray(blk.content) ? blk.content.map((x) => x?.text ?? "").join("\n") : "";
89254
+ parts.push(`[tool result${blk.is_error ? " (error)" : ""}] ${text13.trim()}`);
88740
89255
  }
88741
89256
  }
88742
89257
  }
@@ -88877,8 +89392,8 @@ function readOutputFromLocation(u) {
88877
89392
  if (!path6)
88878
89393
  return "";
88879
89394
  try {
88880
- const text12 = readFileSync24(path6, "utf8");
88881
- return text12.length > MAX_LOCATION_READ_BYTES ? text12.slice(0, MAX_LOCATION_READ_BYTES) : text12;
89395
+ const text13 = readFileSync24(path6, "utf8");
89396
+ return text13.length > MAX_LOCATION_READ_BYTES ? text13.slice(0, MAX_LOCATION_READ_BYTES) : text13;
88882
89397
  } catch {
88883
89398
  return "";
88884
89399
  }
@@ -88995,7 +89510,8 @@ var OPENCODE_ACP = {
88995
89510
  const SAFE = `${READ},"glob":"allow","grep":"allow","lsp":"allow","todowrite":"allow"`;
88996
89511
  const KEEP_DENY = '"question":"deny","plan_enter":"deny","plan_exit":"deny"';
88997
89512
  const { patterns } = opencodeBashDeny(deny);
88998
- const BLOCK = patterns.length ? `,"bash":{${patterns.map((p) => `${JSON.stringify(p)}:"deny"`).join(",")}}` : "";
89513
+ const BLOCKED = patterns.map((p) => `${JSON.stringify(p)}:"deny"`).join(",");
89514
+ const BLOCK = BLOCKED ? `,"bash":{${BLOCKED}}` : "";
88999
89515
  switch (mode) {
89000
89516
  // Everything asks except the harmless reads — this is the mode that means "supervise me".
89001
89517
  case "default":
@@ -89008,10 +89524,12 @@ var OPENCODE_ACP = {
89008
89524
  return { OPENCODE_PERMISSION: `{"*":"ask","edit":"allow",${SAFE},${KEEP_DENY}${BLOCK}}` };
89009
89525
  // Nothing may change on disk. `deny` rather than `ask` so there is no card offering to
89010
89526
  // break the mode's promise, and no prompt for something plan mode should never do.
89011
- // No `${BLOCK}` here on purpose: a nested `bash` object would REPLACE this blanket deny (last key wins
89012
- // in one object) and the patterns it lists are narrower than "no commands at all".
89527
+ // Plan denies every command already, so the block patterns add nothing — but they are listed ANYWAY,
89528
+ // inside the same `bash` object (`{"*":"deny"}` is what a bare `"deny"` means to its matcher), because
89529
+ // "the rules went to the engine" must be true in every mode. A special case here would make the notice
89530
+ // that reads it (`blockRulesInForce`) quietly wrong the day plan mode's ruleset changes.
89013
89531
  case "plan":
89014
- return { OPENCODE_PERMISSION: `{"*":"ask","edit":"deny","bash":"deny",${SAFE},${KEEP_DENY}}` };
89532
+ return { OPENCODE_PERMISSION: `{"*":"ask","edit":"deny","bash":{"*":"deny"${BLOCKED ? `,${BLOCKED}` : ""}},${SAFE},${KEEP_DENY}}` };
89015
89533
  // bypassPermissions / dontAsk / unset ⇒ leave its own ruleset alone: both of those modes mean
89016
89534
  // "never prompt, let it run", which is what OpenCode's shipped `"*": "allow"` already is. Except for
89017
89535
  // what the person BLOCKED: Claude checks its block rules ahead of bypass (its own
@@ -89200,25 +89718,25 @@ function relFromFileUri(uri, dir2) {
89200
89718
  }
89201
89719
  function collectReplay(out, u) {
89202
89720
  const kind = u.sessionUpdate;
89203
- const text12 = u.content?.text ?? "";
89204
- if (kind === "user_message_chunk" && REPLAY_SCAFFOLD.test(text12))
89721
+ const text13 = u.content?.text ?? "";
89722
+ if (kind === "user_message_chunk" && REPLAY_SCAFFOLD.test(text13))
89205
89723
  return;
89206
89724
  const role = kind === "user_message_chunk" ? "user" : kind === "agent_message_chunk" || kind === "agent_thought_chunk" ? "assistant" : null;
89207
- if (!role || !text12)
89725
+ if (!role || !text13)
89208
89726
  return;
89209
89727
  const blockKind = kind === "agent_thought_chunk" ? "thinking" : "text";
89210
89728
  const last = out[out.length - 1];
89211
89729
  if (last && last.role === role) {
89212
89730
  const lastBlock = last.blocks?.[last.blocks.length - 1];
89213
89731
  if (lastBlock && lastBlock.kind === blockKind)
89214
- lastBlock.content += text12;
89732
+ lastBlock.content += text13;
89215
89733
  else
89216
- (last.blocks ??= []).push({ kind: blockKind, content: text12 });
89734
+ (last.blocks ??= []).push({ kind: blockKind, content: text13 });
89217
89735
  if (blockKind === "text")
89218
- last.content += text12;
89736
+ last.content += text13;
89219
89737
  return;
89220
89738
  }
89221
- out.push({ role, content: blockKind === "text" ? text12 : "", blocks: [{ kind: blockKind, content: text12 }] });
89739
+ out.push({ role, content: blockKind === "text" ? text13 : "", blocks: [{ kind: blockKind, content: text13 }] });
89222
89740
  }
89223
89741
  function readUsage(res) {
89224
89742
  const r = res ?? {};
@@ -89481,19 +89999,19 @@ function createAcpBackend(engine, send5, resolve16, services) {
89481
89999
  const origin = turn.origin;
89482
90000
  switch (u.sessionUpdate) {
89483
90001
  case "agent_message_chunk": {
89484
- const text12 = u.content?.text ?? "";
89485
- if (text12) {
89486
- turn.acc += text12;
89487
- transcript.assistantText(sid, text12);
89488
- send5({ type: "chat:stream", sessionId: sid, clientRequestId: turn.clientRequestId, messageId: turn.messageId, delta: text12, kind: "text" }, origin);
90002
+ const text13 = u.content?.text ?? "";
90003
+ if (text13) {
90004
+ turn.acc += text13;
90005
+ transcript.assistantText(sid, text13);
90006
+ send5({ type: "chat:stream", sessionId: sid, clientRequestId: turn.clientRequestId, messageId: turn.messageId, delta: text13, kind: "text" }, origin);
89489
90007
  }
89490
90008
  break;
89491
90009
  }
89492
90010
  case "agent_thought_chunk": {
89493
- const text12 = u.content?.text ?? "";
89494
- if (text12) {
89495
- transcript.thinking(sid, text12);
89496
- send5({ type: "chat:stream", sessionId: sid, clientRequestId: turn.clientRequestId, messageId: turn.messageId, delta: text12, kind: "thinking" }, origin);
90011
+ const text13 = u.content?.text ?? "";
90012
+ if (text13) {
90013
+ transcript.thinking(sid, text13);
90014
+ send5({ type: "chat:stream", sessionId: sid, clientRequestId: turn.clientRequestId, messageId: turn.messageId, delta: text13, kind: "thinking" }, origin);
89497
90015
  }
89498
90016
  break;
89499
90017
  }
@@ -90000,11 +90518,11 @@ function createAcpBackend(engine, send5, resolve16, services) {
90000
90518
  return out;
90001
90519
  }
90002
90520
  function promptBlocks(params) {
90003
- const text12 = params.message.trim() === "/compact" && engine.compactCommand ? engine.compactCommand : params.message;
90521
+ const text13 = params.message.trim() === "/compact" && engine.compactCommand ? engine.compactCommand : params.message;
90004
90522
  const seed = params.sessionId ? pendingSeed.get(params.sessionId) : void 0;
90005
90523
  const blocks = [{ type: "text", text: seed ? `${seed}
90006
90524
 
90007
- ${text12}` : text12 }];
90525
+ ${text13}` : text13 }];
90008
90526
  const skipped = [];
90009
90527
  if (!supportsCapability(declaredCaps, "attachments"))
90010
90528
  return { blocks, skipped };
@@ -90109,8 +90627,8 @@ ${text12}` : text12 }];
90109
90627
  }
90110
90628
  async function summarizeOnScratch(conn, sessionId, projectDir) {
90111
90629
  const rows = await backend2.getMessages(sessionId, projectDir).catch(() => []);
90112
- const text12 = transcriptToText(rows, 6e4);
90113
- if (!text12)
90630
+ const text13 = transcriptToText(rows, 6e4);
90631
+ if (!text13)
90114
90632
  return null;
90115
90633
  const fresh = await conn.t.request("session/new", { cwd: conn.cwd, mcpServers: [] }).catch(() => null);
90116
90634
  const scratch = fresh?.sessionId;
@@ -90128,7 +90646,7 @@ ${text12}` : text12 }];
90128
90646
  return await promptForText(conn.t, scratch, `${prompt("agent.session-summary")}
90129
90647
 
90130
90648
  --- Conversation ---
90131
- ${text12}`);
90649
+ ${text13}`);
90132
90650
  } finally {
90133
90651
  knownSessions.delete(scratch);
90134
90652
  sessionConfig.delete(scratch);
@@ -90612,8 +91130,11 @@ ${text12}`);
90612
91130
  let finishing = null;
90613
91131
  if (params.permissionMode)
90614
91132
  sessionMode.set(sid, params.permissionMode);
90615
- if (params.settings?.permissions?.deny)
90616
- sessionDeny.set(sid, params.settings.permissions.deny);
91133
+ const denyNow = params.settings?.permissions?.deny;
91134
+ if (denyNow?.length)
91135
+ sessionDeny.set(sid, denyNow);
91136
+ else
91137
+ sessionDeny.delete(sid);
90617
91138
  send5({ type: "agent:status", sessionId: sid, clientRequestId: params.clientRequestId, status: "running" }, origin);
90618
91139
  const schema = supportsCapability(declared, "structuredOutput") ? params.jsonSchema : void 0;
90619
91140
  const enforceable = schema ? canEnforce(schema) : { ok: false };
@@ -91200,13 +91721,13 @@ ${detail}` : detail, !overflow);
91200
91721
  sessionCwd.set(sessionId, projectDir);
91201
91722
  if (!conn)
91202
91723
  return { ok: false, error: `${engineLabel} is not running.` };
91203
- const text12 = await summarizeOnScratch(conn, sessionId, projectDir) ?? await (async () => {
91724
+ const text13 = await summarizeOnScratch(conn, sessionId, projectDir) ?? await (async () => {
91204
91725
  await ensureLoaded(conn, sessionId);
91205
91726
  return promptForText(conn.t, sessionId, prompt("agent.session-summary"));
91206
91727
  })();
91207
- if (!text12)
91728
+ if (!text13)
91208
91729
  return { ok: false, error: `${engineLabel} could not summarise this conversation.` };
91209
- const { title, summary } = parseSummaryReply(text12);
91730
+ const { title, summary } = parseSummaryReply(text13);
91210
91731
  if (title)
91211
91732
  sessionTitles.set(sessionId, title);
91212
91733
  if (title)
@@ -91474,8 +91995,8 @@ ${detail}` : detail, !overflow);
91474
91995
  const seenIds = /* @__PURE__ */ new Set();
91475
91996
  let idx = 0;
91476
91997
  for (const m of log10.messages) {
91477
- const text12 = geminiMessageText(m);
91478
- if (!text12 || REPLAY_SCAFFOLD.test(text12))
91998
+ const text13 = geminiMessageText(m);
91999
+ if (!text13 || REPLAY_SCAFFOLD.test(text13))
91479
92000
  continue;
91480
92001
  if (m.id && seenIds.has(m.id))
91481
92002
  continue;
@@ -91485,7 +92006,7 @@ ${detail}` : detail, !overflow);
91485
92006
  const uuid3 = m.id ?? `acp-user-${idx}`;
91486
92007
  if (role === "user")
91487
92008
  idx++;
91488
- rows.push({ role, content: text12, uuid: uuid3, blocks: [{ kind: "text", content: text12 }] });
92009
+ rows.push({ role, content: text13, uuid: uuid3, blocks: [{ kind: "text", content: text13 }] });
91489
92010
  }
91490
92011
  if (rows.length > 0) {
91491
92012
  const firstUser2 = rows.find((r) => r.role === "user")?.content?.trim();
@@ -91602,20 +92123,20 @@ function codexTranscriptRows(turns, textOf) {
91602
92123
  const kind = String(it.type ?? "");
91603
92124
  if (kind && kind !== "userMessage" && kind !== "agentMessage")
91604
92125
  continue;
91605
- const text12 = textOf(it);
91606
- if (!text12)
92126
+ const text13 = textOf(it);
92127
+ if (!text13)
91607
92128
  continue;
91608
92129
  const role = String(it.role ?? (kind === "userMessage" ? "user" : "assistant")) === "user" ? "user" : "assistant";
91609
92130
  const last = rows[rows.length - 1];
91610
92131
  if (last && last.role === role) {
91611
92132
  last.content += `
91612
- ${text12}`;
92133
+ ${text13}`;
91613
92134
  if (last.turns[last.turns.length - 1] !== ti)
91614
92135
  last.turns.push(ti);
91615
92136
  continue;
91616
92137
  }
91617
92138
  const uuid3 = typeof it.id === "string" && it.id ? it.id : void 0;
91618
- rows.push({ role, content: text12, ...uuid3 ? { uuid: uuid3 } : {}, turns: [ti] });
92139
+ rows.push({ role, content: text13, ...uuid3 ? { uuid: uuid3 } : {}, turns: [ti] });
91619
92140
  }
91620
92141
  });
91621
92142
  return rows;
@@ -91833,9 +92354,9 @@ function itemToolName(item) {
91833
92354
  case "webSearch":
91834
92355
  return "WebSearch";
91835
92356
  case "mcpToolCall": {
91836
- const tool21 = String(i.tool ?? "tool");
92357
+ const tool22 = String(i.tool ?? "tool");
91837
92358
  const server2 = String(i.server ?? "");
91838
- return server2 ? `${server2}_${tool21}` : tool21;
92359
+ return server2 ? `${server2}_${tool22}` : tool22;
91839
92360
  }
91840
92361
  case "dynamicToolCall":
91841
92362
  return String(i.tool ?? "tool");
@@ -92192,8 +92713,8 @@ function createCodexAppServerBackend(send5, resolve16, services) {
92192
92713
  subagentSinks.delete(scratch);
92193
92714
  subagentDone.delete(scratch);
92194
92715
  }
92195
- const text12 = out.join("").trim();
92196
- return text12 || null;
92716
+ const text13 = out.join("").trim();
92717
+ return text13 || null;
92197
92718
  }
92198
92719
  const pendingApprovals = /* @__PURE__ */ new Map();
92199
92720
  const pendingDialogs = /* @__PURE__ */ new Map();
@@ -92218,8 +92739,6 @@ function createCodexAppServerBackend(send5, resolve16, services) {
92218
92739
  const settled = turn ? runningTurnApprovalAnswer(modeNow, turn.sandbox) : "ask";
92219
92740
  if (settled === "allow")
92220
92741
  return Promise.resolve(allow);
92221
- if (settled === "deny")
92222
- return Promise.resolve(deny);
92223
92742
  const requestId = randomUUID17();
92224
92743
  return new Promise((resolveOutcome) => {
92225
92744
  pendingApprovals.set(requestId, { resolve: resolveOutcome, threadId, allow, allowPersist, deny });
@@ -92547,7 +93066,11 @@ function createCodexAppServerBackend(send5, resolve16, services) {
92547
93066
  });
92548
93067
  t.onRequest("item/fileChange/requestApproval", (raw) => {
92549
93068
  const p = raw ?? {};
92550
- return parkApproval(raw, "Edit", { decision: "accept" }, { decision: "acceptForSession" }, { decision: "decline" }, { title: p.reason, path: p.grantRoot });
93069
+ return parkApproval(raw, "Edit", { decision: "accept" }, { decision: "acceptForSession" }, { decision: "decline" }, {
93070
+ title: p.reason,
93071
+ path: p.grantRoot,
93072
+ input: { changes: p.changes, path: p.path }
93073
+ });
92551
93074
  });
92552
93075
  t.onRequest("item/permissions/requestApproval", (raw) => parkApproval(raw, "Permissions", { decision: "accept" }, { decision: "acceptForSession" }, { decision: "decline" }, { title: "Codex is requesting additional permissions" }));
92553
93076
  t.onRequest("execCommandApproval", (raw) => {
@@ -92556,7 +93079,11 @@ function createCodexAppServerBackend(send5, resolve16, services) {
92556
93079
  });
92557
93080
  t.onRequest("applyPatchApproval", (raw) => {
92558
93081
  const p = raw ?? {};
92559
- return parkApproval(raw, "Edit", { decision: "accept" }, { decision: "acceptForSession" }, { decision: "decline" }, { title: p.reason });
93082
+ return parkApproval(raw, "Edit", { decision: "accept" }, { decision: "acceptForSession" }, { decision: "decline" }, {
93083
+ title: p.reason,
93084
+ path: p.grantRoot,
93085
+ input: { changes: p.changes ?? p.fileChanges }
93086
+ });
92560
93087
  });
92561
93088
  t.onRequest("item/tool/requestUserInput", (raw) => {
92562
93089
  const p = raw ?? {};
@@ -93610,13 +94137,13 @@ function createCodexAppServerBackend(send5, resolve16, services) {
93610
94137
  */
93611
94138
  summarizeSession: async (sessionId, projectDir) => {
93612
94139
  const msgs = await backend2.getMessages(sessionId, threadCwd.get(sessionId) ?? spawnedCwd ?? projectDir).catch(() => []);
93613
- const text12 = transcriptToText(msgs, 6e4);
93614
- if (!text12)
94140
+ const text13 = transcriptToText(msgs, 6e4);
94141
+ if (!text13)
93615
94142
  return { ok: false, error: "That conversation has nothing to summarise yet." };
93616
94143
  const reply3 = await promptScratchThread(`${prompt("agent.session-summary")}
93617
94144
 
93618
94145
  --- Conversation ---
93619
- ${text12}`, threadCwd.get(sessionId) ?? spawnedCwd ?? projectDir);
94146
+ ${text13}`, threadCwd.get(sessionId) ?? spawnedCwd ?? projectDir);
93620
94147
  if (!reply3)
93621
94148
  return { ok: false, error: `${label} could not summarise this conversation.` };
93622
94149
  const { title, summary } = parseSummaryReply(reply3);
@@ -93729,8 +94256,8 @@ var seq = 0;
93729
94256
  var now = () => (/* @__PURE__ */ new Date()).toISOString();
93730
94257
  var ctx = [];
93731
94258
  var CTX_MAX = 60;
93732
- function pushCtx(role, text12) {
93733
- const t = (text12 ?? "").trim();
94259
+ function pushCtx(role, text13) {
94260
+ const t = (text13 ?? "").trim();
93734
94261
  if (!t)
93735
94262
  return;
93736
94263
  ctx.push({ role, text: t.length > 300 ? `${t.slice(0, 300)}\u2026` : t });
@@ -93996,23 +94523,23 @@ var HostPluginApi = class {
93996
94523
  secret(envKey2) {
93997
94524
  return this.secretResolver(this.pluginId, envKey2);
93998
94525
  }
93999
- registerTool(tool21) {
94526
+ registerTool(tool22) {
94000
94527
  const prefix = this.pluginId.replace(/[^a-zA-Z0-9_]/g, "_");
94001
- const name = tool21.name.startsWith(`${prefix}_`) ? tool21.name : `${prefix}_${tool21.name}`;
94002
- this.registry.tools.push({ ...tool21, name });
94528
+ const name = tool22.name.startsWith(`${prefix}_`) ? tool22.name : `${prefix}_${tool22.name}`;
94529
+ this.registry.tools.push({ ...tool22, name });
94003
94530
  }
94004
- registerUiTool(tool21) {
94005
- if (!this.requireGrant("surfaces", `UI tool "${tool21.name}"`))
94531
+ registerUiTool(tool22) {
94532
+ if (!this.requireGrant("surfaces", `UI tool "${tool22.name}"`))
94006
94533
  return;
94007
94534
  const prefix = this.pluginId.replace(/[^a-zA-Z0-9_]/g, "_");
94008
- const name = tool21.name.startsWith(`${prefix}_`) ? tool21.name : `${prefix}_${tool21.name}`;
94535
+ const name = tool22.name.startsWith(`${prefix}_`) ? tool22.name : `${prefix}_${tool22.name}`;
94009
94536
  const tier = this.hasGrant("customUI") ? "verified" : "third-party";
94010
94537
  this.registry.tools.push({
94011
94538
  name,
94012
- description: tool21.description,
94013
- inputSchema: tool21.inputSchema,
94014
- handler: async (args) => toResult(await tool21.handler(args, NOOP_UI_CTX)),
94015
- __ctxHandler: async (args, ctx2) => toResult(await tool21.handler(args, ctx2)),
94539
+ description: tool22.description,
94540
+ inputSchema: tool22.inputSchema,
94541
+ handler: async (args) => toResult(await tool22.handler(args, NOOP_UI_CTX)),
94542
+ __ctxHandler: async (args, ctx2) => toResult(await tool22.handler(args, ctx2)),
94016
94543
  __tier: tier
94017
94544
  });
94018
94545
  }
@@ -94122,8 +94649,8 @@ function createOpenClawAdapter(pluginId, config2, secretResolver, registry3) {
94122
94649
  pluginId,
94123
94650
  config: config2,
94124
94651
  secret: (envKey2) => secretResolver(pluginId, envKey2),
94125
- registerTool: (tool21) => {
94126
- const got = typeof tool21 === "function" ? tool21({}) : tool21;
94652
+ registerTool: (tool22) => {
94653
+ const got = typeof tool22 === "function" ? tool22({}) : tool22;
94127
94654
  const arr = Array.isArray(got) ? got : got ? [got] : [];
94128
94655
  for (const t of arr) {
94129
94656
  const c = convertTool(t);
@@ -95589,8 +96116,8 @@ init_prompts();
95589
96116
  // ../../packages/voice-core/dist/pipeline/history.js
95590
96117
  function formatHistoryLines(turns, maxLineChars = 300) {
95591
96118
  return turns.map((t) => {
95592
- const text12 = t.text.replace(/\s+/g, " ").trim();
95593
- const capped = text12.length > maxLineChars ? text12.slice(0, maxLineChars - 1).trimEnd() + "\u2026" : text12;
96119
+ const text13 = t.text.replace(/\s+/g, " ").trim();
96120
+ const capped = text13.length > maxLineChars ? text13.slice(0, maxLineChars - 1).trimEnd() + "\u2026" : text13;
95594
96121
  return `${t.role.toUpperCase()}: ${capped}`;
95595
96122
  }).join("\n");
95596
96123
  }
@@ -95885,15 +96412,15 @@ ${truncate(input.result, 4e3)}`
95885
96412
  }
95886
96413
  };
95887
96414
  function ruleBasedTurn(input) {
95888
- const text12 = input.result.trim();
95889
- const lower = text12.toLowerCase();
96415
+ const text13 = input.result.trim();
96416
+ const lower = text13.toLowerCase();
95890
96417
  let status2 = "done";
95891
96418
  if (/\b(error|failed|could not|cannot|unable to)\b/.test(lower))
95892
96419
  status2 = "error";
95893
- else if (text12.endsWith("?") || /\b(which|should i|do you want|would you like|please confirm|clarif)\b/.test(lower)) {
96420
+ else if (text13.endsWith("?") || /\b(which|should i|do you want|would you like|please confirm|clarif)\b/.test(lower)) {
95894
96421
  status2 = "needs_input";
95895
96422
  }
95896
- const line = firstSentences(stripForSpeech(text12)) || input.recentActivity.slice(-1)[0] || "Done.";
96423
+ const line = firstSentences(stripForSpeech(text13)) || input.recentActivity.slice(-1)[0] || "Done.";
95897
96424
  return { line, status: status2 };
95898
96425
  }
95899
96426
  function stripForSpeech(s) {
@@ -95971,8 +96498,8 @@ var VoiceGuideOrchestrator = class {
95971
96498
  /** The bridge feeds every finalized transcript line here (capped buffer). An
95972
96499
  * assistant line means the guide finished speaking — drain the next queued
95973
96500
  * update immediately instead of waiting out the gap timer. */
95974
- noteTranscript(role, text12) {
95975
- const t = text12.trim();
96501
+ noteTranscript(role, text13) {
96502
+ const t = text13.trim();
95976
96503
  if (!t)
95977
96504
  return;
95978
96505
  this.conversation.push({ role, text: t });
@@ -96506,26 +97033,26 @@ Additional context: ${context}` : task;
96506
97033
  this.deps.emit({ type: "voice:milestone", text: spoken });
96507
97034
  this.say(spoken, { urgent: focused });
96508
97035
  }
96509
- speakMilestone(text12) {
96510
- this.deps.emit({ type: "voice:milestone", text: text12 });
96511
- this.say(text12);
97036
+ speakMilestone(text13) {
97037
+ this.deps.emit({ type: "voice:milestone", text: text13 });
97038
+ this.say(text13);
96512
97039
  }
96513
97040
  // ── One mouth, N sessions: serialized speech ───────────────────────────────
96514
97041
  /** Speak `text`, serializing non-urgent lines so concurrent sessions don't
96515
97042
  * talk over each other. Urgent lines (blocking questions, the focused
96516
97043
  * session's result, critical notices) always go out immediately. */
96517
- say(text12, opts = {}) {
96518
- if (!text12)
97044
+ say(text13, opts = {}) {
97045
+ if (!text13)
96519
97046
  return;
96520
97047
  const now2 = this.now();
96521
97048
  if (opts.urgent || this.sayQueue.length === 0 && now2 - this.lastSayAt >= SPEAK_GAP_MS) {
96522
97049
  this.lastSayAt = now2;
96523
- this.voice?.speakNow(text12);
97050
+ this.voice?.speakNow(text13);
96524
97051
  return;
96525
97052
  }
96526
97053
  if (this.sayQueue.length >= SPEAK_QUEUE_CAP)
96527
97054
  this.sayQueue.shift();
96528
- this.sayQueue.push(text12);
97055
+ this.sayQueue.push(text13);
96529
97056
  this.armSayTimer();
96530
97057
  }
96531
97058
  armSayTimer() {
@@ -96656,11 +97183,11 @@ var ClientProxyProvider = class {
96656
97183
  /** Audio is client↔provider direct — the daemon never sees mic/model PCM. */
96657
97184
  sendAudio(_pcm24k) {
96658
97185
  }
96659
- respondToTool(callId, text12, isError) {
96660
- this.deps.send({ type: "voice:cd:respondToTool", callId, text: text12, isError });
97186
+ respondToTool(callId, text13, isError) {
97187
+ this.deps.send({ type: "voice:cd:respondToTool", callId, text: text13, isError });
96661
97188
  }
96662
- speakNow(text12) {
96663
- this.deps.send({ type: "voice:cd:speak", text: text12 });
97189
+ speakNow(text13) {
97190
+ this.deps.send({ type: "voice:cd:speak", text: text13 });
96664
97191
  }
96665
97192
  close() {
96666
97193
  this.settleConnect(new Error("closed before connect"));
@@ -96680,11 +97207,11 @@ var ClientProxyProvider = class {
96680
97207
  handleToolCall(inv) {
96681
97208
  this.cb.onToolCall(inv);
96682
97209
  }
96683
- handleTranscript(speaker, text12) {
97210
+ handleTranscript(speaker, text13) {
96684
97211
  if (speaker === "user")
96685
- this.cb.onUserTranscript(text12);
97212
+ this.cb.onUserTranscript(text13);
96686
97213
  else
96687
- this.cb.onAgentTranscript(text12);
97214
+ this.cb.onAgentTranscript(text13);
96688
97215
  }
96689
97216
  handleProviderEvent(event, message) {
96690
97217
  switch (event) {
@@ -96916,10 +97443,10 @@ var VoiceControl = class {
96916
97443
  * injection as a user turn and comments on every slide change, which is exactly the
96917
97444
  * running commentary the silence contract forbids.
96918
97445
  */
96919
- injectPresentContext(text12) {
97446
+ injectPresentContext(text13) {
96920
97447
  if (this.ended)
96921
97448
  return;
96922
- this.deps.send({ type: "voice:cd:inject", text: text12, turnComplete: false });
97449
+ this.deps.send({ type: "voice:cd:inject", text: text13, turnComplete: false });
96923
97450
  }
96924
97451
  /**
96925
97452
  * A typed question from the phone, routed as if it had been spoken.
@@ -96927,33 +97454,33 @@ var VoiceControl = class {
96927
97454
  * This one DOES form a turn (turnComplete left at its default): unlike slide context,
96928
97455
  * the presenter is deliberately addressing the assistant and is waiting for an answer.
96929
97456
  */
96930
- askPresent(text12) {
97457
+ askPresent(text13) {
96931
97458
  if (this.ended)
96932
97459
  return;
96933
- this.deps.send({ type: "voice:cd:inject", text: `(The presenter asks you directly: ${text12})` });
97460
+ this.deps.send({ type: "voice:cd:inject", text: `(The presenter asks you directly: ${text13})` });
96934
97461
  }
96935
97462
  async runPresentControl(callId, name, args) {
96936
- let text12 = "";
97463
+ let text13 = "";
96937
97464
  let silent = false;
96938
97465
  let clearPlayback = false;
96939
97466
  let isError = false;
96940
97467
  try {
96941
97468
  const r = await this.deps.execPresentControl(name, args);
96942
- text12 = r.text;
97469
+ text13 = r.text;
96943
97470
  silent = r.silent;
96944
97471
  clearPlayback = r.clearPlayback === true;
96945
97472
  } catch (e) {
96946
- text12 = e instanceof Error ? e.message : "presentation action failed";
97473
+ text13 = e instanceof Error ? e.message : "presentation action failed";
96947
97474
  isError = true;
96948
97475
  }
96949
- log(`voice: present-control ${name} \u2192 ${isError ? "ERROR " : ""}${text12}`);
96950
- recordEvent("tool_result", { name, text: text12, isError });
97476
+ log(`voice: present-control ${name} \u2192 ${isError ? "ERROR " : ""}${text13}`);
97477
+ recordEvent("tool_result", { name, text: text13, isError });
96951
97478
  if (clearPlayback && !isError)
96952
97479
  this.deps.send({ type: "voice:clearPlayback" });
96953
- this.deps.send({ type: "voice:cd:respondToTool", callId, text: text12, isError, silent: silent && !isError });
97480
+ this.deps.send({ type: "voice:cd:respondToTool", callId, text: text13, isError, silent: silent && !isError });
96954
97481
  }
96955
97482
  async runScreenControl(callId, name, args) {
96956
- let text12;
97483
+ let text13;
96957
97484
  let isError = false;
96958
97485
  let finished = false, nudges = 0;
96959
97486
  let slowTimer = null;
@@ -96969,18 +97496,18 @@ var VoiceControl = class {
96969
97496
  };
96970
97497
  scheduleNudge(2e4);
96971
97498
  try {
96972
- text12 = await this.deps.execScreenControl(name, args);
97499
+ text13 = await this.deps.execScreenControl(name, args);
96973
97500
  } catch (e) {
96974
- text12 = e instanceof Error ? e.message : "screen action failed";
97501
+ text13 = e instanceof Error ? e.message : "screen action failed";
96975
97502
  isError = true;
96976
97503
  } finally {
96977
97504
  finished = true;
96978
97505
  if (slowTimer)
96979
97506
  clearTimeout(slowTimer);
96980
97507
  }
96981
- log(`voice: screen-control ${name} \u2192 ${isError ? "ERROR " : ""}${text12}`);
96982
- recordEvent("tool_result", { name, text: text12, isError });
96983
- this.deps.send({ type: "voice:cd:respondToTool", callId, text: text12, isError });
97508
+ log(`voice: screen-control ${name} \u2192 ${isError ? "ERROR " : ""}${text13}`);
97509
+ recordEvent("tool_result", { name, text: text13, isError });
97510
+ this.deps.send({ type: "voice:cd:respondToTool", callId, text: text13, isError });
96984
97511
  }
96985
97512
  async start(msg) {
96986
97513
  if (this.starting)
@@ -97118,13 +97645,13 @@ ${priorText}` : "";
97118
97645
  const callbacks = {
97119
97646
  onOutputAudio: () => {
97120
97647
  },
97121
- onUserTranscript: (text12) => {
97122
- orchestrator.noteTranscript("user", text12);
97123
- this.deps.send({ type: "voice:transcript", speaker: "user", text: text12 });
97648
+ onUserTranscript: (text13) => {
97649
+ orchestrator.noteTranscript("user", text13);
97650
+ this.deps.send({ type: "voice:transcript", speaker: "user", text: text13 });
97124
97651
  },
97125
- onAgentTranscript: (text12) => {
97126
- orchestrator.noteTranscript("assistant", text12);
97127
- this.deps.send({ type: "voice:transcript", speaker: "agent", text: text12 });
97652
+ onAgentTranscript: (text13) => {
97653
+ orchestrator.noteTranscript("assistant", text13);
97654
+ this.deps.send({ type: "voice:transcript", speaker: "agent", text: text13 });
97128
97655
  },
97129
97656
  onToolCall: (inv) => orchestrator.handleToolCall(inv),
97130
97657
  onInterruption: () => this.deps.send({ type: "voice:clearPlayback" }),
@@ -98490,7 +99017,7 @@ var fmtTime = (sec) => {
98490
99017
  cs -= s * 100;
98491
99018
  return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(cs).padStart(2, "0")}`;
98492
99019
  };
98493
- var escapeAss = (text12) => text12.replace(/\\/g, "\\\\").replace(/\{/g, "(").replace(/\}/g, ")").replace(/\r?\n/g, "\\N");
99020
+ var escapeAss = (text13) => text13.replace(/\\/g, "\\\\").replace(/\{/g, "(").replace(/\}/g, ")").replace(/\r?\n/g, "\\N");
98494
99021
  function buildAssSubtitles(cues, style, width, height) {
98495
99022
  const base = Math.round(height / 14);
98496
99023
  const styles = style === "tiktok-bold" ? `Style: Cap,Arial Black,${base},&H00FFFFFF,&H000000FF,&H00000000,&H96000000,-1,0,0,0,100,100,0,0,1,${Math.max(2, Math.round(base / 9))},0,2,60,60,${Math.round(height / 5)},1` : `Style: Cap,Arial,${Math.round(base * 0.72)},&H00FFFFFF,&H000000FF,&H00000000,&H64000000,-1,0,0,0,100,100,0,0,1,2,1,2,40,40,${Math.round(height / 12)},1`;
@@ -99101,7 +99628,7 @@ async function cloneVoiceElevenLabs(name, samples, cfg2, opts) {
99101
99628
  throw new Error("ElevenLabs clone returned no voice_id.");
99102
99629
  return { voiceId: j.voice_id, name: j.name ?? name };
99103
99630
  }
99104
- async function synthesizeSpeechElevenLabs(text12, voiceId, cfg2, opts) {
99631
+ async function synthesizeSpeechElevenLabs(text13, voiceId, cfg2, opts) {
99105
99632
  const base = (cfg2.baseUrl?.trim() || BASE_URL).replace(/\/+$/, "");
99106
99633
  const settings = {};
99107
99634
  if (cfg2.stability != null)
@@ -99115,7 +99642,7 @@ async function synthesizeSpeechElevenLabs(text12, voiceId, cfg2, opts) {
99115
99642
  res = await fetch(`${base}/v1/text-to-speech/${encodeURIComponent(voiceId)}?output_format=mp3_44100_128`, {
99116
99643
  method: "POST",
99117
99644
  headers: { "xi-api-key": cfg2.apiKey, "Content-Type": "application/json" },
99118
- body: JSON.stringify({ text: text12, model_id: cfg2.model?.trim() || DEFAULT_TTS_MODEL, ...Object.keys(settings).length ? { voice_settings: settings } : {} }),
99645
+ body: JSON.stringify({ text: text13, model_id: cfg2.model?.trim() || DEFAULT_TTS_MODEL, ...Object.keys(settings).length ? { voice_settings: settings } : {} }),
99119
99646
  signal: opts?.signal ?? AbortSignal.timeout(12e4)
99120
99647
  });
99121
99648
  } catch (e) {
@@ -100518,11 +101045,11 @@ async function geminiJson(opts) {
100518
101045
  const json3 = await res.json().catch(() => ({}));
100519
101046
  if (!res.ok)
100520
101047
  throw new Error(json3.error?.message ?? `Gemini request failed (${res.status})`);
100521
- const text12 = json3.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
101048
+ const text13 = json3.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
100522
101049
  try {
100523
- return JSON.parse(text12);
101050
+ return JSON.parse(text13);
100524
101051
  } catch {
100525
- throw new Error(`Gemini returned non-JSON transcription output: ${text12.slice(0, 200)}`);
101052
+ throw new Error(`Gemini returned non-JSON transcription output: ${text13.slice(0, 200)}`);
100526
101053
  }
100527
101054
  }
100528
101055
  async function transcribeMedia(opts) {
@@ -100656,7 +101183,7 @@ function resolveAvatarCreds(snap, keyFile, envKey2) {
100656
101183
  function heygenCreds() {
100657
101184
  return resolveAvatarCreds(readDaemonAgentSettings(), readDaemonKeyFile("heygen.key"), process.env.HEYGEN_API_KEY ?? null);
100658
101185
  }
100659
- var NO_AVATAR_CREDS = "Avatar videos need either a HeyGen API key (add it in Settings, or set HEYGEN_API_KEY) or a signed-in Tesseract account for the free tier.";
101186
+ var NO_AVATAR_CREDS = "Avatar videos need a HeyGen API key \u2014 add one in Settings, or set HEYGEN_API_KEY. The free tier covers this only when you are signed in AND this server has HeyGen access; one of those is missing here.";
100660
101187
  function readDaemonKeyFile(name) {
100661
101188
  try {
100662
101189
  const home = tesseractHome();
@@ -100875,12 +101402,12 @@ function createDaemonVideoEditRunner() {
100875
101402
  return { ok: false, error: err(e) };
100876
101403
  }
100877
101404
  },
100878
- async synthesizeSpeech(text12, voiceId, outDir, opts) {
101405
+ async synthesizeSpeech(text13, voiceId, outDir, opts) {
100879
101406
  const apiKey = elevenLabsKey();
100880
101407
  if (!apiKey)
100881
101408
  return { ok: false, error: "Text-to-speech needs an ElevenLabs key (Settings \u2192 Voice)." };
100882
101409
  try {
100883
- const audio = await synthesizeSpeechElevenLabs(text12, voiceId, { apiKey, stability: opts?.stability, similarityBoost: opts?.similarityBoost, speed: opts?.speed });
101410
+ const audio = await synthesizeSpeechElevenLabs(text13, voiceId, { apiKey, stability: opts?.stability, similarityBoost: opts?.similarityBoost, speed: opts?.speed });
100884
101411
  const dir2 = outDir ?? join48(musicDir2(), randomUUID24());
100885
101412
  await mkdir9(dir2, { recursive: true });
100886
101413
  const path6 = join48(dir2, "speech.mp3");
@@ -101009,8 +101536,8 @@ function createDaemonSupportSubmit() {
101009
101536
  signal: AbortSignal.timeout(3e4)
101010
101537
  });
101011
101538
  if (!res.ok) {
101012
- const text12 = await res.text().catch(() => "");
101013
- throw new Error(`Report submission failed (${res.status}). ${text12.slice(0, 200)}`);
101539
+ const text13 = await res.text().catch(() => "");
101540
+ throw new Error(`Report submission failed (${res.status}). ${text13.slice(0, 200)}`);
101014
101541
  }
101015
101542
  const body = await res.json();
101016
101543
  if (!body.issueUrl || typeof body.issueNumber !== "number")
@@ -101724,28 +102251,28 @@ async function execPresentControl(name, args) {
101724
102251
  // ../../packages/host-core/dist/app-control-daemon.js
101725
102252
  var DAEMON_TABLE = {
101726
102253
  "present.state": async () => {
101727
- const { text: text12 } = await execPresentControl("deck_overview", {});
101728
- return text12;
102254
+ const { text: text13 } = await execPresentControl("deck_overview", {});
102255
+ return text13;
101729
102256
  },
101730
102257
  "present.deck": async () => {
101731
- const { text: text12 } = await execPresentControl("deck_overview", {});
101732
- return text12;
102258
+ const { text: text13 } = await execPresentControl("deck_overview", {});
102259
+ return text13;
101733
102260
  },
101734
102261
  "present.find_slide": async (input) => {
101735
- const { text: text12 } = await execPresentControl("find_slide", { query: String(input.query ?? "") });
101736
- return text12;
102262
+ const { text: text13 } = await execPresentControl("find_slide", { query: String(input.query ?? "") });
102263
+ return text13;
101737
102264
  },
101738
102265
  "present.goto": async (input) => {
101739
- const { text: text12 } = await execPresentControl("goto_slide", { slide: Number(input.slide) });
101740
- return text12;
102266
+ const { text: text13 } = await execPresentControl("goto_slide", { slide: Number(input.slide) });
102267
+ return text13;
101741
102268
  },
101742
102269
  "present.set_zoom": async (input) => {
101743
- const { text: text12 } = await execPresentControl("set_zoom", { level: Number(input.level) });
101744
- return text12;
102270
+ const { text: text13 } = await execPresentControl("set_zoom", { level: Number(input.level) });
102271
+ return text13;
101745
102272
  },
101746
102273
  "present.set_blank": async (input) => {
101747
- const { text: text12 } = await execPresentControl("set_blank", { blanked: input.blanked === true });
101748
- return text12;
102274
+ const { text: text13 } = await execPresentControl("set_blank", { blanked: input.blanked === true });
102275
+ return text13;
101749
102276
  },
101750
102277
  "present.set_ai": async (input) => {
101751
102278
  if (!isLive())
@@ -102306,7 +102833,10 @@ function refreshDaemonScheduling(b) {
102306
102833
  // and a 3am fire would act on a switch the user changed days ago. The snapshot is
102307
102834
  // already read fresh per turn everywhere else; this reads it fresh per fire.
102308
102835
  isUnattended: () => readDaemonAgentSettings().unattended === true,
102309
- isVerified: () => readDaemonAgentSettings().reinforcedVerification === true
102836
+ isVerified: () => readDaemonAgentSettings().reinforcedVerification === true,
102837
+ // The person's permission rules and hooks, for a fire that OPENS a conversation: it has no earlier turn
102838
+ // to replay them from, and it is the run nobody is watching. Read per fire, like the switches above.
102839
+ settings: () => readDaemonAgentSettings().settings
102310
102840
  });
102311
102841
  }
102312
102842
  function daemonWorkspaceDir() {
@@ -102596,8 +103126,8 @@ async function linuxFavorites() {
102596
103126
  ];
102597
103127
  for (const file2 of candidates) {
102598
103128
  try {
102599
- const text12 = await readFile13(file2, "utf8");
102600
- const favs = finalize2(parseGtkBookmarks(text12));
103129
+ const text13 = await readFile13(file2, "utf8");
103130
+ const favs = finalize2(parseGtkBookmarks(text13));
102601
103131
  if (favs.length)
102602
103132
  return favs;
102603
103133
  } catch {
@@ -102605,9 +103135,9 @@ async function linuxFavorites() {
102605
103135
  }
102606
103136
  return [];
102607
103137
  }
102608
- function parseGtkBookmarks(text12) {
103138
+ function parseGtkBookmarks(text13) {
102609
103139
  const out = [];
102610
- for (const raw of text12.split(/\r?\n/)) {
103140
+ for (const raw of text13.split(/\r?\n/)) {
102611
103141
  const line = raw.trim();
102612
103142
  if (!line.startsWith("file://"))
102613
103143
  continue;
@@ -108807,8 +109337,8 @@ function safeJson(value) {
108807
109337
  return "";
108808
109338
  }
108809
109339
  }
108810
- function firstLine2(text12) {
108811
- return text12.split("\n").map((l) => l.trim()).find(Boolean)?.slice(0, 80) ?? "";
109340
+ function firstLine2(text13) {
109341
+ return text13.split("\n").map((l) => l.trim()).find(Boolean)?.slice(0, 80) ?? "";
108812
109342
  }
108813
109343
  function queryMatcher(query2) {
108814
109344
  const q = query2.trim();
@@ -108816,11 +109346,11 @@ function queryMatcher(query2) {
108816
109346
  return null;
108817
109347
  return new RegExp(q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "iu");
108818
109348
  }
108819
- function snippetAt(text12, at, length, radius = 60) {
109349
+ function snippetAt(text13, at, length, radius = 60) {
108820
109350
  const from = Math.max(0, at - radius);
108821
- const to = Math.min(text12.length, at + length + radius);
108822
- const body = text12.slice(from, to).replace(/\s+/g, " ").trim();
108823
- return `${from > 0 ? "\u2026" : ""}${body}${to < text12.length ? "\u2026" : ""}`;
109351
+ const to = Math.min(text13.length, at + length + radius);
109352
+ const body = text13.slice(from, to).replace(/\s+/g, " ").trim();
109353
+ return `${from > 0 ? "\u2026" : ""}${body}${to < text13.length ? "\u2026" : ""}`;
108824
109354
  }
108825
109355
 
108826
109356
  // ../../packages/host-core/dist/session-search/text-cache.js
@@ -108876,10 +109406,10 @@ function createTextCache(baseDir) {
108876
109406
  const nl = buf.lastIndexOf(10, end2 - 1);
108877
109407
  end2 = nl >= at ? nl + 1 : Math.max(end2, buf.indexOf(10, end2) + 1 || buf.length);
108878
109408
  }
108879
- const { text: text12, bytes: bytes3 } = completeLines(buf.subarray(at, end2));
109409
+ const { text: text13, bytes: bytes3 } = completeLines(buf.subarray(at, end2));
108880
109410
  if (bytes3 === 0)
108881
109411
  break;
108882
- const out = extractSession(text12, { ...hints, text: "" });
109412
+ const out = extractSession(text13, { ...hints, text: "" });
108883
109413
  if (out.text)
108884
109414
  texts.push(out.text);
108885
109415
  hints = out;
@@ -108887,9 +109417,9 @@ function createTextCache(baseDir) {
108887
109417
  }
108888
109418
  return { extraction: { ...hints, text: "", consumed: at }, texts, bytes: at };
108889
109419
  }
108890
- async function writeText(file2, text12) {
109420
+ async function writeText(file2, text13) {
108891
109421
  const tmp = `${file2}.${process.pid}.${Date.now()}.tmp`;
108892
- await writeFile14(tmp, text12, { mode: 384 });
109422
+ await writeFile14(tmp, text13, { mode: 384 });
108893
109423
  await rename5(tmp, file2);
108894
109424
  }
108895
109425
  async function extractWhole(file2, entry) {
@@ -108916,9 +109446,9 @@ function createTextCache(baseDir) {
108916
109446
  return null;
108917
109447
  }
108918
109448
  const main2 = extractChunked(raw, fromArchive.extraction);
108919
- const text12 = [...fromArchive.texts, ...main2.texts].join("\n");
109449
+ const text13 = [...fromArchive.texts, ...main2.texts].join("\n");
108920
109450
  const textFile = entry?.textFile ?? textFileFor(file2.path);
108921
- await writeText(join65(dir2, textFile), text12);
109451
+ await writeText(join65(dir2, textFile), text13);
108922
109452
  const { text: _t, consumed: _c, ...hints } = main2.extraction;
108923
109453
  return {
108924
109454
  path: file2.path,
@@ -109100,8 +109630,8 @@ function createSessionSearch(deps) {
109100
109630
  continue;
109101
109631
  const index = indexed.get(id);
109102
109632
  const title = entry.hints.customTitle ?? index?.title ?? entry.hints.firstPrompt ?? entry.hints.aiTitle;
109103
- const text12 = await cache6.text(entry);
109104
- const inBody = matcher.exec(text12);
109633
+ const text13 = await cache6.text(entry);
109634
+ const inBody = matcher.exec(text13);
109105
109635
  const inTitle = title ? matcher.test(title) : false;
109106
109636
  if (!inBody && !inTitle)
109107
109637
  continue;
@@ -109113,7 +109643,7 @@ function createSessionSearch(deps) {
109113
109643
  lastModified: entry.hints.lastTimestamp ?? entry.mtimeMs,
109114
109644
  ...cwd ? { cwd } : {},
109115
109645
  engineId,
109116
- ...inBody ? { snippet: snippetAt(text12, inBody.index, inBody[0].length) } : {}
109646
+ ...inBody ? { snippet: snippetAt(text13, inBody.index, inBody[0].length) } : {}
109117
109647
  };
109118
109648
  run9.hits.push(hit);
109119
109649
  found.push(hit);
@@ -111281,12 +111811,12 @@ var RdeskSessionManager = class {
111281
111811
  this.deps.sendControl(sessionId, s.viewer.clientId, { type: "rdesk:muted", mic: !!mute.mic, systemAudio: !!mute.systemAudio });
111282
111812
  }
111283
111813
  /** Host operator → viewer: send an in-session chat line. No-op if the session is gone. */
111284
- sendChat(sessionId, text12) {
111814
+ sendChat(sessionId, text13) {
111285
111815
  const s = this.sessions.get(sessionId);
111286
111816
  if (!s)
111287
111817
  return;
111288
- this.deps.sendControl(sessionId, s.viewer.clientId, { type: "rdesk:chat", text: text12, from: "host", at: Date.now() });
111289
- this.audit({ event: "chat", sessionId, detail: `host: ${text12.slice(0, 80)}` });
111818
+ this.deps.sendControl(sessionId, s.viewer.clientId, { type: "rdesk:chat", text: text13, from: "host", at: Date.now() });
111819
+ this.audit({ event: "chat", sessionId, detail: `host: ${text13.slice(0, 80)}` });
111290
111820
  }
111291
111821
  /** Switch the captured display. */
111292
111822
  async setMonitor(sessionId, monitorId) {
@@ -111318,9 +111848,9 @@ var RdeskSessionManager = class {
111318
111848
  return this.sendKeys(sessionId, msg.combo);
111319
111849
  if (msg.type === "rdesk:chat") {
111320
111850
  const s = this.sessions.get(sessionId);
111321
- const text12 = msg.text.slice(0, 4e3);
111322
- this.deps.onChat?.({ sessionId, viewerName: s?.viewer.name, text: text12 });
111323
- this.audit({ event: "chat", sessionId, detail: text12.slice(0, 80) });
111851
+ const text13 = msg.text.slice(0, 4e3);
111852
+ this.deps.onChat?.({ sessionId, viewerName: s?.viewer.name, text: text13 });
111853
+ this.audit({ event: "chat", sessionId, detail: text13.slice(0, 80) });
111324
111854
  return;
111325
111855
  }
111326
111856
  if (msg.type === "rdesk:av") {
@@ -111963,8 +112493,8 @@ function waylandInputRunner(p, screen) {
111963
112493
  async scroll(o) {
111964
112494
  p.pointerAxis(o.dx ?? 0, o.dy ?? 0);
111965
112495
  },
111966
- async typeText(text12) {
111967
- for (const ch of text12) {
112496
+ async typeText(text13) {
112497
+ for (const ch of text13) {
111968
112498
  const ks = charKeysym(ch);
111969
112499
  if (!ks)
111970
112500
  continue;
@@ -112138,11 +112668,11 @@ var TesseractEngine = class {
112138
112668
  onInput: (ev) => void this.injector?.apply(ev),
112139
112669
  // Clipboard sync (Phase 6): write the viewer's clipboard onto the host so ⌘V pastes
112140
112670
  // into the remote machine. Gated by control access (view-only never writes).
112141
- onClipboard: (text12) => {
112671
+ onClipboard: (text13) => {
112142
112672
  if (this.access === "control") {
112143
112673
  const el2 = electronModule();
112144
112674
  try {
112145
- el2?.clipboard?.writeText(text12);
112675
+ el2?.clipboard?.writeText(text13);
112146
112676
  } catch {
112147
112677
  }
112148
112678
  }
@@ -114560,8 +115090,8 @@ async function startLocalServer(opts) {
114560
115090
  if (!vc || !vc.isPresentMode)
114561
115091
  return null;
114562
115092
  return {
114563
- injectContext: (text12) => vc.injectPresentContext(text12),
114564
- ask: (text12) => vc.askPresent(text12)
115093
+ injectContext: (text13) => vc.injectPresentContext(text13),
115094
+ ask: (text13) => vc.askPresent(text13)
114565
115095
  };
114566
115096
  });
114567
115097
  const unregisterSink = registerAgentSink((msg, origin2) => {
@@ -115685,7 +116215,7 @@ async function getPeerIdentityPub(opts) {
115685
116215
  }
115686
116216
 
115687
116217
  // ../../packages/host-core/dist/mesh/node.js
115688
- import { createSocket as createSocket2 } from "node:dgram";
116218
+ import { createSocket } from "node:dgram";
115689
116219
  import { createRequire as createRequire4 } from "node:module";
115690
116220
  init_credentials();
115691
116221
 
@@ -116212,6 +116742,8 @@ async function netdNodeStatus(meshId) {
116212
116742
  relayed: strs("relayed"),
116213
116743
  policyEpoch: num2("policyEpoch"),
116214
116744
  revoked: b.revoked === true,
116745
+ lan: b.lan === true,
116746
+ lanError: typeof b.lanError === "string" ? b.lanError : null,
116215
116747
  bundleStaleInMs: typeof b.bundleStaleInMs === "number" ? b.bundleStaleInMs : null,
116216
116748
  lastError: typeof b.lastError === "string" ? b.lastError : null,
116217
116749
  localStaticPub: Uint8Array.from(Buffer.from(pubHex, "hex")),
@@ -116394,6 +116926,15 @@ var NetdEngine = class {
116394
116926
  get srflx() {
116395
116927
  return this.#snapshot?.srflx ?? null;
116396
116928
  }
116929
+ /** Whether the HELPER's LAN discovery joined the multicast group — it owns that socket
116930
+ * now, so this is the only honest source for it. */
116931
+ get lan() {
116932
+ return this.#snapshot?.lan === true;
116933
+ }
116934
+ /** Why LAN discovery is off, when it is — the helper records the step that failed. */
116935
+ get lanError() {
116936
+ return this.#snapshot?.lanError ?? null;
116937
+ }
116397
116938
  /** The same over IPv6, advertised alongside rather than instead. */
116398
116939
  get srflx6() {
116399
116940
  return this.#snapshot?.srflx6 ?? null;
@@ -116489,127 +117030,6 @@ ${ts}`);
116489
117030
  }
116490
117031
  }
116491
117032
 
116492
- // ../../packages/host-core/dist/mesh/lan.js
116493
- import { createSocket } from "node:dgram";
116494
- var MAGIC = Buffer.from("TSRL");
116495
- var VERSION2 = 1;
116496
- var LAN_GROUP_V4 = "224.0.0.251";
116497
- var LAN_PORT = 5353;
116498
- var BEACON_LEN = 4 + 1 + 3 + 32 + 32 + 2 + 8;
116499
- var ANNOUNCE_MS = 5e3;
116500
- function encodeBeacon(b) {
116501
- const out = Buffer.alloc(BEACON_LEN);
116502
- MAGIC.copy(out, 0);
116503
- out[4] = VERSION2;
116504
- b.genesisFp.copy(out, 8, 0, 32);
116505
- b.staticPub.copy(out, 40, 0, 32);
116506
- out.writeUInt16BE(b.port, 72);
116507
- out.writeUInt32BE(Math.floor(b.epoch / 4294967296), 74);
116508
- out.writeUInt32BE(b.epoch >>> 0, 78);
116509
- return out;
116510
- }
116511
- function parseBeacon(buf) {
116512
- if (buf.length !== BEACON_LEN)
116513
- return null;
116514
- if (!buf.subarray(0, 4).equals(MAGIC))
116515
- return null;
116516
- if (buf[4] !== VERSION2)
116517
- return null;
116518
- if (buf[5] !== 0 || buf[6] !== 0 || buf[7] !== 0)
116519
- return null;
116520
- const port = buf.readUInt16BE(72);
116521
- if (port < 1)
116522
- return null;
116523
- const epoch2 = buf.readUInt32BE(74) * 4294967296 + buf.readUInt32BE(78);
116524
- return {
116525
- genesisFp: buf.subarray(8, 40),
116526
- staticPub: buf.subarray(40, 72),
116527
- port,
116528
- epoch: epoch2
116529
- };
116530
- }
116531
- var LanDiscovery = class {
116532
- #sock = null;
116533
- #timer = null;
116534
- #joined = false;
116535
- #opts;
116536
- constructor(opts) {
116537
- this.#opts = opts;
116538
- }
116539
- get active() {
116540
- return this.#joined;
116541
- }
116542
- start() {
116543
- if (this.#sock)
116544
- return;
116545
- const sock = createSocket({ type: "udp4", reuseAddr: true });
116546
- sock.on("error", () => {
116547
- this.stop();
116548
- });
116549
- sock.on("message", (msg, rinfo) => this.#onBeacon(msg, rinfo.address));
116550
- sock.bind(LAN_PORT, () => {
116551
- try {
116552
- sock.addMembership(LAN_GROUP_V4);
116553
- sock.setMulticastLoopback(true);
116554
- sock.setMulticastTTL(1);
116555
- this.#joined = true;
116556
- this.#announce();
116557
- } catch {
116558
- this.stop();
116559
- }
116560
- });
116561
- sock.unref?.();
116562
- this.#sock = sock;
116563
- this.#timer = setInterval(() => this.#announce(), ANNOUNCE_MS);
116564
- this.#timer.unref?.();
116565
- }
116566
- stop() {
116567
- if (this.#timer) {
116568
- clearInterval(this.#timer);
116569
- this.#timer = null;
116570
- }
116571
- if (this.#sock) {
116572
- try {
116573
- if (this.#joined)
116574
- this.#sock.dropMembership(LAN_GROUP_V4);
116575
- } catch {
116576
- }
116577
- try {
116578
- this.#sock.close();
116579
- } catch {
116580
- }
116581
- }
116582
- this.#sock = null;
116583
- this.#joined = false;
116584
- }
116585
- #announce() {
116586
- const port = this.#opts.port();
116587
- if (!this.#sock || !this.#joined || port === null)
116588
- return;
116589
- const frame = encodeBeacon({
116590
- genesisFp: this.#opts.genesisFp,
116591
- staticPub: this.#opts.staticPub,
116592
- port,
116593
- epoch: this.#opts.epoch()
116594
- });
116595
- try {
116596
- this.#sock.send(frame, LAN_PORT, LAN_GROUP_V4, () => {
116597
- });
116598
- } catch {
116599
- }
116600
- }
116601
- #onBeacon(msg, address) {
116602
- const b = parseBeacon(msg);
116603
- if (!b)
116604
- return;
116605
- if (!b.genesisFp.equals(this.#opts.genesisFp))
116606
- return;
116607
- if (b.staticPub.equals(this.#opts.staticPub))
116608
- return;
116609
- this.#opts.onPeer(Buffer.from(b.staticPub), address, b.port, b.epoch);
116610
- }
116611
- };
116612
-
116613
117033
  // ../../packages/host-core/dist/mesh/endpoints.js
116614
117034
  function inRange(range, addr) {
116615
117035
  if (addr.length !== range.addr.length)
@@ -116886,7 +117306,6 @@ var MeshNode = class {
116886
117306
  * can be offered a choice that has somewhere to go.
116887
117307
  */
116888
117308
  #exitNodes = /* @__PURE__ */ new Map();
116889
- #lan = null;
116890
117309
  #offRejoin = null;
116891
117310
  #statusTimer = null;
116892
117311
  /**
@@ -116936,7 +117355,8 @@ var MeshNode = class {
116936
117355
  enrolled: this.#identity !== null || this.#loadIdentity() !== null,
116937
117356
  running: this.#engine !== null,
116938
117357
  port: this.#port,
116939
- lan: this.#lan?.active ?? false,
117358
+ lan: this.#engine instanceof NetdEngine ? this.#engine.lan : false,
117359
+ lanError: this.#engine instanceof NetdEngine ? this.#engine.lanError : null,
116940
117360
  reachableAt: this.#engine instanceof NetdEngine ? [this.#engine.mapped, this.#engine.srflx, this.#engine.srflx6].filter((x) => x !== null).join(", ") || null : null,
116941
117361
  // Only the helper publishes names: writing the machine's name table needs root, and
116942
117362
  // the roster comes from the engine the helper runs. An in-process engine has a roster
@@ -117024,7 +117444,6 @@ var MeshNode = class {
117024
117444
  onUp: (port) => {
117025
117445
  this.#port = port;
117026
117446
  this.#join();
117027
- this.#startLan();
117028
117447
  this.#publishStatus();
117029
117448
  }
117030
117449
  });
@@ -117051,7 +117470,6 @@ var MeshNode = class {
117051
117470
  this.#identity = id;
117052
117471
  if (!inNetd) {
117053
117472
  this.#openSocket();
117054
- this.#startLan();
117055
117473
  }
117056
117474
  this.#offRejoin = onMeshRejoin(() => this.#join());
117057
117475
  this.#publishStatus();
@@ -117096,11 +117514,6 @@ var MeshNode = class {
117096
117514
  this.#published = false;
117097
117515
  }
117098
117516
  this.#runningMeshDir = null;
117099
- try {
117100
- this.#lan?.stop();
117101
- } catch {
117102
- }
117103
- this.#lan = null;
117104
117517
  try {
117105
117518
  this.#sock?.close();
117106
117519
  } catch {
@@ -117141,7 +117554,7 @@ var MeshNode = class {
117141
117554
  return Number.isInteger(port) && port > 0 ? { host: ep.slice(0, i), port } : null;
117142
117555
  }
117143
117556
  #openSocket() {
117144
- const sock = createSocket2({ type: "udp6", ipv6Only: false, reuseAddr: true });
117557
+ const sock = createSocket({ type: "udp6", ipv6Only: false, reuseAddr: true });
117145
117558
  sock.on("message", (msg, rinfo) => {
117146
117559
  if (!this.#engine)
117147
117560
  return;
@@ -117166,54 +117579,20 @@ var MeshNode = class {
117166
117579
  sock.unref?.();
117167
117580
  this.#sock = sock;
117168
117581
  }
117169
- /** LAN discovery: the path that needs neither the relay nor a hole punch.
117582
+ /* LAN discovery used to live here, as a multicast beacon in this process
117583
+ * (`./lan.ts`). It has MOVED INTO THE HELPER (`crates/tsmesh-netd/src/lan.rs`,
117584
+ * frame in `tsmesh_proto::lan`).
117170
117585
  *
117171
- * Started alongside the socket, and deliberately independent of the control link —
117172
- * two laptops on the same Wi-Fi with the internet down must still find each other,
117173
- * which is the whole demonstration that the overlay is not relay-dependent. */
117174
- #startLan() {
117175
- const engine = this.#engine;
117176
- if (!engine)
117177
- return;
117178
- try {
117179
- this.#lan = new LanDiscovery({
117180
- genesisFp: Buffer.from(engine.meshId(), "hex"),
117181
- staticPub: engine.localStaticPub(),
117182
- port: () => this.#port,
117183
- epoch: () => engine.policyEpoch(),
117184
- onPeer: (staticPub, address, port, epoch2) => this.#learnLanPeer(staticPub, address, port, epoch2)
117185
- });
117186
- this.#lan.start();
117187
- } catch {
117188
- this.#lan = null;
117189
- }
117190
- }
117191
- /** A beacon arrived. Treated exactly like a relay hint — a candidate, not a fact.
117586
+ * It had to. The beacon only ran while this app was running, so a headless machine --
117587
+ * a server, the thing the network exists to reach -- could not find a peer sharing its
117588
+ * switch. And on the netd path the app was announcing a port held by a different
117589
+ * process entirely. The helper starts at boot with no app and owns the socket, so the
117590
+ * beacon belongs beside it.
117192
117591
  *
117193
- * Including its EPOCH. The beacon has always carried one (`lan.ts`: "port(2) | epoch(8)")
117194
- * for the same reason the relay hint does — so a peer that is behind can be brought up
117195
- * to date — and, like the relay hint's, it was thrown away here. Two laptops on one
117196
- * Wi-Fi with the internet down are the demonstration that the overlay is not
117197
- * relay-dependent; without this they would find each other and then never agree on who
117198
- * else exists. */
117199
- #learnLanPeer(staticPub, address, port, epoch2 = 0) {
117200
- const engine = this.#engine;
117201
- if (!engine)
117202
- return;
117203
- const hex6 = staticPub.toString("hex");
117204
- this.#offerBundleTo(hex6, epoch2);
117205
- try {
117206
- if (!this.#peers.has(hex6)) {
117207
- engine.addPeer(staticPub, 0, Buffer.alloc(16));
117208
- this.#peers.set(hex6, staticPub);
117209
- }
117210
- engine.addPath(staticPub, `${address}:${port}`, false);
117211
- engine.ensureHandshake(staticPub, Date.now());
117212
- this.#drain();
117213
- } catch (e) {
117214
- this.#lastError = e instanceof Error ? e.message : String(e);
117215
- }
117216
- }
117592
+ * Moved rather than duplicated: two implementations of a parser that reads
117593
+ * unauthenticated multicast from every device on the segment is the one risk
117594
+ * docs/MESH.md names as unacceptable.
117595
+ */
117217
117596
  #subscribe() {
117218
117597
  this.#unsubscribe = onMesh((m) => this.#onHint(m));
117219
117598
  }
@@ -117796,11 +118175,11 @@ async function call2(path6, init, auth = true) {
117796
118175
  } catch (e) {
117797
118176
  return fail3("network", e instanceof Error ? e.message : String(e));
117798
118177
  }
117799
- const text12 = await res.text();
118178
+ const text13 = await res.text();
117800
118179
  if (!res.ok) {
117801
- let detail = text12.slice(0, 500);
118180
+ let detail = text13.slice(0, 500);
117802
118181
  try {
117803
- const parsed = JSON.parse(text12);
118182
+ const parsed = JSON.parse(text13);
117804
118183
  if (parsed.message)
117805
118184
  detail = Array.isArray(parsed.message) ? parsed.message.join("; ") : parsed.message;
117806
118185
  } catch {
@@ -117808,7 +118187,7 @@ async function call2(path6, init, auth = true) {
117808
118187
  return fail3(res.status >= 500 ? "server" : "refused", detail);
117809
118188
  }
117810
118189
  try {
117811
- return { ok: true, data: text12 ? JSON.parse(text12) : {} };
118190
+ return { ok: true, data: text13 ? JSON.parse(text13) : {} };
117812
118191
  } catch {
117813
118192
  return fail3("server", "the server sent something that is not JSON");
117814
118193
  }
@@ -118010,18 +118389,18 @@ async function postJoin(origin, body) {
118010
118389
  } catch (e) {
118011
118390
  return fail3("network", e instanceof Error ? e.message : String(e));
118012
118391
  }
118013
- const text12 = await res.text();
118392
+ const text13 = await res.text();
118014
118393
  if (!res.ok) {
118015
- let detail = text12.slice(0, 500);
118394
+ let detail = text13.slice(0, 500);
118016
118395
  try {
118017
- const parsed = JSON.parse(text12);
118396
+ const parsed = JSON.parse(text13);
118018
118397
  if (parsed.message)
118019
118398
  detail = String(parsed.message);
118020
118399
  } catch {
118021
118400
  }
118022
118401
  return fail3(res.status >= 500 ? "server" : "refused", detail);
118023
118402
  }
118024
- return { ok: true, data: JSON.parse(text12) };
118403
+ return { ok: true, data: JSON.parse(text13) };
118025
118404
  }
118026
118405
  async function enrolWithCode(args) {
118027
118406
  return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }), resolveOrigin(args.origin), args.fromHostname);
@@ -118881,9 +119260,9 @@ import { pipeline } from "node:stream/promises";
118881
119260
 
118882
119261
  // ../../packages/host-core/dist/drops/crypto.js
118883
119262
  import { Transform } from "node:stream";
118884
- var MAGIC2 = Buffer.from("TSD1", "ascii");
119263
+ var MAGIC = Buffer.from("TSD1", "ascii");
118885
119264
  var NONCE_BYTES = 12;
118886
- var HEADER_BYTES = MAGIC2.length + NONCE_BYTES;
119265
+ var HEADER_BYTES = MAGIC.length + NONCE_BYTES;
118887
119266
  var FRAME_BYTES = 1024 * 1024;
118888
119267
  var TAG_BYTES = 16;
118889
119268
  var MAX_FRAME_CIPHERTEXT = FRAME_BYTES + TAG_BYTES + 1024;
@@ -118964,9 +119343,9 @@ function decryptStream(source, keyRaw) {
118964
119343
  cb();
118965
119344
  return;
118966
119345
  }
118967
- if (!head.subarray(0, MAGIC2.length).equals(MAGIC2))
119346
+ if (!head.subarray(0, MAGIC.length).equals(MAGIC))
118968
119347
  throw new Error("not an encrypted drop");
118969
- base = Buffer.from(head.subarray(MAGIC2.length, HEADER_BYTES));
119348
+ base = Buffer.from(head.subarray(MAGIC.length, HEADER_BYTES));
118970
119349
  q.consume(HEADER_BYTES);
118971
119350
  key2 = await importKey(keyRaw);
118972
119351
  }
@@ -119136,7 +119515,7 @@ function createChatAgentRunner(deps) {
119136
119515
  const backend2 = await getSharedBackend();
119137
119516
  const clientRequestId = `chat-agent-${randomUUID32()}`;
119138
119517
  return new Promise((resolve16, reject) => {
119139
- let text12 = "";
119518
+ let text13 = "";
119140
119519
  let settled = false;
119141
119520
  const finish2 = (fn) => {
119142
119521
  if (settled)
@@ -119150,13 +119529,13 @@ function createChatAgentRunner(deps) {
119150
119529
  }
119151
119530
  fn();
119152
119531
  };
119153
- const timer2 = setTimeout(() => finish2(() => text12.trim() ? resolve16(text12) : reject(new Error("the agent took too long"))), TURN_TIMEOUT_MS);
119532
+ const timer2 = setTimeout(() => finish2(() => text13.trim() ? resolve16(text13) : reject(new Error("the agent took too long"))), TURN_TIMEOUT_MS);
119154
119533
  let posting = false;
119155
119534
  let lastSent = "";
119156
119535
  const tick = setInterval(() => {
119157
119536
  if (posting || !onPartial)
119158
119537
  return;
119159
- const snapshot4 = text12.trim();
119538
+ const snapshot4 = text13.trim();
119160
119539
  if (!snapshot4 || snapshot4 === lastSent)
119161
119540
  return;
119162
119541
  lastSent = snapshot4;
@@ -119172,9 +119551,9 @@ function createChatAgentRunner(deps) {
119172
119551
  return;
119173
119552
  if (m.type === "chat:stream") {
119174
119553
  if ((m.kind === "text" || m.kind === void 0) && m.delta)
119175
- text12 += m.delta;
119554
+ text13 += m.delta;
119176
119555
  } else if (m.type === "chat:complete") {
119177
- finish2(() => resolve16(text12));
119556
+ finish2(() => resolve16(text13));
119178
119557
  } else if (m.type === "chat:error") {
119179
119558
  finish2(() => reject(new Error(m.message ?? m.error ?? "the agent errored")));
119180
119559
  }
@@ -121334,7 +121713,7 @@ function backoff2() {
121334
121713
  var daemonClipboardSync = null;
121335
121714
  var REFRESH_INTERVAL_MS2 = 12 * 60 * 60 * 1e3;
121336
121715
  async function runDaemon(opts = {}) {
121337
- const VERSION3 = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
121716
+ const VERSION2 = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
121338
121717
  const TAG = opts.tag ?? "host-daemon";
121339
121718
  const KIND = opts.kind ?? "app";
121340
121719
  const log10 = (...args) => console.log(`[${TAG} ${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
@@ -121410,14 +121789,14 @@ async function runDaemon(opts = {}) {
121410
121789
  if (!ownsMachine)
121411
121790
  return;
121412
121791
  try {
121413
- writeFileSync44(statusFile2(), JSON.stringify({ pid: process.pid, startedAt: OUR_STARTED_AT, role: process.env.CCB_RDESK_ROLE ?? "host", kind: KIND, version: VERSION3, execPath: process.env.CCB_DAEMON_EXECPATH ?? process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), gui: daemonGuiTrayActive(), ...localInfo ?? {}, ...s }));
121792
+ writeFileSync44(statusFile2(), JSON.stringify({ pid: process.pid, startedAt: OUR_STARTED_AT, role: process.env.CCB_RDESK_ROLE ?? "host", kind: KIND, version: VERSION2, execPath: process.env.CCB_DAEMON_EXECPATH ?? process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), gui: daemonGuiTrayActive(), ...localInfo ?? {}, ...s }));
121414
121793
  chmodSync8(statusFile2(), 384);
121415
121794
  } catch {
121416
121795
  }
121417
121796
  }
121418
121797
  const standaloneViewer = process.env.CCB_RDESK_ROLE === "client";
121419
121798
  if (!standaloneViewer) {
121420
- registerSelfAsDaemon(VERSION3);
121799
+ registerSelfAsDaemon(VERSION2);
121421
121800
  if (KIND === "app") {
121422
121801
  try {
121423
121802
  await evictCliAgent(log10);
@@ -121625,7 +122004,7 @@ async function runDaemon(opts = {}) {
121625
122004
  unregisterSelfAsDaemon();
121626
122005
  process.exit(code);
121627
122006
  }
121628
- startSystemEvents({ version: VERSION3, log: (m) => log10(m) });
122007
+ startSystemEvents({ version: VERSION2, log: (m) => log10(m) });
121629
122008
  const mergedProcess = opts.merged === true;
121630
122009
  const deliberateMergedQuit = () => !!globalThis.__ccbDeliberateQuit;
121631
122010
  const onTermSignal = (sig) => {
@@ -121892,7 +122271,7 @@ async function runDaemon(opts = {}) {
121892
122271
  if (!creds3) {
121893
122272
  timer2 = setInterval(() => writeStatus2({ deviceId: deviceId3, connected: false, localOnly: true }), 6e4);
121894
122273
  writeStatus2({ deviceId: deviceId3, connected: false, localOnly: true });
121895
- log10(`started LOCAL-ONLY (no identity yet) \u2014 local :${localInfo?.localPort ?? "off"}, version ${VERSION3}; will keep trying`);
122274
+ log10(`started LOCAL-ONLY (no identity yet) \u2014 local :${localInfo?.localPort ?? "off"}, version ${VERSION2}; will keep trying`);
121896
122275
  const watch4 = watchForIdentity({ attempt: acquireIdentity, log: (m) => log10(m) });
121897
122276
  identityWatch = watch4;
121898
122277
  setDaemonIdentityReload(async () => {
@@ -122153,14 +122532,14 @@ async function runDaemon(opts = {}) {
122153
122532
  // On macOS the notification carries an inline REPLY box (hasReply) whose text is sent
122154
122533
  // straight back via sendChat — a full operator chat loop with no extra window. Off-mac (no
122155
122534
  // hasReply) it's receive-only for now; the daemon log always records it as a fallback.
122156
- onChat: ({ sessionId, viewerName, text: text12 }) => {
122535
+ onChat: ({ sessionId, viewerName, text: text13 }) => {
122157
122536
  const NotifCtor = electronModule()?.Notification;
122158
122537
  if (!NotifCtor) {
122159
- log10(`[rdesk chat] ${viewerName ?? "viewer"}: ${text12}`);
122538
+ log10(`[rdesk chat] ${viewerName ?? "viewer"}: ${text13}`);
122160
122539
  return;
122161
122540
  }
122162
122541
  try {
122163
- const n = new NotifCtor({ title: `${viewerName ?? "Remote viewer"} \u2014 remote desktop`, body: text12, hasReply: true, replyPlaceholder: "Reply\u2026" });
122542
+ const n = new NotifCtor({ title: `${viewerName ?? "Remote viewer"} \u2014 remote desktop`, body: text13, hasReply: true, replyPlaceholder: "Reply\u2026" });
122164
122543
  n.on("reply", (_e, reply3) => {
122165
122544
  const r = typeof reply3 === "string" ? reply3.trim() : "";
122166
122545
  if (r)
@@ -122500,7 +122879,7 @@ async function runDaemon(opts = {}) {
122500
122879
  log10("e2e responder init failed (staying plaintext)", err2.message);
122501
122880
  }
122502
122881
  }
122503
- startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION3, e2e }, hooks2);
122882
+ startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION2, e2e }, hooks2);
122504
122883
  offRecovery = onSystemEvent(({ event }) => {
122505
122884
  const suspended = isSuspensionSignal(event);
122506
122885
  if (suspended)
@@ -122601,7 +122980,7 @@ async function runDaemon(opts = {}) {
122601
122980
  }
122602
122981
  }
122603
122982
  const who = c.userId ? `user ${c.userId}` : "no account (unclaimed machine)";
122604
- log10(`started \u2014 device ${c.deviceId}, ${who}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION3}`);
122983
+ log10(`started \u2014 device ${c.deviceId}, ${who}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION2}`);
122605
122984
  await refresh();
122606
122985
  timer2 = setInterval(() => {
122607
122986
  void refresh();
@@ -122709,8 +123088,8 @@ function installOwnerUid(env = process.env, self2 = typeof process.getuid === "f
122709
123088
  }
122710
123089
  return self2;
122711
123090
  }
122712
- function parseOwnerUid(text12) {
122713
- const m = /--owner-uid(?:<\/string>)?[\s=]*(?:<string>)?(\d+)/.exec(text12);
123091
+ function parseOwnerUid(text13) {
123092
+ const m = /--owner-uid(?:<\/string>)?[\s=]*(?:<string>)?(\d+)/.exec(text13);
122714
123093
  if (!m)
122715
123094
  return void 0;
122716
123095
  const uid = Number(m[1]);
@@ -122767,8 +123146,8 @@ function bundleIdForExec(exec2) {
122767
123146
  return void 0;
122768
123147
  }
122769
123148
  }
122770
- function parseAssociatedBundleId(text12) {
122771
- const m = /<key>AssociatedBundleIdentifiers<\/key>\s*(?:<array>\s*)?<string>([^<]*)<\/string>/.exec(text12);
123149
+ function parseAssociatedBundleId(text13) {
123150
+ const m = /<key>AssociatedBundleIdentifiers<\/key>\s*(?:<array>\s*)?<string>([^<]*)<\/string>/.exec(text13);
122772
123151
  const id = m?.[1]?.trim();
122773
123152
  return id && looksLikeBundleId(id) ? id : void 0;
122774
123153
  }
@@ -122829,10 +123208,25 @@ ${args}
122829
123208
  <integer>30</integer>
122830
123209
  <key>ProcessType</key>
122831
123210
  <string>Interactive</string>
123211
+ <!-- /dev/null, NOT netd.log, and this is load-bearing.
123212
+
123213
+ These two keys used to point at netd.log, and launchd does not rotate what it
123214
+ redirects \u2014 so the file grew for the life of the install. The same omission on
123215
+ Windows reached 136 GB on a CI runner and hung two hours of release builds while
123216
+ looking like an npm problem; macOS had it too and nobody had looked.
123217
+
123218
+ netd now owns netd.log itself and rotates it (tsmesh_netd::logfile, 8 MiB, one
123219
+ previous generation). Leaving launchd pointed at the same path would be WORSE than
123220
+ the bug: after netd renames the file, launchd's descriptor still refers to the
123221
+ renamed inode, so it would append to the "previous generation" forever and no
123222
+ rotation could ever reclaim it. One writer, or none.
123223
+
123224
+ Nothing is lost by discarding this stream: everything netd logs goes through its own
123225
+ sink, and a panic is routed there too by a hook installed with the log file. -->
122832
123226
  <key>StandardErrorPath</key>
122833
- <string>${xml(join91(spec.stateDir, "netd.log"))}</string>
123227
+ <string>/dev/null</string>
122834
123228
  <key>StandardOutPath</key>
122835
- <string>${xml(join91(spec.stateDir, "netd.log"))}</string>
123229
+ <string>/dev/null</string>
122836
123230
  </dict>
122837
123231
  </plist>
122838
123232
  `;
@@ -122920,9 +123314,9 @@ ${plist}TESSERACT_PLIST_EOF`,
122920
123314
  async state() {
122921
123315
  if (!existsSync50(PLIST))
122922
123316
  return { installed: false, running: false };
122923
- const text12 = readFileSync59(PLIST, "utf8");
122924
- const owner = parseOwnerUid(text12);
122925
- const assoc = parseAssociatedBundleId(text12);
123317
+ const text13 = readFileSync59(PLIST, "utf8");
123318
+ const owner = parseOwnerUid(text13);
123319
+ const assoc = parseAssociatedBundleId(text13);
122926
123320
  const running2 = await netdAvailable();
122927
123321
  return {
122928
123322
  installed: true,
@@ -123559,10 +123953,10 @@ import { dirname as dirname42, join as join94 } from "node:path";
123559
123953
  import { homedir as homedir16, userInfo as userInfo2 } from "node:os";
123560
123954
 
123561
123955
  // ../../packages/host-core/dist/service/legacy-label.js
123562
- function definitionIsCli(text12) {
123563
- if (!text12)
123956
+ function definitionIsCli(text13) {
123957
+ if (!text13)
123564
123958
  return false;
123565
- return /node_modules[/\\]tesseract-agent[/\\]/.test(text12) || /[/\\]bin[/\\]tesseract-agent(\.cmd|\.exe)?["'\s]*$/m.test(text12) || /[/\\]bin[/\\]tesseract-agent(\.cmd|\.exe)?[<"'\s]/.test(text12);
123959
+ return /node_modules[/\\]tesseract-agent[/\\]/.test(text13) || /[/\\]bin[/\\]tesseract-agent(\.cmd|\.exe)?["'\s]*$/m.test(text13) || /[/\\]bin[/\\]tesseract-agent(\.cmd|\.exe)?[<"'\s]/.test(text13);
123566
123960
  }
123567
123961
 
123568
123962
  // ../../packages/host-core/dist/service/launchd.js
@@ -124689,7 +125083,9 @@ async function meshCommand(rest, flags) {
124689
125083
  return;
124690
125084
  }
124691
125085
  console.log(`node: running on UDP port ${s.port ?? "?"}`);
124692
- console.log(`node: LAN discovery ${s.lan ? "on" : "off (no multicast on this network)"}`);
125086
+ console.log(
125087
+ `node: LAN discovery ${s.lan ? "on" : `off \u2014 ${s.lanError ?? "no reason was recorded"}`}`
125088
+ );
124693
125089
  if (!("reachableAt" in s)) {
124694
125090
  console.log("reachable at: unknown \u2014 the background service predates this CLI; restart it and ask again");
124695
125091
  } else {