tesseract-agent 0.11.15 → 0.11.17

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.
@@ -20222,6 +20222,21 @@ Rules:
20222
20222
  - Plain words a non-expert understands. No jargon, no file paths, no command names unless the suggestion itself names them.
20223
20223
  - Address the developer directly ("This would\u2026"). Never answer the suggestion, never do the work, never ask a question.
20224
20224
  - Two sentences maximum. Stop.`
20225
+ },
20226
+ {
20227
+ id: "side-llm.summarize-turn",
20228
+ group: "side-llm",
20229
+ label: "Conversation tab summariser",
20230
+ help: "One short line describing what the conversation has DONE so far, shown in the tab's hover tooltip under the title. Rebuilt after each turn. The \u226412-word / one-sentence limit is the whole contract \u2014 it sits in a small tooltip, so anything longer is clipped; and it must read as a status of work done, never as a reply to the developer.",
20231
+ vars: [],
20232
+ content: `A developer is working with an AI coding agent. Summarise, in ONE short sentence, what this conversation has accomplished SO FAR \u2014 the kind of line that belongs under a browser-tab title.
20233
+ Rules:
20234
+ - Output ONLY the sentence. No markdown, no quotes, no preamble, no trailing period.
20235
+ - 12 words MAXIMUM. Shorter is better.
20236
+ - Describe the WORK DONE, not what is planned or being asked. Past/perfect or a noun phrase ("Added tab tooltips and per-turn summaries", "Debugging the failing image upload").
20237
+ - Weight the MOST RECENT messages most heavily: if the topic shifted, describe where it is now, not where it started.
20238
+ - Plain, concrete words. Name the real feature/file/thing when it is clear. No filler like "The agent\u2026" or "This conversation\u2026".
20239
+ - Never answer the developer, never do the work, never ask a question.`
20225
20240
  },
20226
20241
  {
20227
20242
  id: "side-llm.project-inspect",
@@ -23061,7 +23076,7 @@ var init_capabilities = __esm({
23061
23076
  label: "Create a presentation",
23062
23077
  help: "What the model is told the `present.create_deck` App-Control capability does (present domain, write).",
23063
23078
  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.`
23079
+ content: `Make a new, empty Tesseract Stage presentation and return its deck handle. It lands in the project the user has open \u2014 beside the files it is about \u2014 or in their library (~/Documents/Tesseract/Presentations) when no project is open. 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
23080
  },
23066
23081
  {
23067
23082
  id: "capability.present.write_slide",
@@ -24422,6 +24437,13 @@ function schemaFieldRejected(status2, body) {
24422
24437
  function anthropicBaseUrl(env) {
24423
24438
  return env?.ANTHROPIC_BASE_URL?.trim() || process.env.ANTHROPIC_BASE_URL?.trim() || "https://api.anthropic.com";
24424
24439
  }
24440
+ function isAnthropicEndpoint(env) {
24441
+ try {
24442
+ return /(^|\.)api\.anthropic\.com$/i.test(new URL(anthropicBaseUrl(env)).hostname);
24443
+ } catch {
24444
+ return false;
24445
+ }
24446
+ }
24425
24447
  var OAUTH_BETA_HEADER;
24426
24448
  var init_model_json = __esm({
24427
24449
  "../../packages/shared/dist/model-json.js"() {
@@ -24974,15 +24996,12 @@ function isAnthropicModelId(model) {
24974
24996
  const id = (model ?? "").trim().toLowerCase();
24975
24997
  if (!id)
24976
24998
  return false;
24977
- if (TIER_ALIAS.test(id))
24978
- return false;
24979
24999
  return /(^|[/.\-])(claude|opus|sonnet|haiku|fable)([/.\-]|$)/.test(id) || /^(opus|sonnet|haiku|fable)\b/.test(id);
24980
25000
  }
24981
- var TIER_ALIAS, ONE_MILLION_FAMILIES, SUFFIX;
25001
+ var ONE_MILLION_FAMILIES, SUFFIX;
24982
25002
  var init_model_long_context = __esm({
24983
25003
  "../../packages/shared/dist/model-long-context.js"() {
24984
25004
  "use strict";
24985
- TIER_ALIAS = /^tsr-/i;
24986
25005
  ONE_MILLION_FAMILIES = [
24987
25006
  /(^|[/.])claude-sonnet-(4-6|4-7|4-8|5)\b/i,
24988
25007
  /(^|[/.])claude-opus-(4-6|4-7|4-8|5)\b/i,
@@ -26626,10 +26645,10 @@ function providerBase(key2, which2) {
26626
26645
  return fromServer;
26627
26646
  return which2 === "anthropic" ? gatewayAnthropicBaseUrl(key2.baseUrl) : which2 === "openai" ? gatewayOpenAiBaseUrl(key2.baseUrl) : gatewayGeminiBaseUrl(key2.baseUrl);
26628
26647
  }
26629
- function aliasProviders(key2) {
26648
+ function slotProviders(key2) {
26630
26649
  const out = /* @__PURE__ */ new Map();
26631
26650
  for (const slot of Object.keys(TIER_PROVIDER)) {
26632
- const id = key2.models[slot];
26651
+ const id = key2.models[slot]?.trim();
26633
26652
  if (id)
26634
26653
  out.set(id, TIER_PROVIDER[slot]);
26635
26654
  }
@@ -26638,25 +26657,26 @@ function aliasProviders(key2) {
26638
26657
  function tiers(engineId, key2, models) {
26639
26658
  const provider = gatewayProviderForEngine(engineId);
26640
26659
  const preference = provider ? TIER_PREFERENCE[provider] : void 0;
26641
- const byAlias = aliasProviders(key2);
26660
+ const byModel = slotProviders(key2);
26642
26661
  const asked = (v) => {
26643
26662
  const t = v?.trim();
26644
26663
  if (!t)
26645
26664
  return void 0;
26646
26665
  if (t.includes("/"))
26647
26666
  return t;
26648
- return byAlias.get(t) === provider ? t : void 0;
26667
+ return byModel.get(t) === provider ? t : void 0;
26649
26668
  };
26650
26669
  const pick3 = (slots) => {
26651
26670
  for (const slot of slots) {
26652
- const id = key2.models[slot];
26671
+ const id = key2.models[slot]?.trim();
26653
26672
  if (id)
26654
26673
  return id;
26655
26674
  }
26656
26675
  return void 0;
26657
26676
  };
26658
- const main2 = asked(models.main) || preference && pick3(preference.main);
26659
- const fast = asked(models.fast) || preference && pick3(preference.fast) || main2;
26677
+ const serverDefault = asked(key2.defaults?.model);
26678
+ const main2 = asked(models.main) || serverDefault || preference && pick3(preference.main);
26679
+ const fast = asked(models.fast) || serverDefault || preference && pick3(preference.fast) || main2;
26660
26680
  return { main: main2 || void 0, fast: fast || void 0 };
26661
26681
  }
26662
26682
  function planGatewayRun(engineId, key2, models, homeBase) {
@@ -26698,7 +26718,7 @@ var init_gateway_plan = __esm({
26698
26718
  };
26699
26719
  TIER_PREFERENCE = {
26700
26720
  // 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.
26721
+ // and the nano model remains the cheap fallback for anything that asks for one.
26702
26722
  openai: { main: ["codex", "gpt", "high"], fast: ["low", "high"] },
26703
26723
  anthropic: { main: ["claude"], fast: ["claudeFast", "claude"] },
26704
26724
  "google-ai": { main: ["vision"], fast: ["vision"] }
@@ -32981,8 +33001,9 @@ ${JSON.stringify(r, null, 2)}`
32981
33001
  domain: "present",
32982
33002
  title: "Create a presentation",
32983
33003
  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."),
33004
+ title: external_exports.string().describe("What the presentation is called. Used for its folder name; keep the user's own wording and language."),
32985
33005
  description: external_exports.string().optional().describe("One line about what it is for."),
33006
+ root: external_exports.string().optional().describe("Folder to create it in. Leave unset \u2014 it then lands in the project the user has open, which is almost always what they want. Only set this if they asked for somewhere specific."),
32986
33007
  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
33008
  }),
32988
33009
  mutation: "write",
@@ -32995,7 +33016,7 @@ ${JSON.stringify(r, null, 2)}`
32995
33016
  domain: "present",
32996
33017
  title: "Write a slide",
32997
33018
  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)."),
33019
+ deck: external_exports.string().describe("The deck handle, exactly as `list_decks` or `create_deck` returned it. Either a folder name or an absolute path, both ending in .tessdeck \u2014 pass it back unchanged rather than rebuilding it."),
32999
33020
  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
33021
  source: external_exports.string().describe("The whole file. A slide exports `meta` ({ id, title, summary?, notes?, transition?, builds? }) and a default React component.")
33001
33022
  }),
@@ -33009,7 +33030,7 @@ ${JSON.stringify(r, null, 2)}`
33009
33030
  domain: "present",
33010
33031
  title: "Read a slide",
33011
33032
  input: external_exports.object({
33012
- deck: external_exports.string().describe("The deck folder."),
33033
+ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you."),
33013
33034
  path: external_exports.string().describe('Path inside the presentation, e.g. "slides/03-pricing.tsx".')
33014
33035
  }),
33015
33036
  mutation: "read",
@@ -33021,7 +33042,7 @@ ${JSON.stringify(r, null, 2)}`
33021
33042
  domain: "present",
33022
33043
  title: "Delete a slide",
33023
33044
  input: external_exports.object({
33024
- deck: external_exports.string().describe("The deck folder."),
33045
+ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you."),
33025
33046
  path: external_exports.string().describe("Path inside the presentation.")
33026
33047
  }),
33027
33048
  mutation: "write",
@@ -33033,7 +33054,7 @@ ${JSON.stringify(r, null, 2)}`
33033
33054
  id: "present.validate_deck",
33034
33055
  domain: "present",
33035
33056
  title: "Check a presentation",
33036
- input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33057
+ input: external_exports.object({ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you.") }),
33037
33058
  mutation: "read",
33038
33059
  availability: "desktop",
33039
33060
  binding: { kind: "ipc", channel: "decklib", verb: "validate" },
@@ -33044,7 +33065,7 @@ ${JSON.stringify(r, null, 2)}`
33044
33065
  domain: "present",
33045
33066
  title: "Set a presentation\u2019s theme",
33046
33067
  input: external_exports.object({
33047
- deck: external_exports.string().describe("The deck folder."),
33068
+ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you."),
33048
33069
  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
33070
  }),
33050
33071
  mutation: "write",
@@ -33057,7 +33078,7 @@ ${JSON.stringify(r, null, 2)}`
33057
33078
  domain: "present",
33058
33079
  title: "Add an image to a presentation",
33059
33080
  input: external_exports.object({
33060
- deck: external_exports.string().describe("The deck folder."),
33081
+ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you."),
33061
33082
  source_path: external_exports.string().describe("Absolute path of the file to copy in \u2014 a generated image, a photo, an audio clip."),
33062
33083
  name: external_exports.string().optional().describe("Filename to use inside the presentation.")
33063
33084
  }),
@@ -33070,7 +33091,7 @@ ${JSON.stringify(r, null, 2)}`
33070
33091
  id: "present.open_deck",
33071
33092
  domain: "present",
33072
33093
  title: "Open a presentation",
33073
- input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33094
+ input: external_exports.object({ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you.") }),
33074
33095
  mutation: "write",
33075
33096
  availability: "desktop",
33076
33097
  binding: { kind: "ipc", channel: "decklib", verb: "open" },
@@ -33080,7 +33101,7 @@ ${JSON.stringify(r, null, 2)}`
33080
33101
  id: "present.delete_deck",
33081
33102
  domain: "present",
33082
33103
  title: "Delete a presentation",
33083
- input: external_exports.object({ deck: external_exports.string().describe("The deck folder.") }),
33104
+ input: external_exports.object({ deck: external_exports.string().describe("The deck handle, exactly as it was returned to you.") }),
33084
33105
  mutation: "write",
33085
33106
  availability: "desktop",
33086
33107
  binding: { kind: "ipc", channel: "decklib", verb: "delete" },
@@ -33285,7 +33306,7 @@ var CAPABILITIES_DOC;
33285
33306
  var init_capabilities_doc_generated = __esm({
33286
33307
  "../../packages/agent-core/dist/capabilities-doc.generated.js"() {
33287
33308
  "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';
33309
+ 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. Models are named the way their makers\n name them \u2014 `claude-sonnet-4-6`, `claude-haiku-4-5-20251001`, `gpt-6-astra`, `gpt-5.5`,\n `gpt-5.4-mini`, `gpt-5.4-nano`, `gemini-2.5-flash`, `gpt-image-2` ($0.10/image flat) \u2014 and a\n signed-in key may also name a model we do not host as `vendor/model`.\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
33310
  }
33290
33311
  });
33291
33312
 
@@ -38847,13 +38868,6 @@ async function anthropicCall(args) {
38847
38868
  }
38848
38869
  return { text: null, rateLimited: false };
38849
38870
  }
38850
- function isDirectAnthropic(env) {
38851
- try {
38852
- return /(^|\.)api\.anthropic\.com$/i.test(new URL(anthropicBaseUrl(env)).hostname);
38853
- } catch {
38854
- return false;
38855
- }
38856
- }
38857
38871
  async function buildClaudeNarrationLlm(env) {
38858
38872
  if (!await hasAnthropicCred(env))
38859
38873
  return null;
@@ -38943,7 +38957,7 @@ async function buildClaudeVisionLlm(env) {
38943
38957
  ]
38944
38958
  });
38945
38959
  }
38946
- var execFileP, USAGE_URL, FETCH_TIMEOUT_MS, cache, TTL_MS, RATE_LIMIT_BACKOFF_MS, inFlight, rateLimitedUntil, schemaUnsupported, schemaKey, SMART_TIMEOUT_MS, VISION_TIMEOUT_MS;
38960
+ var execFileP, USAGE_URL, FETCH_TIMEOUT_MS, cache, TTL_MS, RATE_LIMIT_BACKOFF_MS, inFlight, rateLimitedUntil, isDirectAnthropic, schemaUnsupported, schemaKey, SMART_TIMEOUT_MS, VISION_TIMEOUT_MS;
38947
38961
  var init_session_usage = __esm({
38948
38962
  "../../packages/agent-core/dist/session-usage.js"() {
38949
38963
  "use strict";
@@ -38958,6 +38972,7 @@ var init_session_usage = __esm({
38958
38972
  RATE_LIMIT_BACKOFF_MS = 5 * 6e4;
38959
38973
  inFlight = null;
38960
38974
  rateLimitedUntil = 0;
38975
+ isDirectAnthropic = isAnthropicEndpoint;
38961
38976
  schemaUnsupported = /* @__PURE__ */ new Set();
38962
38977
  schemaKey = (base, model) => `${base}|${model}`;
38963
38978
  SMART_TIMEOUT_MS = RV_GRADE_TIMEOUT_MS;
@@ -39087,7 +39102,7 @@ var init_stream_stall = __esm({
39087
39102
  // ../../packages/agent-core/dist/model-switch.js
39088
39103
  function modelFamily(model) {
39089
39104
  const id = (model ?? "").trim().toLowerCase();
39090
- if (!id || id.startsWith("tsr-"))
39105
+ if (!id)
39091
39106
  return void 0;
39092
39107
  return FAMILIES2.find((family) => id.includes(family));
39093
39108
  }
@@ -39492,12 +39507,14 @@ function sessionStoreFor(mode) {
39492
39507
  function unattendedReportDirective(send5) {
39493
39508
  return unattendedModeFor({ optedIn: send5.unattended, origin: send5.origin }) !== "off" ? prompt("agent.system.unattended-report") : "";
39494
39509
  }
39495
- function sdkModelId(model) {
39510
+ function sdkModelId(model, env) {
39496
39511
  const id = model?.trim();
39497
39512
  if (!id)
39498
39513
  return void 0;
39499
39514
  if (process.env.TESSERACT_DISABLE_1M_CONTEXT === "1")
39500
39515
  return id;
39516
+ if (!isAnthropicEndpoint(env))
39517
+ return id;
39501
39518
  if (/[/]/.test(id) || /^[a-z]{2,}\./i.test(id))
39502
39519
  return id;
39503
39520
  return withOneMillionContext(id) ?? id;
@@ -41317,7 +41334,7 @@ var init_agent_manager = __esm({
41317
41334
  return false;
41318
41335
  }
41319
41336
  try {
41320
- await live.query.setModel(sdkModelId(next));
41337
+ await live.query.setModel(sdkModelId(next, live.endpointEnv));
41321
41338
  } catch (err2) {
41322
41339
  log(`[model] ${turnSid}: could not switch to ${next}:`, err2 instanceof Error ? err2.message : String(err2));
41323
41340
  return false;
@@ -42852,7 +42869,7 @@ ${lines}`;
42852
42869
  if (opts.permissionMode)
42853
42870
  await live.query.setPermissionMode(sdkPermissionMode(opts.permissionMode));
42854
42871
  if (opts.model !== void 0) {
42855
- await live.query.setModel(sdkModelId(opts.model));
42872
+ await live.query.setModel(sdkModelId(opts.model, live.endpointEnv));
42856
42873
  live.model = opts.model || void 0;
42857
42874
  this.rememberSessionModel(live.sessionId, live.model);
42858
42875
  }
@@ -43081,7 +43098,7 @@ ${lines}`;
43081
43098
  live.windowId = params.windowId;
43082
43099
  live.pendingTitle = params.message.trim().slice(0, 80);
43083
43100
  if (params.model)
43084
- void live.query.setModel(sdkModelId(params.model)).catch(() => {
43101
+ void live.query.setModel(sdkModelId(params.model, live.endpointEnv)).catch(() => {
43085
43102
  });
43086
43103
  if (params.permissionMode)
43087
43104
  void live.query.setPermissionMode(sdkPermissionMode(params.permissionMode)).catch(() => {
@@ -43398,6 +43415,7 @@ ${prompt("agent.system.long-running")}`;
43398
43415
  }
43399
43416
  async createLiveSession(params) {
43400
43417
  const spawnModel = this.modelForSpawn(params);
43418
+ const spawnEndpoint = { ...params.env ?? {}, ...params.engineEnv ?? {} };
43401
43419
  this.rememberSessionModel(params.sessionId, spawnModel);
43402
43420
  this.rememberSessionModel(params.clientRequestId, spawnModel);
43403
43421
  const openProject = params.projectDir ?? this.projectDir ?? params.sources?.[0];
@@ -43559,7 +43577,7 @@ ${prompt("agent.system.long-running")}`;
43559
43577
  // `modelForSpawn`, not `params.model`: a resume that nobody sent a model with (a
43560
43578
  // wake-up after a background task, a daemon-side continuation) would otherwise open a
43561
43579
  // 1M conversation against 200k. See sessionModels.
43562
- ...spawnModel ? { model: sdkModelId(spawnModel) } : {},
43580
+ ...spawnModel ? { model: sdkModelId(spawnModel, spawnEndpoint) } : {},
43563
43581
  // Clamp to what THIS CLI accepts — an unsupported level is a hard exit-1, not
43564
43582
  // a warning. See clampEffort.
43565
43583
  ...ultracodeEffort ? { effort: ultracodeEffort } : {},
@@ -43568,7 +43586,7 @@ ${prompt("agent.system.long-running")}`;
43568
43586
  // of surfacing an error the user has to act on.
43569
43587
  // Same for the fallback: it REPLACES the main model on an overload, so a bare id here
43570
43588
  // would quietly move a long conversation onto a 200k window mid-run.
43571
- ...params.fallbackModel ? { fallbackModel: sdkModelId(params.fallbackModel) } : {},
43589
+ ...params.fallbackModel ? { fallbackModel: sdkModelId(params.fallbackModel, spawnEndpoint) } : {},
43572
43590
  ...params.enableFileCheckpointing ? { enableFileCheckpointing: true } : {},
43573
43591
  // ON BY DEFAULT. This is what turns a silent helper into one that says what it is
43574
43592
  // doing — a short present-tense line on each progress frame, written by forking the
@@ -44299,7 +44317,7 @@ ${prompt("agent.system.long-running")}`;
44299
44317
  // rather than the number in the run-mode menu.
44300
44318
  ...this.daemonCompactEnv(params.env),
44301
44319
  // A GATEWAY MODEL ALIAS MUST NOT BE WINDOW-ENFORCED BY THE CLI. Routed through
44302
- // our gateway the id is an alias (`tsr-claude`) the CLI does not know. Harmless
44320
+ // our gateway it may be a retired tier alias the CLI does not know. Harmless
44303
44321
  // for an ordinary query; fatal for its INTERNAL compaction query, which never
44304
44322
  // returns — no frame, 120s stall, killed as "connection went quiet", replayed
44305
44323
  // by the client, and stalled again every two minutes for ever. MEASURED on a
@@ -47514,9 +47532,9 @@ function materializeClaudeAuthHome(contents) {
47514
47532
  });
47515
47533
  return home;
47516
47534
  }
47517
- function withClaudeModels(env, main2, fast) {
47535
+ function withClaudeModels(env, main2, fast, longWindow = true) {
47518
47536
  if (main2) {
47519
- env.ANTHROPIC_MODEL = withOneMillionContext(main2) ?? main2;
47537
+ env.ANTHROPIC_MODEL = (longWindow ? withOneMillionContext(main2) : main2) ?? main2;
47520
47538
  }
47521
47539
  if (fast)
47522
47540
  env.ANTHROPIC_SMALL_FAST_MODEL = fast;
@@ -47563,7 +47581,7 @@ function resolveRunnerEnv(spec) {
47563
47581
  if (!baseUrl.trim()) {
47564
47582
  return { CCB_RUNNER_CONFIG_ERROR: `Set an address for ${provider.label} in Settings \u2192 Models & accounts.` };
47565
47583
  }
47566
- return withClaudeModels({ ANTHROPIC_BASE_URL: baseUrl, ANTHROPIC_AUTH_TOKEN: key2, ANTHROPIC_API_KEY: "" }, main2, fast);
47584
+ return withClaudeModels({ ANTHROPIC_BASE_URL: baseUrl, ANTHROPIC_AUTH_TOKEN: key2, ANTHROPIC_API_KEY: "" }, main2, fast, false);
47567
47585
  }
47568
47586
  const endpoint2 = routerEndpointSync({
47569
47587
  providerId,
@@ -47637,7 +47655,10 @@ function readGatewayKey() {
47637
47655
  baseUrl: raw.baseUrl,
47638
47656
  apiKey: raw.apiKey,
47639
47657
  models: raw.models,
47640
- ...raw.endpoints ? { endpoints: raw.endpoints } : {}
47658
+ ...raw.endpoints ? { endpoints: raw.endpoints } : {},
47659
+ // Per-audience defaults (engine + model) travel through too — `tiers()` in shared
47660
+ // honours `defaults.model` on this headless spawn path exactly as the desktop does.
47661
+ ...raw.defaults ? { defaults: raw.defaults } : {}
47641
47662
  };
47642
47663
  }
47643
47664
  } catch {
@@ -48159,29 +48180,6 @@ function readDaemonAgentSettings() {
48159
48180
  return snap;
48160
48181
  }
48161
48182
  }
48162
- function gatewayCanCall(model, snapshot4) {
48163
- const tiers2 = snapshot4.gatewayTiers;
48164
- if (!tiers2?.length)
48165
- return true;
48166
- return tiers2.includes(model) || snapshot4.gatewayDirectNaming === true;
48167
- }
48168
- function gatewayTierRejection(model) {
48169
- if (!model)
48170
- return null;
48171
- const snap = readDaemonAgentSettings();
48172
- if (gatewayCanCall(model, snap))
48173
- return null;
48174
- return { model, tiers: [...snap.gatewayTiers ?? []] };
48175
- }
48176
- function gatewayTierClamp(model) {
48177
- if (!model)
48178
- return model;
48179
- const snap = readDaemonAgentSettings();
48180
- if (gatewayCanCall(model, snap))
48181
- return model;
48182
- console.warn(`[gateway] Model "${model}" isn't served by this device's gateway key (${(snap.gatewayTiers ?? []).join(", ")}) \u2014 dropping it so the gateway picks one it can reach.`);
48183
- return void 0;
48184
- }
48185
48183
  function chatSendToParams(msg, defaults) {
48186
48184
  return mergeDaemonAgentSettings({
48187
48185
  message: msg.message,
@@ -48222,7 +48220,6 @@ function chatSendToParams(msg, defaults) {
48222
48220
  function mergeDaemonAgentSettings(params) {
48223
48221
  const s = readDaemonAgentSettings();
48224
48222
  const hostModel = poolActiveModel() ?? s.model;
48225
- const clampToGatewayTier = gatewayTierClamp;
48226
48223
  return {
48227
48224
  ...params,
48228
48225
  mcpServers: params.mcpServers ?? s.mcpServers,
@@ -48232,16 +48229,15 @@ function mergeDaemonAgentSettings(params) {
48232
48229
  settingSources: params.settingSources ?? s.settingSources,
48233
48230
  maxTurns: params.maxTurns ?? s.maxTurns,
48234
48231
  maxBudgetUsd: params.maxBudgetUsd ?? s.maxBudgetUsd,
48235
- model: clampToGatewayTier(params.model ?? hostModel),
48232
+ model: params.model ?? hostModel,
48236
48233
  hostInjectedModel: params.hostInjectedModel ?? (params.model === void 0 && hostModel !== void 0),
48237
48234
  permissionMode: params.permissionMode ?? s.permissionMode,
48238
48235
  effort: params.effort ?? s.effort,
48239
48236
  ultracode: params.ultracode ?? s.ultracode,
48240
48237
  // Model-level fallback: the engine switches models BEFORE the turn fails. Our runner
48241
48238
  // rotation works one level up (credential/config) and only AFTER a failure, so the two
48242
- // compose rather than compete. Clamped like `model` — a fallback the gateway key
48243
- // cannot serve would 403 exactly like a primary that cannot.
48244
- fallbackModel: clampToGatewayTier(params.fallbackModel ?? s.fallbackModel),
48239
+ // compose rather than compete. Sent as named, like `model`.
48240
+ fallbackModel: params.fallbackModel ?? s.fallbackModel,
48245
48241
  // Token budget the MODEL is told about, so it paces tool use and wraps up instead of
48246
48242
  // being cut off mid-thought (distinct from maxBudgetUsd, which is dollars and hard).
48247
48243
  taskBudget: params.taskBudget ?? s.taskBudget,
@@ -84110,7 +84106,6 @@ function toRewindOutcome(res, noHistory) {
84110
84106
 
84111
84107
  // ../../packages/host-core/dist/control-link.js
84112
84108
  import { join as join67, basename as basename12 } from "node:path";
84113
- import { randomUUID as randomUUID28 } from "node:crypto";
84114
84109
  import { mkdirSync as mkdirSync31 } from "node:fs";
84115
84110
 
84116
84111
  // ../../node_modules/ws/wrapper.mjs
@@ -98523,7 +98518,7 @@ var GeminiVeoProvider = class {
98523
98518
  };
98524
98519
 
98525
98520
  // ../../packages/video-core/dist/tesseract.js
98526
- var DEFAULT_MODEL4 = "tsr-video";
98521
+ var DEFAULT_MODEL4 = "veo-3.1-fast-generate-preview";
98527
98522
  var REQUEST_TIMEOUT_MS2 = 9e4;
98528
98523
  var DOWNLOAD_TIMEOUT_MS2 = 18e4;
98529
98524
  var withTimeout4 = (signal2, ms) => signal2 ?? AbortSignal.timeout(ms);
@@ -99734,8 +99729,8 @@ var VIDEO_PROVIDERS = [
99734
99729
  {
99735
99730
  id: "tesseract",
99736
99731
  label: "Tesseract \xB7 free (no key)",
99737
- defaultModel: "tsr-video",
99738
- models: ["tsr-video"],
99732
+ defaultModel: "veo-3.1-fast-generate-preview",
99733
+ models: ["veo-3.1-fast-generate-preview"],
99739
99734
  keysUrl: "",
99740
99735
  free: true
99741
99736
  },
@@ -100001,7 +99996,7 @@ var IN_TURN_WAIT_MS = 8 * 6e4;
100001
99996
  var HEARTBEAT_MS2 = 15e3;
100002
99997
  var FAST_MODEL = VIDEO_PROVIDERS.find((p) => p.id === "google").defaultModel;
100003
99998
  var STANDARD_MODEL = "veo-3.1-generate-preview";
100004
- var FREE_MODEL = "tsr-video";
99999
+ var FREE_MODEL = "veo-3.1-fast-generate-preview";
100005
100000
  var clampDuration2 = (n) => [4, 6, 8].reduce((best, d) => Math.abs(d - n) < Math.abs(best - n) ? d : best);
100006
100001
  function creds2() {
100007
100002
  return readDaemonAgentSettings().videoGen;
@@ -104943,7 +104938,25 @@ var PeerLinkManager = class {
104943
104938
  const socket2 = await q.makeQuicSocket(this.udpPort);
104944
104939
  this.quicSocket = socket2;
104945
104940
  await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (bs5) => this.acceptInbound(bs5, "punch") });
104941
+ const disableQuicTier = (why) => {
104942
+ if (this.quicUnavailable)
104943
+ return;
104944
+ this.quicUnavailable = true;
104945
+ if (this.quicKeepalive) {
104946
+ clearInterval(this.quicKeepalive);
104947
+ this.quicKeepalive = null;
104948
+ }
104949
+ this.quicSocket = null;
104950
+ try {
104951
+ void socket2.stop({ force: true });
104952
+ } catch {
104953
+ }
104954
+ console.warn(`[peer-link] QUIC tier disabled at runtime \u2014 ${why}. Using TCP tiers; a sandbox-safe @matrixai/quic rebuild re-enables it.`);
104955
+ };
104956
+ const isSandboxFailure = (m) => /create external arraybuffer failed/i.test(m);
104946
104957
  const refresh = async () => {
104958
+ if (this.quicUnavailable)
104959
+ return;
104947
104960
  const found = [];
104948
104961
  for (const { host, port } of resolveStunServers()) {
104949
104962
  try {
@@ -104953,7 +104966,12 @@ var PeerLinkManager = class {
104953
104966
  if (!found.some((c) => c.ip === cand.ip && c.port === cand.port))
104954
104967
  found.push(cand);
104955
104968
  } catch (e) {
104956
- console.warn(`[peer-link] srflx FAILED via ${host}:${port}: ${e.message}`);
104969
+ const detail = e.message;
104970
+ if (isSandboxFailure(detail)) {
104971
+ disableQuicTier(`the native binding rejects this runtime's ArrayBuffers (${detail})`);
104972
+ return;
104973
+ }
104974
+ console.warn(`[peer-link] srflx FAILED via ${host}:${port}: ${detail}`);
104957
104975
  }
104958
104976
  }
104959
104977
  if (found.length && JSON.stringify(found) !== JSON.stringify(this.quicSrflxList)) {
@@ -104961,6 +104979,14 @@ var PeerLinkManager = class {
104961
104979
  console.log(`[peer-link] srflx candidates: ${found.map((c) => `${c.ip}:${c.port}`).join(", ")}`);
104962
104980
  }
104963
104981
  };
104982
+ const onUnhandled = (reason) => {
104983
+ const m = reason instanceof Error ? reason.message : String(reason);
104984
+ if (!isSandboxFailure(m))
104985
+ return;
104986
+ process.off("unhandledRejection", onUnhandled);
104987
+ disableQuicTier(`the native binding rejects this runtime's ArrayBuffers (${m})`);
104988
+ };
104989
+ process.on("unhandledRejection", onUnhandled);
104964
104990
  await refresh();
104965
104991
  this.quicKeepalive = setInterval(() => {
104966
104992
  void refresh();
@@ -110527,23 +110553,7 @@ async function handleInboundAgent(msg, driverClientId) {
110527
110553
  void b.backgroundTask?.(msg.sessionId, msg.toolUseId, origin);
110528
110554
  break;
110529
110555
  case "agent:set_mode":
110530
- {
110531
- const rejected = gatewayTierRejection(msg.model);
110532
- if (rejected) {
110533
- send3("agent", {
110534
- type: "agent:notice",
110535
- sessionId: msg.sessionId,
110536
- noticeId: randomUUID28(),
110537
- category: "model",
110538
- level: "warning",
110539
- title: "That model is not available on this device",
110540
- body: `The gateway key on this machine serves ${rejected.tiers.join(", ")}, so "${rejected.model}" was not applied \u2014 the conversation is still on its previous model.`,
110541
- toast: true,
110542
- inline: true
110543
- }, route3);
110544
- }
110545
- }
110546
- void b.setRunMode?.(msg.sessionId, { permissionMode: msg.permissionMode, model: gatewayTierClamp(msg.model), maxThinkingTokens: msg.maxThinkingTokens, unattended: msg.unattended, reinforcedVerification: msg.reinforcedVerification, effort: msg.effort, ultracode: msg.ultracode });
110556
+ void b.setRunMode?.(msg.sessionId, { permissionMode: msg.permissionMode, model: msg.model, maxThinkingTokens: msg.maxThinkingTokens, unattended: msg.unattended, reinforcedVerification: msg.reinforcedVerification, effort: msg.effort, ultracode: msg.ultracode });
110547
110557
  break;
110548
110558
  case "chat:permission":
110549
110559
  b.respondToPermission?.(msg.requestId, { behavior: msg.behavior, updatedInput: msg.updatedInput, persist: msg.persist, message: msg.message, interrupt: msg.interrupt }, origin);
@@ -111354,7 +111364,7 @@ init_base_paths();
111354
111364
  init_credentials();
111355
111365
  init_dist5();
111356
111366
  import { mkdirSync as mkdirSync32, readFileSync as readFileSync42, writeFileSync as writeFileSync28 } from "node:fs";
111357
- import { randomUUID as randomUUID29 } from "node:crypto";
111367
+ import { randomUUID as randomUUID28 } from "node:crypto";
111358
111368
  import { dirname as dirname28 } from "node:path";
111359
111369
  var REFRESH_MS = 24 * 60 * 60 * 1e3;
111360
111370
  var TIMEOUT_MS2 = 1e4;
@@ -111370,7 +111380,7 @@ function deviceId2() {
111370
111380
  return raw.deviceId;
111371
111381
  } catch {
111372
111382
  }
111373
- const fresh = randomUUID29().slice(0, 8);
111383
+ const fresh = randomUUID28().slice(0, 8);
111374
111384
  try {
111375
111385
  mkdirSync32(dirname28(DEVICE_ID()), { recursive: true });
111376
111386
  writeFileSync28(DEVICE_ID(), JSON.stringify({ deviceId: fresh }), { mode: 384 });
@@ -113451,7 +113461,6 @@ function stopSystemEvents() {
113451
113461
  }
113452
113462
 
113453
113463
  // ../../packages/host-core/dist/local-server.js
113454
- import { randomUUID as randomUUID31 } from "node:crypto";
113455
113464
  init_daemon_settings();
113456
113465
 
113457
113466
  // ../../packages/host-core/dist/vault/manager.js
@@ -114047,7 +114056,7 @@ var XJSON_AAD = utf8("tesseract-vault-encrypted-json-v1");
114047
114056
  init_credentials();
114048
114057
 
114049
114058
  // ../../packages/host-core/dist/vault/store.js
114050
- import { randomUUID as randomUUID30 } from "node:crypto";
114059
+ import { randomUUID as randomUUID29 } from "node:crypto";
114051
114060
  var b64 = (u) => Buffer.from(u).toString("base64");
114052
114061
  var u82 = (s) => new Uint8Array(Buffer.from(s, "base64"));
114053
114062
  var isServerRecoverableMeta = (m) => !m.wrappedVaultKey;
@@ -114349,7 +114358,7 @@ var VaultStore = class {
114349
114358
  const existing = input.id ? items.get(input.id) : void 0;
114350
114359
  const { id: _drop, name, folderId, favorite, reprompt, notes, tags, ...content } = input;
114351
114360
  const item = {
114352
- id: input.id ?? randomUUID30(),
114361
+ id: input.id ?? randomUUID29(),
114353
114362
  name,
114354
114363
  folderId,
114355
114364
  favorite,
@@ -114378,7 +114387,7 @@ var VaultStore = class {
114378
114387
  const now2 = this.now();
114379
114388
  for (const d of drafts) {
114380
114389
  const { name, notes, favorite, tags, ...content } = d;
114381
- const item = { id: randomUUID30(), name, notes, favorite, tags, createdAt: now2, updatedAt: now2, ...content };
114390
+ const item = { id: randomUUID29(), name, notes, favorite, tags, createdAt: now2, updatedAt: now2, ...content };
114382
114391
  items.set(item.id, item);
114383
114392
  this.enc.set(item.id, await encryptItem(key2, item));
114384
114393
  this.dirty.add(item.id);
@@ -115421,21 +115430,7 @@ async function startLocalServer(opts) {
115421
115430
  void b.backgroundTask?.(msg.sessionId, msg.toolUseId, loopbackOrigin);
115422
115431
  break;
115423
115432
  case "agent:set_mode": {
115424
- const rejected = gatewayTierRejection(msg.model);
115425
- if (rejected) {
115426
- send5("agent", {
115427
- type: "agent:notice",
115428
- sessionId: msg.sessionId,
115429
- noticeId: randomUUID31(),
115430
- category: "model",
115431
- level: "warning",
115432
- title: "That model is not available on this device",
115433
- body: `Your gateway key serves ${rejected.tiers.join(", ")}, so "${rejected.model}" was not applied \u2014 the conversation is still on its previous model.`,
115434
- toast: true,
115435
- inline: true
115436
- });
115437
- }
115438
- void b.setRunMode?.(msg.sessionId, { permissionMode: msg.permissionMode, model: gatewayTierClamp(msg.model), maxThinkingTokens: msg.maxThinkingTokens, unattended: msg.unattended, reinforcedVerification: msg.reinforcedVerification, effort: msg.effort, ultracode: msg.ultracode });
115433
+ void b.setRunMode?.(msg.sessionId, { permissionMode: msg.permissionMode, model: msg.model, maxThinkingTokens: msg.maxThinkingTokens, unattended: msg.unattended, reinforcedVerification: msg.reinforcedVerification, effort: msg.effort, ultracode: msg.ultracode });
115439
115434
  break;
115440
115435
  }
115441
115436
  case "chat:permission":
@@ -119484,7 +119479,7 @@ async function fetchBlobDrop(opts) {
119484
119479
  }
119485
119480
 
119486
119481
  // ../../packages/host-core/dist/chat-agent.js
119487
- import { randomUUID as randomUUID32 } from "node:crypto";
119482
+ import { randomUUID as randomUUID30 } from "node:crypto";
119488
119483
  init_prompts();
119489
119484
  var TURN_TIMEOUT_MS = 4 * 6e4;
119490
119485
  var PARTIAL_INTERVAL_MS = 700;
@@ -119513,7 +119508,7 @@ function createChatAgentRunner(deps) {
119513
119508
  }
119514
119509
  async function runTurn(prompt2, onPartial) {
119515
119510
  const backend2 = await getSharedBackend();
119516
- const clientRequestId = `chat-agent-${randomUUID32()}`;
119511
+ const clientRequestId = `chat-agent-${randomUUID30()}`;
119517
119512
  return new Promise((resolve16, reject) => {
119518
119513
  let text13 = "";
119519
119514
  let settled = false;
@@ -121539,7 +121534,7 @@ init_credentials();
121539
121534
  init_base_paths();
121540
121535
  import { existsSync as existsSync47, mkdirSync as mkdirSync46, readFileSync as readFileSync57, writeFileSync as writeFileSync43 } from "node:fs";
121541
121536
  import { dirname as dirname39, join as join88 } from "node:path";
121542
- import { randomUUID as randomUUID33 } from "node:crypto";
121537
+ import { randomUUID as randomUUID31 } from "node:crypto";
121543
121538
  function deviceIdFile() {
121544
121539
  return join88(tesseractHome(), "device-id.json");
121545
121540
  }
@@ -121555,7 +121550,7 @@ function persistentDeviceId() {
121555
121550
  const cur = readPersistedDeviceId();
121556
121551
  if (cur)
121557
121552
  return cur;
121558
- const id = randomUUID33().slice(0, 8);
121553
+ const id = randomUUID31().slice(0, 8);
121559
121554
  try {
121560
121555
  const f = deviceIdFile();
121561
121556
  const dir2 = dirname39(f);
@@ -121570,7 +121565,7 @@ function persistentDeviceId() {
121570
121565
  // ../../packages/host-core/dist/error-report.js
121571
121566
  init_dist2();
121572
121567
  init_credentials();
121573
- import { randomUUID as randomUUID34 } from "node:crypto";
121568
+ import { randomUUID as randomUUID32 } from "node:crypto";
121574
121569
  import { release as osRelease, arch as osArch } from "node:os";
121575
121570
  var MAX_QUEUE = 100;
121576
121571
  var FLUSH_INTERVAL_MS = 3e4;
@@ -121622,7 +121617,7 @@ function captureDaemonError(error51, opts = {}) {
121622
121617
  return;
121623
121618
  const creds3 = readCredentials();
121624
121619
  const report = {
121625
- reportId: randomUUID34(),
121620
+ reportId: randomUUID32(),
121626
121621
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
121627
121622
  schemaVersion: 1,
121628
121623
  surface,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fineguide-tesseract/mesh-core",
3
- "version": "0.11.15",
3
+ "version": "0.11.17",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tesseract-agent",
3
- "version": "0.11.15",
3
+ "version": "0.11.17",
4
4
  "license": "UNLICENSED",
5
5
  "publishConfig": {
6
6
  "access": "public",