@critiquedotsh/harness 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +489 -227
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -7787,8 +7787,8 @@ async function createWorkspaceArchive(args) {
7787
7787
  if (relativeDestination.startsWith(`..${sep}`) || relativeDestination === ".." || isAbsolute(relativeDestination)) {
7788
7788
  throw new ChangeCapsuleError(`Unsafe workspace archive path: ${file.path}`, "invalid_path");
7789
7789
  }
7790
- const { mkdir: mkdir17 } = await import("node:fs/promises");
7791
- await mkdir17(dirname(destination), { recursive: true });
7790
+ const { mkdir: mkdir18 } = await import("node:fs/promises");
7791
+ await mkdir18(dirname(destination), { recursive: true });
7792
7792
  await writeFile(destination, file.content, { mode: 384, flag: "wx" });
7793
7793
  }
7794
7794
  await execFileAsync("tar", ["-czf", archivePath, "--exclude=.git", "-C", snapshotRoot, "."], {
@@ -11069,7 +11069,11 @@ var init_pi_session_harness = __esm({
11069
11069
  void active.kernel.abort();
11070
11070
  }, timeoutMs);
11071
11071
  try {
11072
- const turn = await active.kernel.prompt({ prompt: input.prompt, signal: controller.signal });
11072
+ const turn = await active.kernel.prompt({
11073
+ prompt: input.prompt,
11074
+ signal: controller.signal,
11075
+ ...input.images && input.images.length > 0 ? { images: input.images } : {}
11076
+ });
11073
11077
  if (timedOut) throw new Error(`Pi session exceeded ${timeoutMs}ms.`);
11074
11078
  const serialized = typeof turn.output === "string" ? turn.output : JSON.stringify(turn.output);
11075
11079
  if (Buffer.byteLength(serialized) > this.#maxOutputBytes) {
@@ -13599,7 +13603,10 @@ var init_earendil_pi_driver = __esm({
13599
13603
  try {
13600
13604
  const promptRelease = await this.#egress.release({ kind: "model_prompt", content: input.prompt });
13601
13605
  if (promptRelease.content === void 0) throw new Error(`Pi model prompt egress was denied: ${promptRelease.decision.outcome}.`);
13602
- await this.#session.prompt(promptRelease.content);
13606
+ await this.#session.prompt(
13607
+ promptRelease.content,
13608
+ input.images && input.images.length > 0 ? { images: input.images.map((image) => ({ type: "image", data: image.data, mimeType: image.mimeType })) } : void 0
13609
+ );
13603
13610
  const message = lastAssistantMessage(this.#session);
13604
13611
  const rawOutput = this.#assistantText || contentText(message?.content);
13605
13612
  const outputRelease = await this.#egress.release({ kind: "model_response", content: rawOutput });
@@ -13731,11 +13738,11 @@ import { parseArgs } from "node:util";
13731
13738
 
13732
13739
  // lib/finish/critique-code-local-author.ts
13733
13740
  init_change_capsule();
13734
- import { createHash as createHash38, randomBytes } from "node:crypto";
13741
+ import { createHash as createHash38, randomBytes as randomBytes2 } from "node:crypto";
13735
13742
  import { watch } from "node:fs";
13736
- import { mkdir as mkdir14, readFile as readFile22, unlink as unlink4, writeFile as writeFile13 } from "node:fs/promises";
13743
+ import { mkdir as mkdir15, readFile as readFile23, unlink as unlink4, writeFile as writeFile14 } from "node:fs/promises";
13737
13744
  import { tmpdir as tmpdir6 } from "node:os";
13738
- import { isAbsolute as isAbsolute19, join as join19 } from "node:path";
13745
+ import { isAbsolute as isAbsolute19, join as join20 } from "node:path";
13739
13746
  import { createInterface } from "node:readline";
13740
13747
 
13741
13748
  // lib/finish/critique-code-author-coordinator.ts
@@ -20593,6 +20600,90 @@ function formatAuthorReviewStatus(input) {
20593
20600
 
20594
20601
  // lib/finish/critique-code-local-author.ts
20595
20602
  init_critique_code_kernel();
20603
+
20604
+ // lib/finish/critique-code-web-images.ts
20605
+ import { mkdir as mkdir12, readFile as readFile17, writeFile as writeFile11 } from "node:fs/promises";
20606
+ import { join as join15 } from "node:path";
20607
+ import { randomBytes } from "node:crypto";
20608
+ var MARK_START = "<<<critique-code-images>>>";
20609
+ var MARK_END = "<<<end>>>";
20610
+ var MAX_IMAGES = 8;
20611
+ var MAX_BYTES = 6 * 1024 * 1024;
20612
+ function normalizePromptImageMime(value) {
20613
+ const mime = value.trim().toLowerCase();
20614
+ if (mime === "image/jpg") return "image/jpeg";
20615
+ if (mime === "image/png" || mime === "image/jpeg" || mime === "image/webp" || mime === "image/gif") return mime;
20616
+ return void 0;
20617
+ }
20618
+ function parseWebPromptImages(input) {
20619
+ if (!Array.isArray(input)) return [];
20620
+ const out = [];
20621
+ for (const row of input.slice(0, MAX_IMAGES)) {
20622
+ if (!row || typeof row !== "object") continue;
20623
+ const rec = row;
20624
+ const mime = normalizePromptImageMime(String(rec.mimeType ?? rec.mime ?? rec.type ?? ""));
20625
+ const data = String(rec.data ?? rec.b64 ?? "").replace(/^data:[^;]+;base64,/, "").replace(/\s/g, "");
20626
+ if (!mime || !data) continue;
20627
+ const bytes = Buffer.from(data, "base64");
20628
+ if (bytes.length === 0 || bytes.length > MAX_BYTES) continue;
20629
+ out.push({
20630
+ mimeType: mime,
20631
+ data: bytes.toString("base64"),
20632
+ ...typeof rec.name === "string" && rec.name.trim() ? { name: rec.name.trim().slice(0, 80) } : {}
20633
+ });
20634
+ }
20635
+ return out;
20636
+ }
20637
+ function extForMime(mime) {
20638
+ if (mime === "image/jpeg") return "jpg";
20639
+ if (mime === "image/webp") return "webp";
20640
+ if (mime === "image/gif") return "gif";
20641
+ return "png";
20642
+ }
20643
+ async function writeWebPromptImages(input) {
20644
+ if (input.images.length === 0) return [];
20645
+ const dir = join15(input.home, "inbox");
20646
+ await mkdir12(dir, { recursive: true });
20647
+ const paths = [];
20648
+ for (const image of input.images) {
20649
+ const name = `${Date.now()}-${randomBytes(4).toString("hex")}.${extForMime(image.mimeType)}`;
20650
+ const path2 = join15(dir, name);
20651
+ await writeFile11(path2, Buffer.from(image.data, "base64"));
20652
+ paths.push(path2);
20653
+ }
20654
+ return paths;
20655
+ }
20656
+ function wrapPromptWithImagePaths(text, paths) {
20657
+ if (paths.length === 0) return text;
20658
+ return `${MARK_START}
20659
+ ${paths.join("\n")}
20660
+ ${MARK_END}
20661
+ ${text}`;
20662
+ }
20663
+ function unwrapPromptImagePaths(line) {
20664
+ if (!line.startsWith(MARK_START)) return { text: line, paths: [] };
20665
+ const end = line.indexOf(`
20666
+ ${MARK_END}
20667
+ `);
20668
+ if (end < 0) return { text: line, paths: [] };
20669
+ const block = line.slice(MARK_START.length + 1, end);
20670
+ const paths = block.split("\n").map((item) => item.trim()).filter(Boolean);
20671
+ const text = line.slice(end + MARK_END.length + 2);
20672
+ return { text, paths };
20673
+ }
20674
+ async function loadPromptImagesFromPaths(paths) {
20675
+ const images = [];
20676
+ for (const path2 of paths.slice(0, MAX_IMAGES)) {
20677
+ const bytes = await readFile17(path2);
20678
+ if (bytes.length === 0 || bytes.length > MAX_BYTES) continue;
20679
+ const lower = path2.toLowerCase();
20680
+ const mime = lower.endsWith(".jpg") || lower.endsWith(".jpeg") ? "image/jpeg" : lower.endsWith(".webp") ? "image/webp" : lower.endsWith(".gif") ? "image/gif" : "image/png";
20681
+ images.push({ mimeType: mime, data: bytes.toString("base64") });
20682
+ }
20683
+ return images;
20684
+ }
20685
+
20686
+ // lib/finish/critique-code-local-author.ts
20596
20687
  init_critique_code_model_defaults();
20597
20688
  init_critique_code_runtime_config();
20598
20689
 
@@ -20861,24 +20952,26 @@ function filterMenuRows(items, query) {
20861
20952
  }), ...back];
20862
20953
  }
20863
20954
 
20864
- // lib/finish/critique-code-web-icons.ts
20865
- var ICONS = {
20866
- anthropic: { color: "#D97757", mark: "C" },
20867
- critique: { color: "#E61919", mark: "C" },
20868
- deepseek: { color: "#4D6BFE", mark: "D" },
20869
- google: { color: "#4285F4", mark: "G" },
20870
- groq: { color: "#F55036", mark: "Q" },
20871
- kimi: { color: "#1A1A1A", mark: "K" },
20872
- meta: { color: "#0668E1", mark: "M" },
20873
- minimax: { color: "#F23F5D", mark: "m" },
20874
- mistral: { color: "#FA520F", mark: "M" },
20875
- nvidia: { color: "#76B900", mark: "N" },
20876
- openai: { color: "#10A37F", mark: "O" },
20877
- qwen: { color: "#615CED", mark: "Q" },
20878
- xai: { color: "#E8E8E8", mark: "X" },
20879
- zai: { color: "#3859FF", mark: "Z" },
20880
- other: { color: "#8A8178", mark: "A" }
20955
+ // lib/finish/critique-code-web-lobe-svgs.ts
20956
+ var LOBE_MODEL_ICON_SVGS = {
20957
+ critique: '<svg class="model-ico" width="16" height="16" viewBox="0 0 32 32" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect width="32" height="32" rx="4" fill="#0d6336"/><path d="M16 5.5 26.5 16 16 26.5 5.5 16 16 5.5Z" stroke="#f7f4ee" stroke-width="1.4" fill="none"/><path d="M20.2 11.2c-1.1-1-2.5-1.5-4.2-1.5-3.4 0-5.6 2.2-5.6 6.3 0 4.1 2.2 6.3 5.6 6.3 1.7 0 3.1-.5 4.2-1.5" stroke="#f7f4ee" stroke-width="1.8" stroke-linecap="square" fill="none"/></svg>',
20958
+ anthropic: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="#D97757" fill-rule="nonzero"></path></svg>',
20959
+ deepseek: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" fill="#4D6BFE"></path></svg>',
20960
+ google: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="#3186FF"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-0-_R_0_)"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-1-_R_0_)"></path><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="url(#lobe-icons-gemini-2-_R_0_)"></path><defs><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-0-_R_0_" x1="7" x2="11" y1="15.5" y2="12"><stop stop-color="#08B962"></stop><stop offset="1" stop-color="#08B962" stop-opacity="0"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-1-_R_0_" x1="8" x2="11.5" y1="5.5" y2="11"><stop stop-color="#F94543"></stop><stop offset="1" stop-color="#F94543" stop-opacity="0"></stop></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-2-_R_0_" x1="3.5" x2="17.5" y1="13.5" y2="12"><stop stop-color="#FABC12"></stop><stop offset=".46" stop-color="#FABC12" stop-opacity="0"></stop></linearGradient></defs></svg>',
20961
+ groq: '<svg class="model-ico" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect width="24" height="24" rx="5" fill="#F55036"/><g transform="translate(3.2 3.2) scale(0.733)"><path d="M12.036 2c-3.853-.035-7 3-7.036 6.781-.035 3.782 3.055 6.872 6.908 6.907h2.42v-2.566h-2.292c-2.407.028-4.38-1.866-4.408-4.23-.029-2.362 1.901-4.298 4.308-4.326h.1c2.407 0 4.358 1.915 4.365 4.278v6.305c0 2.342-1.944 4.25-4.323 4.279a4.375 4.375 0 01-3.033-1.252l-1.851 1.818A7 7 0 0012.029 22h.092c3.803-.056 6.858-3.083 6.879-6.816v-6.5C18.907 4.963 15.817 2 12.036 2z"></path></g></svg>',
20962
+ kimi: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M21.846 0a1.923 1.923 0 110 3.846H20.15a.226.226 0 01-.227-.226V1.923C19.923.861 20.784 0 21.846 0z" fill="#1783FF"></path><path d="M11.065 11.199l7.257-7.2c.137-.136.06-.41-.116-.41H14.3a.164.164 0 00-.117.051l-7.82 7.756c-.122.12-.302.013-.302-.179V3.82c0-.127-.083-.23-.185-.23H3.186c-.103 0-.186.103-.186.23V19.77c0 .128.083.23.186.23h2.69c.103 0 .186-.102.186-.23v-3.25c0-.069.025-.135.069-.178l2.424-2.406a.158.158 0 01.205-.023l6.484 4.772a7.677 7.677 0 003.453 1.283c.108.012.2-.095.2-.23v-3.06c0-.117-.07-.212-.164-.227a5.028 5.028 0 01-2.027-.807l-5.613-4.064c-.117-.078-.132-.279-.028-.381z" fill="#fff"></path></svg>',
20963
+ meta: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.897 4h-.024l-.031 2.615h.022c1.715 0 3.046 1.357 5.94 6.246l.175.297.012.02 1.62-2.438-.012-.019a48.763 48.763 0 00-1.098-1.716 28.01 28.01 0 00-1.175-1.629C10.413 4.932 8.812 4 6.896 4z" fill="url(#lobe-icons-meta-0-_R_0_)"></path><path d="M6.873 4C4.95 4.01 3.247 5.258 2.02 7.17a4.352 4.352 0 00-.01.017l2.254 1.231.011-.017c.718-1.083 1.61-1.774 2.568-1.785h.021L6.896 4h-.023z" fill="url(#lobe-icons-meta-1-_R_0_)"></path><path d="M2.019 7.17l-.011.017C1.2 8.447.598 9.995.274 11.664l-.005.022 2.534.6.004-.022c.27-1.467.786-2.828 1.456-3.845l.011-.017L2.02 7.17z" fill="url(#lobe-icons-meta-2-_R_0_)"></path><path d="M2.807 12.264l-2.533-.6-.005.022c-.177.918-.267 1.851-.269 2.786v.023l2.598.233v-.023a12.591 12.591 0 01.21-2.44z" fill="url(#lobe-icons-meta-3-_R_0_)"></path><path d="M2.677 15.537a5.462 5.462 0 01-.079-.813v-.022L0 14.468v.024a8.89 8.89 0 00.146 1.652l2.535-.585a4.106 4.106 0 01-.004-.022z" fill="url(#lobe-icons-meta-4-_R_0_)"></path><path d="M3.27 16.89c-.284-.31-.484-.756-.589-1.328l-.004-.021-2.535.585.004.021c.192 1.01.568 1.85 1.106 2.487l.014.017 2.018-1.745a2.106 2.106 0 01-.015-.016z" fill="url(#lobe-icons-meta-5-_R_0_)"></path><path d="M10.78 9.654c-1.528 2.35-2.454 3.825-2.454 3.825-2.035 3.2-2.739 3.917-3.871 3.917a1.545 1.545 0 01-1.186-.508l-2.017 1.744.014.017C2.01 19.518 3.058 20 4.356 20c1.963 0 3.374-.928 5.884-5.33l1.766-3.13a41.283 41.283 0 00-1.227-1.886z" fill="#0082FB"></path><path d="M13.502 5.946l-.016.016c-.4.43-.786.908-1.16 1.416.378.483.768 1.024 1.175 1.63.48-.743.928-1.345 1.367-1.807l.016-.016-1.382-1.24z" fill="url(#lobe-icons-meta-6-_R_0_)"></path><path d="M20.918 5.713C19.853 4.633 18.583 4 17.225 4c-1.432 0-2.637.787-3.723 1.944l-.016.016 1.382 1.24.016-.017c.715-.747 1.408-1.12 2.176-1.12.826 0 1.6.39 2.27 1.075l.015.016 1.589-1.425-.016-.016z" fill="#0082FB"></path><path d="M23.998 14.125c-.06-3.467-1.27-6.566-3.064-8.396l-.016-.016-1.588 1.424.015.016c1.35 1.392 2.277 3.98 2.361 6.971v.023h2.292v-.022z" fill="url(#lobe-icons-meta-7-_R_0_)"></path><path d="M23.998 14.15v-.023h-2.292v.022c.004.14.006.282.006.424 0 .815-.121 1.474-.368 1.95l-.011.022 1.708 1.782.013-.02c.62-.96.946-2.293.946-3.91 0-.083 0-.165-.002-.247z" fill="url(#lobe-icons-meta-8-_R_0_)"></path><path d="M21.344 16.52l-.011.02c-.214.402-.519.67-.917.787l.778 2.462a3.493 3.493 0 00.438-.182 3.558 3.558 0 001.366-1.218l.044-.065.012-.02-1.71-1.784z" fill="url(#lobe-icons-meta-9-_R_0_)"></path><path d="M19.92 17.393c-.262 0-.492-.039-.718-.14l-.798 2.522c.449.153.927.222 1.46.222.492 0 .943-.073 1.352-.215l-.78-2.462c-.167.05-.341.075-.517.073z" fill="url(#lobe-icons-meta-10-_R_0_)"></path><path d="M18.323 16.534l-.014-.017-1.836 1.914.016.017c.637.682 1.246 1.105 1.937 1.337l.797-2.52c-.291-.125-.573-.353-.9-.731z" fill="url(#lobe-icons-meta-11-_R_0_)"></path><path d="M18.309 16.515c-.55-.642-1.232-1.712-2.303-3.44l-1.396-2.336-.011-.02-1.62 2.438.012.02.989 1.668c.959 1.61 1.74 2.774 2.493 3.585l.016.016 1.834-1.914a2.353 2.353 0 01-.014-.017z" fill="url(#lobe-icons-meta-12-_R_0_)"></path><defs><linearGradient id="lobe-icons-meta-0-_R_0_" x1="75.897%" x2="26.312%" y1="89.199%" y2="12.194%"><stop offset=".06%" stop-color="#0867DF"></stop><stop offset="45.39%" stop-color="#0668E1"></stop><stop offset="85.91%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-1-_R_0_" x1="21.67%" x2="97.068%" y1="75.874%" y2="23.985%"><stop offset="13.23%" stop-color="#0064DF"></stop><stop offset="99.88%" stop-color="#0064E0"></stop></linearGradient><linearGradient id="lobe-icons-meta-2-_R_0_" x1="38.263%" x2="60.895%" y1="89.127%" y2="16.131%"><stop offset="1.47%" stop-color="#0072EC"></stop><stop offset="68.81%" stop-color="#0064DF"></stop></linearGradient><linearGradient id="lobe-icons-meta-3-_R_0_" x1="47.032%" x2="52.15%" y1="90.19%" y2="15.745%"><stop offset="7.31%" stop-color="#007CF6"></stop><stop offset="99.43%" stop-color="#0072EC"></stop></linearGradient><linearGradient id="lobe-icons-meta-4-_R_0_" x1="52.155%" x2="47.591%" y1="58.301%" y2="37.004%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#007CF6"></stop></linearGradient><linearGradient id="lobe-icons-meta-5-_R_0_" x1="37.689%" x2="61.961%" y1="12.502%" y2="63.624%"><stop offset="7.31%" stop-color="#007FF9"></stop><stop offset="100%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-6-_R_0_" x1="34.808%" x2="62.313%" y1="68.859%" y2="23.174%"><stop offset="27.99%" stop-color="#007FF8"></stop><stop offset="91.41%" stop-color="#0082FB"></stop></linearGradient><linearGradient id="lobe-icons-meta-7-_R_0_" x1="43.762%" x2="57.602%" y1="6.235%" y2="98.514%"><stop offset="0%" stop-color="#0082FB"></stop><stop offset="99.95%" stop-color="#0081FA"></stop></linearGradient><linearGradient id="lobe-icons-meta-8-_R_0_" x1="60.055%" x2="39.88%" y1="4.661%" y2="69.077%"><stop offset="6.19%" stop-color="#0081FA"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-9-_R_0_" x1="30.282%" x2="61.081%" y1="59.32%" y2="33.244%"><stop offset="0%" stop-color="#027AF3"></stop><stop offset="100%" stop-color="#0080F9"></stop></linearGradient><linearGradient id="lobe-icons-meta-10-_R_0_" x1="20.433%" x2="82.112%" y1="50.001%" y2="50.001%"><stop offset="0%" stop-color="#0377EF"></stop><stop offset="99.94%" stop-color="#0279F1"></stop></linearGradient><linearGradient id="lobe-icons-meta-11-_R_0_" x1="40.303%" x2="72.394%" y1="35.298%" y2="57.811%"><stop offset=".19%" stop-color="#0471E9"></stop><stop offset="100%" stop-color="#0377EF"></stop></linearGradient><linearGradient id="lobe-icons-meta-12-_R_0_" x1="32.254%" x2="68.003%" y1="19.719%" y2="84.908%"><stop offset="27.65%" stop-color="#0867DF"></stop><stop offset="100%" stop-color="#0471E9"></stop></linearGradient></defs></svg>',
20964
+ minimax: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="lobe-icons-minimax-_R_0_" x1="0%" x2="100.182%" y1="50.057%" y2="50.057%"><stop offset="0%" stop-color="#E2167E"></stop><stop offset="100%" stop-color="#FE603C"></stop></linearGradient></defs><path d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z" fill="url(#lobe-icons-minimax-_R_0_)" fill-rule="nonzero"></path></svg>',
20965
+ mistral: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3.428 3.4h3.429v3.428H3.428V3.4zm13.714 0h3.43v3.428h-3.43V3.4z" fill="gold"></path><path d="M3.428 6.828h6.857v3.429H3.429V6.828zm10.286 0h6.857v3.429h-6.857V6.828z" fill="#FFAF00"></path><path d="M3.428 10.258h17.144v3.428H3.428v-3.428z" fill="#FF8205"></path><path d="M3.428 13.686h3.429v3.428H3.428v-3.428zm6.858 0h3.429v3.428h-3.429v-3.428zm6.856 0h3.43v3.428h-3.43v-3.428z" fill="#FA500F"></path><path d="M0 17.114h10.286v3.429H0v-3.429zm13.714 0H24v3.429H13.714v-3.429z" fill="#E10500"></path></svg>',
20966
+ nvidia: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.212 8.976V7.62c.127-.01.256-.017.388-.021 3.596-.117 5.957 3.184 5.957 3.184s-2.548 3.647-5.282 3.647a3.227 3.227 0 01-1.063-.175v-4.109c1.4.174 1.681.812 2.523 2.258l1.873-1.627a4.905 4.905 0 00-3.67-1.846 6.594 6.594 0 00-.729.044m0-4.476v2.025c.13-.01.259-.019.388-.024 5.002-.174 8.261 4.226 8.261 4.226s-3.743 4.69-7.643 4.69c-.338 0-.675-.031-1.007-.092v1.25c.278.038.558.057.838.057 3.629 0 6.253-1.91 8.794-4.169.421.347 2.146 1.193 2.501 1.564-2.416 2.083-8.048 3.763-11.24 3.763-.308 0-.603-.02-.894-.048V19.5H24v-15H10.21zm0 9.756v1.068c-3.356-.616-4.287-4.21-4.287-4.21a7.173 7.173 0 014.287-2.138v1.172h-.005a3.182 3.182 0 00-2.502 1.178s.615 2.276 2.507 2.931m-5.961-3.3c1.436-1.935 3.604-3.148 5.961-3.336V6.523C5.81 6.887 2 10.723 2 10.723s2.158 6.427 8.21 7.015v-1.166C5.77 16 4.25 10.958 4.25 10.958h-.002z" fill="#74B71B" fill-rule="nonzero"></path></svg>',
20967
+ openai: '<svg class="model-ico" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect width="24" height="24" rx="5" fill="#10A37F"/><g transform="translate(3.2 3.2) scale(0.733)"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"></path></g></svg>',
20968
+ qwen: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.604 1.34c.393.69.784 1.382 1.174 2.075a.18.18 0 00.157.091h5.552c.174 0 .322.11.446.327l1.454 2.57c.19.337.24.478.024.837-.26.43-.513.864-.76 1.3l-.367.658c-.106.196-.223.28-.04.512l2.652 4.637c.172.301.111.494-.043.77-.437.785-.882 1.564-1.335 2.34-.159.272-.352.375-.68.37-.777-.016-1.552-.01-2.327.016a.099.099 0 00-.081.05 575.097 575.097 0 01-2.705 4.74c-.169.293-.38.363-.725.364-.997.003-2.002.004-3.017.002a.537.537 0 01-.465-.271l-1.335-2.323a.09.09 0 00-.083-.049H4.982c-.285.03-.553-.001-.805-.092l-1.603-2.77a.543.543 0 01-.002-.54l1.207-2.12a.198.198 0 000-.197 550.951 550.951 0 01-1.875-3.272l-.79-1.395c-.16-.31-.173-.496.095-.965.465-.813.927-1.625 1.387-2.436.132-.234.304-.334.584-.335a338.3 338.3 0 012.589-.001.124.124 0 00.107-.063l2.806-4.895a.488.488 0 01.422-.246c.524-.001 1.053 0 1.583-.006L11.704 1c.341-.003.724.032.9.34zm-3.432.403a.06.06 0 00-.052.03L6.254 6.788a.157.157 0 01-.135.078H3.253c-.056 0-.07.025-.041.074l5.81 10.156c.025.042.013.062-.034.063l-2.795.015a.218.218 0 00-.2.116l-1.32 2.31c-.044.078-.021.118.068.118l5.716.008c.046 0 .08.02.104.061l1.403 2.454c.046.081.092.082.139 0l5.006-8.76.783-1.382a.055.055 0 01.096 0l1.424 2.53a.122.122 0 00.107.062l2.763-.02a.04.04 0 00.035-.02.041.041 0 000-.04l-2.9-5.086a.108.108 0 010-.113l.293-.507 1.12-1.977c.024-.041.012-.062-.035-.062H9.2c-.059 0-.073-.026-.043-.077l1.434-2.505a.107.107 0 000-.114L9.225 1.774a.06.06 0 00-.053-.031zm6.29 8.02c.046 0 .058.02.034.06l-.832 1.465-2.613 4.585a.056.056 0 01-.05.029.058.058 0 01-.05-.029L8.498 9.841c-.02-.034-.01-.052.028-.054l.216-.012 6.722-.012z" fill="url(#lobe-icons-qwen-_R_0_)" fill-rule="nonzero"></path><defs><linearGradient id="lobe-icons-qwen-_R_0_" x1="0%" x2="100%" y1="0%" y2="0%"><stop offset="0%" stop-color="#6336E7" stop-opacity=".84"></stop><stop offset="100%" stop-color="#6F69F7" stop-opacity=".84"></stop></linearGradient></defs></svg>',
20969
+ xai: '<svg class="model-ico" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect width="24" height="24" rx="5" fill="#111111"/><g transform="translate(3.2 3.2) scale(0.733)"><path d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815"></path></g></svg>',
20970
+ zai: '<svg class="model-ico" width="16" height="16" aria-hidden="true" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11.991 23.503a.24.24 0 00-.244.248.24.24 0 00.244.249.24.24 0 00.245-.249.24.24 0 00-.22-.247l-.025-.001zM9.671 5.365a1.697 1.697 0 011.099 2.132l-.071.172-.016.04-.018.054c-.07.16-.104.32-.104.498-.035.71.47 1.279 1.186 1.314h.366c1.309.053 2.338 1.173 2.286 2.523-.052 1.332-1.152 2.38-2.478 2.327h-.174c-.715.018-1.274.64-1.239 1.368 0 .124.018.23.053.337.209.373.54.658.96.8.75.23 1.517-.125 1.9-.782l.018-.035c.402-.64 1.17-.96 1.92-.711.854.284 1.378 1.226 1.099 2.167a1.661 1.661 0 01-2.077 1.102 1.711 1.711 0 01-.907-.711l-.017-.035c-.2-.323-.463-.58-.851-.711l-.056-.018a1.646 1.646 0 00-1.954.746 1.66 1.66 0 01-1.065.764 1.677 1.677 0 01-1.989-1.279c-.209-.906.332-1.83 1.257-2.043a1.51 1.51 0 01.296-.035h.018c.68-.071 1.151-.622 1.116-1.333a1.307 1.307 0 00-.227-.693 2.515 2.515 0 01-.366-1.403 2.39 2.39 0 01.366-1.208c.14-.195.21-.444.227-.693.018-.71-.506-1.261-1.186-1.332l-.07-.018a1.43 1.43 0 01-.299-.07l-.05-.019a1.7 1.7 0 01-1.047-2.114 1.68 1.68 0 012.094-1.101zm-5.575 10.11c.26-.264.639-.367.994-.27.355.096.633.379.728.74.095.362-.007.748-.267 1.013-.402.41-1.053.41-1.455 0a1.062 1.062 0 010-1.482zm14.845-.294c.359-.09.738.024.992.297.254.274.344.665.237 1.025-.107.36-.396.634-.756.718-.551.128-1.1-.22-1.23-.781a1.05 1.05 0 01.757-1.26zm-.064-4.39c.314.32.49.753.49 1.206 0 .452-.176.886-.49 1.206-.315.32-.74.5-1.185.5-.444 0-.87-.18-1.184-.5a1.727 1.727 0 010-2.412 1.654 1.654 0 012.369 0zm-11.243.163c.364.484.447 1.128.218 1.691a1.665 1.665 0 01-2.188.923c-.855-.36-1.26-1.358-.907-2.228a1.68 1.68 0 011.33-1.038c.593-.08 1.183.169 1.547.652zm11.545-4.221c.368 0 .708.2.892.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.892.524c-.568 0-1.03-.47-1.03-1.048 0-.579.462-1.048 1.03-1.048zm-14.358 0c.368 0 .707.2.891.524.184.324.184.724 0 1.048a1.026 1.026 0 01-.891.524c-.569 0-1.03-.47-1.03-1.048 0-.579.461-1.048 1.03-1.048zm10.031-1.475c.925 0 1.675.764 1.675 1.706s-.75 1.705-1.675 1.705-1.674-.763-1.674-1.705c0-.942.75-1.706 1.674-1.706zm-2.626-.684c.362-.082.653-.356.761-.718a1.062 1.062 0 00-.238-1.028 1.017 1.017 0 00-.996-.294c-.547.14-.881.7-.752 1.257.13.558.675.907 1.225.783zm0 16.876c.359-.087.644-.36.75-.72a1.062 1.062 0 00-.237-1.019 1.018 1.018 0 00-.985-.301 1.037 1.037 0 00-.762.717c-.108.361-.017.754.239 1.028.245.263.606.377.953.305l.043-.01zM17.19 3.5a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64a.631.631 0 00-.628.64c0 .355.28.64.628.64zm-10.38 0a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64a.631.631 0 00-.628.64c0 .355.279.64.628.64zm-5.182 7.852a.631.631 0 00-.628.64c0 .354.28.639.628.639a.63.63 0 00.627-.606l.001-.034a.62.62 0 00-.628-.64zm5.182 9.13a.631.631 0 00-.628.64c0 .355.279.64.628.64a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm10.38.018a.631.631 0 00-.628.64c0 .355.28.64.628.64a.631.631 0 00.628-.64c0-.355-.279-.64-.628-.64zm5.182-9.148a.631.631 0 00-.628.64c0 .354.279.639.628.639a.631.631 0 00.628-.64c0-.355-.28-.64-.628-.64zm-.384-4.992a.24.24 0 00.244-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249c0 .142.122.249.244.249zM11.991.497a.24.24 0 00.245-.248A.24.24 0 0011.99 0a.24.24 0 00-.244.249c0 .133.108.236.223.247l.021.001zM2.011 6.36a.24.24 0 00.245-.249.24.24 0 00-.244-.249.24.24 0 00-.244.249.24.24 0 00.244.249zm0 11.263a.24.24 0 00-.243.248.24.24 0 00.244.249.24.24 0 00.244-.249.252.252 0 00-.244-.248zm19.995-.018a.24.24 0 00-.245.248.24.24 0 00.245.25.24.24 0 00.244-.25.252.252 0 00-.244-.248z" fill="#3859FF" fill-rule="nonzero"></path></svg>',
20971
+ other: '<svg class="model-ico" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><rect width="24" height="24" rx="5" fill="#111111"/><g transform="translate(3.2 3.2) scale(0.733)"><path d="M18.654 3.87a5.087 5.087 0 110 10.174L23.7 19.09c.64.641.187 1.737-.72 1.737H8.48a8.479 8.479 0 010-16.958h10.175zM8.479 7.26a5.087 5.087 0 100 10.176 5.087 5.087 0 000-10.175z" fill="#C8FF00"></path></g></svg>'
20881
20972
  };
20973
+
20974
+ // lib/finish/critique-code-web-icons.ts
20882
20975
  function webModelProvider(modelId) {
20883
20976
  const id4 = modelId.toLowerCase();
20884
20977
  if (id4.includes("claude") || id4.includes("anthropic")) return "anthropic";
@@ -20897,17 +20990,10 @@ function webModelProvider(modelId) {
20897
20990
  if (id4.includes("critique") || id4 === "auto" || id4.endsWith("/auto")) return "critique";
20898
20991
  return "other";
20899
20992
  }
20900
- function webModelIconSvg(modelId, size = 16) {
20901
- const provider = webModelProvider(modelId);
20902
- const spec = ICONS[provider];
20903
- const fg = provider === "xai" || provider === "kimi" ? "#111" : "#fff";
20904
- const letter = spec.mark;
20905
- return `<svg class="model-ico" width="${size}" height="${size}" viewBox="0 0 16 16" aria-hidden="true"><rect width="16" height="16" rx="4" fill="${spec.color}"/><text x="8" y="11.2" text-anchor="middle" font-size="8.5" font-weight="700" font-family="ui-sans-serif,system-ui,sans-serif" fill="${fg}">${letter}</text></svg>`;
20906
- }
20907
20993
  function webModelIconMap() {
20908
20994
  const map = {};
20909
- for (const provider of Object.keys(ICONS)) {
20910
- map[provider] = webModelIconSvg(provider === "other" ? "unknown" : provider);
20995
+ for (const provider of Object.keys(LOBE_MODEL_ICON_SVGS)) {
20996
+ map[provider] = LOBE_MODEL_ICON_SVGS[provider];
20911
20997
  }
20912
20998
  return map;
20913
20999
  }
@@ -21307,17 +21393,18 @@ function critiqueCodeWebPage(input) {
21307
21393
  const currentId = input.model.replace(/^critique\/critique\//, "critique/");
21308
21394
  const options = models.map((entry) => {
21309
21395
  const selected = entry.id === input.model || entry.id === currentId ? " selected" : "";
21310
- const icon = webModelIconSvg(entry.id);
21311
- return `<option value="${escapeHtml(entry.id)}" data-provider="${webModelProvider(entry.id)}" data-icon="${escapeHtml(icon)}"${selected}>${escapeHtml(entry.label)}</option>`;
21396
+ return `<option value="${escapeHtml(entry.id)}" data-provider="${webModelProvider(entry.id)}"${selected}>${escapeHtml(entry.label)}</option>`;
21312
21397
  }).join("");
21313
21398
  return `<!DOCTYPE html>
21314
21399
  <html lang="en">
21315
21400
  <head>
21401
+ <script>try{if(localStorage.getItem("critique-code-theme")==="light")document.documentElement.setAttribute("data-theme","light")}catch(e){}</script>
21316
21402
  <meta charset="utf-8">
21317
21403
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
21318
21404
  <title>CritiqueCode</title>
21319
21405
  <style>
21320
21406
  :root {
21407
+ color-scheme:dark;
21321
21408
  --bg:#0f0f0e;
21322
21409
  --rail:#141412;
21323
21410
  --surface:#181816;
@@ -21337,26 +21424,81 @@ function critiqueCodeWebPage(input) {
21337
21424
  --primary:#f1ece4;
21338
21425
  --primary-fg:#171613;
21339
21426
  --shadow:0 24px 80px rgba(0,0,0,.48);
21427
+ --menu:#171715;
21428
+ --inset:#11110f;
21429
+ --code:#11110f;
21430
+ --term:#0d0d0c;
21431
+ --term-fg:#bbb4aa;
21432
+ --topbar:rgba(15,15,14,.86);
21433
+ --dock:linear-gradient(180deg,transparent,rgba(15,15,14,.96) 18%);
21434
+ --chip:rgba(255,255,255,.07);
21435
+ --wash:rgba(255,255,255,.03);
21436
+ --placeholder:#716b63;
21437
+ --link:#a9c8ff;
21438
+ --mono:#c5bdb1;
21439
+ --detail:#918a81;
21440
+ --reply:#e9e3da;
21441
+ --h4:#ddd6cc;
21442
+ --overlay:rgba(0,0,0,.55);
21443
+ --composer-shadow:0 16px 42px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.035);
21444
+ --diff-add-bg:rgba(85,216,140,.08);
21445
+ --diff-add-fg:#bae7ca;
21446
+ --diff-del-bg:rgba(239,119,112,.09);
21447
+ --diff-del-fg:#efc3c0;
21448
+ --diff-ctx:#aaa39a;
21449
+ --ln:#5e5953;
21450
+ --alert-fg:#f0b8b4;
21451
+ --review-fg:#d8e7dd;
21340
21452
  --ease:cubic-bezier(.2,.8,.2,1);
21341
21453
  --rail-w:252px;
21342
21454
  --changes-w:390px;
21343
21455
  --top-h:48px;
21344
21456
  }
21345
21457
  html[data-theme="light"]{
21346
- --bg:#f6f4ef;
21347
- --rail:#eeeae3;
21458
+ color-scheme:light;
21459
+ --bg:#f3f1ec;
21460
+ --rail:#e8e4db;
21348
21461
  --surface:#fff;
21349
- --surface-2:#f3f0ea;
21350
- --surface-3:#e9e4dc;
21351
- --hover:rgba(0,0,0,.045);
21352
- --line:rgba(20,18,14,.1);
21353
- --line-strong:rgba(20,18,14,.16);
21354
- --fg:#1a1814;
21355
- --muted:#5f5a52;
21356
- --faint:#8a837a;
21357
- --primary:#1a1814;
21358
- --primary-fg:#f6f4ef;
21359
- --shadow:0 24px 80px rgba(0,0,0,.12);
21462
+ --surface-2:#efece5;
21463
+ --surface-3:#e3ded4;
21464
+ --hover:rgba(22,18,12,.07);
21465
+ --line:rgba(28,22,14,.14);
21466
+ --line-strong:rgba(28,22,14,.22);
21467
+ --fg:#16130f;
21468
+ --muted:#4a453e;
21469
+ --faint:#6a645c;
21470
+ --green:#1b8a52;
21471
+ --amber:#a36b0e;
21472
+ --red:#c4453e;
21473
+ --blue:#2d5db8;
21474
+ --primary:#16130f;
21475
+ --primary-fg:#faf8f3;
21476
+ --shadow:0 16px 44px rgba(40,28,12,.14);
21477
+ --menu:#ffffff;
21478
+ --inset:#f7f5f0;
21479
+ --code:#f6f3ec;
21480
+ --term:#f7f5f0;
21481
+ --term-fg:#3a352e;
21482
+ --topbar:rgba(243,241,236,.92);
21483
+ --dock:linear-gradient(180deg,transparent,rgba(243,241,236,.98) 20%);
21484
+ --chip:rgba(22,18,12,.07);
21485
+ --wash:rgba(22,18,12,.04);
21486
+ --placeholder:#7a746b;
21487
+ --link:#2453b0;
21488
+ --mono:#3a352e;
21489
+ --detail:#534e47;
21490
+ --reply:#1c1914;
21491
+ --h4:#3a352e;
21492
+ --overlay:rgba(28,22,14,.4);
21493
+ --composer-shadow:0 14px 36px rgba(40,28,12,.1),0 0 0 1px rgba(28,22,14,.04);
21494
+ --diff-add-bg:rgba(27,138,82,.12);
21495
+ --diff-add-fg:#145c37;
21496
+ --diff-del-bg:rgba(196,69,62,.12);
21497
+ --diff-del-fg:#922f2a;
21498
+ --diff-ctx:#4a453e;
21499
+ --ln:#8a8378;
21500
+ --alert-fg:#922f2a;
21501
+ --review-fg:#145c37;
21360
21502
  }
21361
21503
  *{box-sizing:border-box}
21362
21504
  [hidden]{display:none!important}
@@ -21384,7 +21526,7 @@ button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px soli
21384
21526
  .rail-search{position:relative}
21385
21527
  .rail-search svg{position:absolute;left:10px;top:9px;color:var(--faint);pointer-events:none}
21386
21528
  .rail .search{width:100%;height:34px;background:transparent;border:1px solid transparent;border-radius:8px;padding:0 10px 0 31px;color:var(--fg);outline:0;font-size:12px}
21387
- .rail .search:focus{border-color:var(--line);background:rgba(255,255,255,.025)}
21529
+ .rail .search:focus{border-color:var(--line);background:var(--wash)}
21388
21530
  #sessions{display:flex;flex-direction:column;gap:2px;min-height:0;overflow:auto;overscroll-behavior:contain;padding-right:1px;flex:1}
21389
21531
  .session-row{display:grid;grid-template-columns:minmax(0,1fr) 28px;gap:2px;align-items:center;border-radius:8px}
21390
21532
  .session-row:hover,.session-row.on{background:var(--hover)}
@@ -21400,14 +21542,14 @@ button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px soli
21400
21542
 
21401
21543
  /* main */
21402
21544
  .stage{display:grid;grid-template-rows:var(--top-h) minmax(0,1fr) auto auto;min-width:0;min-height:0;height:100dvh;overflow:hidden;background:var(--bg)}
21403
- .topbar{height:var(--top-h);display:flex;align-items:center;gap:10px;padding:0 14px;border-bottom:1px solid var(--line);background:rgba(15,15,14,.86);backdrop-filter:blur(16px);z-index:2}
21545
+ .topbar{height:var(--top-h);display:flex;align-items:center;gap:10px;padding:0 14px;border-bottom:1px solid var(--line);background:var(--topbar);backdrop-filter:blur(16px);z-index:2}
21404
21546
  .topbar-left{display:flex;align-items:center;gap:9px;min-width:0;flex:1}
21405
21547
  .session-title{min-width:0;font-size:12.5px;font-weight:600;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21406
21548
  .top-actions{display:flex;align-items:center;gap:5px;min-width:0}
21407
21549
  .icon-btn,.toolbar-btn{border:1px solid transparent;background:transparent;color:var(--muted);border-radius:8px;display:inline-flex;align-items:center;justify-content:center;gap:7px;height:30px;padding:0 8px;font-size:11.5px;white-space:nowrap}
21408
21550
  .icon-btn{width:30px;padding:0}
21409
21551
  .icon-btn:hover,.toolbar-btn:hover,.icon-btn.on,.toolbar-btn.on{background:var(--hover);color:var(--fg);border-color:var(--line)}
21410
- .toolbar-btn .count{min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:rgba(255,255,255,.07);display:inline-grid;place-items:center;color:var(--fg);font:10px ui-monospace,Menlo,monospace}
21552
+ .toolbar-btn .count{min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--chip);display:inline-grid;place-items:center;color:var(--fg);font:10px ui-monospace,Menlo,monospace}
21411
21553
  .top-status,.badge{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--faint);font-size:11px}
21412
21554
  .badge.err{color:var(--red)}
21413
21555
  #stop{color:var(--red);background:rgba(239,119,112,.08)}
@@ -21422,20 +21564,20 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21422
21564
  #feed.on{display:block}
21423
21565
  .turn{margin:0 0 34px;position:relative}
21424
21566
  .prompt{max-width:82%;margin:0 0 18px auto;background:var(--surface-2);border:1px solid var(--line);border-radius:16px 16px 5px 16px;padding:11px 14px;line-height:1.5;white-space:pre-wrap;word-break:break-word;color:var(--fg);font-size:13.5px}
21425
- .reply{margin:0;color:#e9e3da;font-size:14px;line-height:1.63;word-break:break-word}
21567
+ .reply{margin:0;color:var(--reply);font-size:14px;line-height:1.63;word-break:break-word}
21426
21568
  .reply>*:first-child{margin-top:0}.reply>*:last-child{margin-bottom:0}
21427
21569
  .reply h1,.reply h2,.reply h3,.reply h4{color:var(--fg);font-weight:620;letter-spacing:-.03em;line-height:1.3;margin:20px 0 9px}
21428
- .reply h1{font-size:19px}.reply h2{font-size:16.5px}.reply h3{font-size:14.5px}.reply h4{font-size:13.5px;color:#ddd6cc}
21570
+ .reply h1{font-size:19px}.reply h2{font-size:16.5px}.reply h3{font-size:14.5px}.reply h4{font-size:13.5px;color:var(--h4)}
21429
21571
  .reply p{margin:0 0 11px}.reply ul,.reply ol{margin:0 0 12px;padding-left:1.25em}.reply li{margin:0 0 4px}.reply li::marker{color:var(--faint)}
21430
- .reply a{color:#a9c8ff;text-decoration:none}.reply a:hover{text-decoration:underline}
21431
- .reply code{font:12px ui-monospace,Menlo,monospace;background:rgba(255,255,255,.055);border:1px solid var(--line);border-radius:5px;padding:1px 5px}
21432
- .reply blockquote{margin:0 0 12px;padding:7px 11px;border-left:2px solid var(--line-strong);color:var(--muted);background:rgba(255,255,255,.02)}
21572
+ .reply a{color:var(--link);text-decoration:none}.reply a:hover{text-decoration:underline}
21573
+ .reply code{font:12px ui-monospace,Menlo,monospace;background:var(--chip);border:1px solid var(--line);border-radius:5px;padding:1px 5px}
21574
+ .reply blockquote{margin:0 0 12px;padding:7px 11px;border-left:2px solid var(--line-strong);color:var(--muted);background:var(--wash)}
21433
21575
  .reply hr{border:0;border-top:1px solid var(--line);margin:16px 0}
21434
- .md-code{position:relative;background:#11110f;border:1px solid var(--line);border-radius:10px;padding:30px 12px 12px;overflow:auto;font:12px ui-monospace,Menlo,monospace;line-height:1.55}
21576
+ .md-code{position:relative;background:var(--code);border:1px solid var(--line);border-radius:10px;padding:30px 12px 12px;overflow:auto;font:12px ui-monospace,Menlo,monospace;line-height:1.55}
21435
21577
  .md-code .copy{position:absolute;right:7px;top:7px;border:1px solid var(--line);background:var(--surface);color:var(--muted);border-radius:6px;padding:3px 7px;font-size:10.5px}
21436
21578
  .reply .md-code code{background:none;border:0;padding:0}
21437
- .md-table{margin:10px 0 14px;overflow:auto;border:1px solid var(--line);border-radius:10px;background:#121210}
21438
- .reply table{width:100%;border-collapse:collapse;font-size:12.5px}.reply th,.reply td{padding:8px 10px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}.reply th{color:var(--muted);font-weight:600;font-size:11px;background:rgba(255,255,255,.025)}.reply tr:last-child td{border-bottom:0}
21579
+ .md-table{margin:10px 0 14px;overflow:auto;border:1px solid var(--line);border-radius:10px;background:var(--code)}
21580
+ .reply table{width:100%;border-collapse:collapse;font-size:12.5px}.reply th,.reply td{padding:8px 10px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}.reply th{color:var(--muted);font-weight:600;font-size:11px;background:var(--wash)}.reply tr:last-child td{border-bottom:0}
21439
21581
 
21440
21582
  /* activity: each tool is its own row */
21441
21583
  .activity{display:grid;gap:5px;margin:9px 0 13px}
@@ -21447,24 +21589,24 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21447
21589
  .tool-event.running .tool-dot{background:var(--amber);box-shadow:0 0 0 3px rgba(224,170,67,.08)}
21448
21590
  .tool-event.done .tool-dot{background:var(--green)}
21449
21591
  .tool-event.failed .tool-dot{background:var(--red)}
21450
- .tool-name{font-family:ui-monospace,Menlo,monospace;color:#c5bdb1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21592
+ .tool-name{font-family:ui-monospace,Menlo,monospace;color:var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21451
21593
  .tool-phase{color:var(--faint)}
21452
- .tool-detail{margin:2px 0 4px 21px;max-width:min(680px,calc(100vw - 64px));padding:9px 10px;border-left:1px solid var(--line);color:#918a81;white-space:pre-wrap;word-break:break-word;font:11px/1.5 ui-monospace,Menlo,monospace}
21594
+ .tool-detail{margin:2px 0 4px 21px;max-width:min(680px,calc(100vw - 64px));padding:9px 10px;border-left:1px solid var(--line);color:var(--detail);white-space:pre-wrap;word-break:break-word;font:11px/1.5 ui-monospace,Menlo,monospace}
21453
21595
  .think{margin:6px 0;color:var(--faint)}
21454
- .think>div{margin:2px 0 4px 21px;padding-left:10px;border-left:1px solid var(--line);white-space:pre-wrap;word-break:break-word;font-size:11.5px;line-height:1.5;color:#858078}
21596
+ .think>div{margin:2px 0 4px 21px;padding-left:10px;border-left:1px solid var(--line);white-space:pre-wrap;word-break:break-word;font-size:11.5px;line-height:1.5;color:var(--detail)}
21455
21597
  .note,.alert,.review-card{margin:10px 0;padding:8px 10px;border-radius:8px;line-height:1.5;word-break:break-word;font-size:12px}
21456
21598
  .note{padding:5px 2px;background:transparent;border:0;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
21457
- .alert{background:rgba(239,119,112,.07);border:1px solid rgba(239,119,112,.18);color:#f0b8b4}
21458
- .review-card{background:rgba(85,216,140,.045);border:1px solid rgba(85,216,140,.18);color:#d8e7dd}
21599
+ .alert{background:rgba(239,119,112,.07);border:1px solid rgba(239,119,112,.18);color:var(--alert-fg)}
21600
+ .review-card{background:rgba(85,216,140,.045);border:1px solid rgba(85,216,140,.18);color:var(--review-fg)}
21459
21601
  .trust{display:none}
21460
21602
  .agent{margin:6px 0;padding:7px 9px;border-left:1px solid var(--line);color:var(--muted);font-size:11.5px}.agent.running{border-left-color:var(--amber)}.agent b{color:var(--fg);font-weight:560}
21461
21603
 
21462
21604
  /* composer */
21463
- .dock{padding:8px 18px 14px;background:linear-gradient(180deg,transparent,rgba(15,15,14,.96) 18%)}
21464
- .composer{position:relative;max-width:800px;margin:0 auto;background:var(--surface);border:1px solid var(--line-strong);border-radius:17px;padding:6px;box-shadow:0 16px 42px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.035)}
21605
+ .dock{padding:8px 18px 14px;background:var(--dock)}
21606
+ .composer{position:relative;max-width:800px;margin:0 auto;background:var(--surface);border:1px solid var(--line-strong);border-radius:17px;padding:6px;box-shadow:var(--composer-shadow)}
21465
21607
  .composer-line{display:flex;align-items:flex-end;gap:5px}
21466
21608
  .composer-line textarea{flex:1;min-width:0;width:100%;min-height:44px;max-height:190px;resize:none;background:transparent;border:0;padding:9px 7px;outline:0;line-height:1.5;font-size:14px;color:var(--fg)}
21467
- .composer-line textarea::placeholder{color:#716b63}
21609
+ .composer-line textarea::placeholder{color:var(--placeholder)}
21468
21610
  .round-btn,.plus-btn,.mic-btn{width:32px;height:32px;flex:0 0 auto;border:0;border-radius:9px;background:transparent;color:var(--muted);display:grid;place-items:center}
21469
21611
  .round-btn:hover,.plus-btn:hover,.mic-btn:hover,.mic-btn.on{background:var(--hover);color:var(--fg)}
21470
21612
  .composer-foot{display:flex;align-items:center;gap:6px;padding:3px 3px 1px;min-height:36px}
@@ -21474,12 +21616,12 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21474
21616
  .pick-open:hover,#model-open:hover,.pick-open[aria-expanded="true"],#model-open[aria-expanded="true"]{background:var(--hover);border-color:var(--line);color:var(--fg)}
21475
21617
  .pick-open .caret,#model-open .caret{opacity:.45;font-size:9px}
21476
21618
  .model-ico{width:15px!important;height:15px!important;display:block;flex:0 0 auto;border-radius:4px}
21477
- .model-fallback{width:15px;height:15px;display:inline-grid;place-items:center;border-radius:4px;background:rgba(255,255,255,.08);color:var(--fg);font:700 8px ui-monospace,Menlo,monospace;flex:0 0 auto}
21478
- .pick-menu,#model-menu,#mention,.cmd-pop,.branch-menu,.ship-menu{display:none;position:absolute;z-index:20;background:#171715;border:1px solid var(--line-strong);border-radius:12px;box-shadow:var(--shadow);overflow:hidden}
21619
+ .model-fallback{width:15px;height:15px;display:inline-grid;place-items:center;border-radius:4px;background:var(--chip);color:var(--fg);font:700 8px ui-monospace,Menlo,monospace;flex:0 0 auto}
21620
+ .pick-menu,#model-menu,#mention,.cmd-pop,.branch-menu,.ship-menu{display:none;position:absolute;z-index:20;background:var(--menu);border:1px solid var(--line-strong);border-radius:12px;box-shadow:var(--shadow);overflow:hidden}
21479
21621
  .pick-menu.open,#model-menu.open,#mention.open,.cmd-pop.open,.branch-menu.open,.ship-menu.open{display:grid}
21480
21622
  .pick-menu{bottom:36px;left:0;min-width:210px;max-height:300px;overflow:auto;overscroll-behavior:contain;padding:5px}
21481
21623
  #model-menu{right:0;bottom:36px;width:310px;max-height:360px;grid-template-rows:auto minmax(0,1fr);overscroll-behavior:contain}
21482
- #model-filter{width:100%;height:39px;background:#121211;border:0;border-bottom:1px solid var(--line);padding:0 11px;outline:0;font-size:12px}
21624
+ #model-filter{width:100%;height:39px;background:var(--inset);border:0;border-bottom:1px solid var(--line);padding:0 11px;outline:0;font-size:12px}
21483
21625
  #model-list{overflow:auto;overscroll-behavior:contain;max-height:310px;padding:5px}
21484
21626
  .pick-menu button,#model-list button,#mention-list button{width:100%;min-height:34px;border:0;background:transparent;color:var(--muted);border-radius:8px;padding:7px 9px;text-align:left;display:flex;align-items:center;gap:8px;font-size:11.5px}
21485
21627
  .pick-menu button:hover,.pick-menu button.on,#model-list button:hover,#model-list button.on,#mention-list button:hover,#mention-list button.on{background:var(--hover);color:var(--fg)}
@@ -21488,35 +21630,35 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21488
21630
  #send:hover{transform:translateY(-1px);filter:brightness(1.04)}#send.steer{background:#bfe8cf;color:#142219}#send:disabled{opacity:.45;cursor:not-allowed}
21489
21631
  #review-run{height:30px;padding:0 10px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--muted);font-size:11.5px}#review-run:hover{background:var(--hover);color:var(--fg)}
21490
21632
  .autonomy-pill.auto{color:var(--amber);border-color:rgba(224,170,67,.24);background:rgba(224,170,67,.055)}
21491
- .chips{display:flex;flex-wrap:wrap;gap:4px;padding:0 7px 4px}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:7px;background:rgba(255,255,255,.055);padding:3px 7px;color:var(--muted);font-size:10.5px;cursor:pointer}
21492
- .queue{margin:2px 5px 6px;border:1px solid var(--line);border-radius:10px;background:#141412}.queue-item{padding:8px 10px;color:var(--fg);font-size:12px;white-space:pre-wrap;border-bottom:1px solid var(--line)}.queue-item button{float:right;border:0;background:transparent;color:var(--faint);font-size:10px}.queue-bar{display:flex;align-items:center;justify-content:space-between;padding:6px 9px;color:var(--muted);font-size:10.5px}.queue-bar button{border:0;background:transparent;color:var(--muted);font-size:10.5px}.queue-bar .now{color:var(--fg)}
21633
+ .chips{display:flex;flex-wrap:wrap;gap:4px;padding:0 7px 4px}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:7px;background:var(--chip);padding:3px 7px;color:var(--muted);font-size:10.5px;cursor:pointer}.chip-img{padding:3px 7px 3px 3px}.chip-img img{width:36px;height:36px;object-fit:cover;border-radius:5px;background:var(--inset)}
21634
+ .queue{margin:2px 5px 6px;border:1px solid var(--line);border-radius:10px;background:var(--inset)}.queue-item{padding:8px 10px;color:var(--fg);font-size:12px;white-space:pre-wrap;border-bottom:1px solid var(--line)}.queue-item button{float:right;border:0;background:transparent;color:var(--faint);font-size:10px}.queue-bar{display:flex;align-items:center;justify-content:space-between;padding:6px 9px;color:var(--muted);font-size:10.5px}.queue-bar button{border:0;background:transparent;color:var(--muted);font-size:10.5px}.queue-bar .now{color:var(--fg)}
21493
21635
  .cmds{position:relative}.cmd-pop{left:0;bottom:38px;width:220px;padding:5px}.cmd-pop button{height:34px;border:0;background:transparent;color:var(--muted);border-radius:8px;padding:0 9px;text-align:left;font-size:11.5px}.cmd-pop button:hover{background:var(--hover);color:var(--fg)}
21494
21636
  #mention{left:8px;bottom:calc(100% - 4px);width:min(500px,calc(100% - 16px));max-height:330px}#mention-list{overflow:auto;overscroll-behavior:contain;padding:5px}.mention-k{font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);padding:7px 9px 3px}.pick-menu button .meta,#mention-list button .meta{margin-left:auto;color:var(--faint);font-size:10px}
21495
21637
 
21496
21638
  /* terminal */
21497
- .term{border-top:1px solid var(--line);background:#0d0d0c;display:grid;grid-template-rows:auto minmax(70px,1fr) auto;max-height:220px}.term header{height:32px;display:flex;align-items:center;justify-content:space-between;padding:0 10px;color:var(--faint);font-size:10.5px}.term header>span{display:flex;align-items:center;gap:6px}.term pre{margin:0;padding:8px 11px;overflow:auto;overscroll-behavior:contain;font:11px/1.5 ui-monospace,Menlo,monospace;color:#bbb4aa}.term form{display:flex;padding:5px 8px 8px}.term input{flex:1;height:31px;background:#141412;border:1px solid var(--line);border-radius:7px;padding:0 9px;outline:0;font:11px ui-monospace,Menlo,monospace}.term button{border:0;background:transparent;color:var(--muted);font-size:10.5px}.term .pick-menu{bottom:auto;top:30px}.always-row{display:flex;align-items:center;gap:7px;color:var(--muted);font-size:11px}.always-row input{accent-color:var(--amber)}
21639
+ .term{border-top:1px solid var(--line);background:var(--term);display:grid;grid-template-rows:auto minmax(70px,1fr) auto;max-height:220px}.term header{height:32px;display:flex;align-items:center;justify-content:space-between;padding:0 10px;color:var(--faint);font-size:10.5px}.term header>span{display:flex;align-items:center;gap:6px}.term pre{margin:0;padding:8px 11px;overflow:auto;overscroll-behavior:contain;font:11px/1.5 ui-monospace,Menlo,monospace;color:var(--term-fg)}.term form{display:flex;padding:5px 8px 8px}.term input{flex:1;height:31px;background:var(--inset);border:1px solid var(--line);border-radius:7px;padding:0 9px;outline:0;font:11px ui-monospace,Menlo,monospace}.term button{border:0;background:transparent;color:var(--muted);font-size:10.5px}.term .pick-menu{bottom:auto;top:30px}.always-row{display:flex;align-items:center;gap:7px;color:var(--muted);font-size:11px}.always-row input{accent-color:var(--amber)}
21498
21640
 
21499
21641
  /* workspace */
21500
- .changes{min-width:0;min-height:0;height:100dvh;background:var(--rail);border-left:1px solid var(--line);display:grid;grid-template-rows:auto auto minmax(0,1fr);overflow:hidden}.changes>header{padding:11px 10px 8px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 8px;align-items:center}.changes h2{margin:0;font-size:12.5px;font-weight:620}.changes .sum{grid-column:1/-1;color:var(--faint);font:10.5px ui-monospace,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.work-tabs{display:flex;gap:2px;padding:0 7px 7px;border-bottom:1px solid var(--line)}.work-tabs button{height:27px;border:0;background:transparent;color:var(--muted);font-size:10.5px;border-radius:7px;padding:0 8px}.work-tabs button.on{background:var(--hover);color:var(--fg)}.work-stack{min-height:0;overflow:hidden;display:grid}.work-stack>*{min-height:0;height:100%}.workspace{min-height:0;padding:7px;display:grid;gap:6px;overflow:hidden}.workspace input{width:100%;height:32px;background:#121211;border:1px solid var(--line);border-radius:8px;padding:0 9px;outline:0;font-size:11px}.tree{min-height:0;overflow:auto;overscroll-behavior:contain;font:11px ui-monospace,Menlo,monospace}.tree button{width:100%;min-height:27px;border:0;background:transparent;color:var(--muted);border-radius:6px;text-align:left;display:flex;align-items:center;gap:6px;padding:4px 6px}.tree button:hover,.tree button.on{background:var(--hover);color:var(--fg)}.tree .twist{width:9px;flex:0 0 auto;opacity:.55;font-size:8px}.tree .kind-untracked,.tree .kind-added{color:#83d7a4}.tree .kind-modified{color:#e6c36a}.tree .kind-deleted{color:var(--red);text-decoration:line-through}.tree .kind-renamed{color:var(--blue)}
21501
- #tab-tree{grid-template-rows:auto minmax(110px,.8fr) auto minmax(180px,1.25fr)}#tab-findings,#tab-timeline{grid-template-rows:auto minmax(0,1fr)}.diff-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:7px;align-items:center;padding:5px 2px}.diff-title-wrap{min-width:0;display:flex;align-items:center;gap:8px}.diff-back{flex:0 0 auto;border:0;background:transparent;color:var(--muted);padding:3px 5px;border-radius:6px;font-size:10.5px}.diff-back:hover{color:var(--fg);background:var(--hover)}.diff-head b,#diff-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:10.5px ui-monospace,Menlo,monospace;color:var(--fg)}.diff-actions{display:flex;gap:4px}.diff-actions button{height:27px;border:1px solid var(--line);background:transparent;border-radius:7px;padding:0 7px;color:var(--muted);font-size:10px}.diff-actions button:hover{background:var(--hover);color:var(--fg)}.diff-actions .danger{color:var(--red)}.diff{min-height:0;overflow:auto;overscroll-behavior:contain;padding-bottom:14px;border:1px solid var(--line);border-radius:8px;background:#10100f;font:11px/1.5 ui-monospace,Menlo,monospace}.diff-file+.diff-file{border-top:1px solid var(--line-strong);margin-top:10px;padding-top:5px}.diff-file-head{position:sticky;top:0;z-index:2;padding:8px 10px;background:#151513;border-bottom:1px solid var(--line);color:var(--fg);font:600 11px ui-monospace,Menlo,monospace}.diff-loading{padding:14px;color:var(--faint);font-size:11px}.diff .hunk{color:var(--faint);padding:6px 8px 3px;background:rgba(255,255,255,.02)}.diff .row{display:grid;grid-template-columns:36px 36px minmax(0,1fr)}.diff .ln{color:#5e5953;text-align:right;padding:0 6px;user-select:none}.diff .add{background:rgba(85,216,140,.08);color:#bae7ca}.diff .del{background:rgba(239,119,112,.09);color:#efc3c0}.diff .ctx{color:#aaa39a}.diff .code{white-space:pre-wrap;word-break:break-word;padding-right:7px}#file-view{margin:0;white-space:pre-wrap;font:11px/1.5 ui-monospace,Menlo,monospace;padding:10px;color:var(--muted);overflow:auto;border:1px solid var(--line);border-radius:8px;background:#10100f}#preview{width:100%;height:100%;min-height:0;border:0;background:#fff;border-radius:8px}.compare{display:grid;grid-template-columns:1fr 1fr;gap:7px;padding:8px;font:11px ui-monospace,Menlo,monospace;overflow:auto}.find-row{width:100%;text-align:left;border:0;background:transparent;color:var(--muted);padding:7px;border-radius:7px;font-size:11px}.find-row:hover{background:var(--hover);color:var(--fg)}.change-list{display:none}.gate{display:none}
21642
+ .changes{min-width:0;min-height:0;height:100dvh;background:var(--rail);border-left:1px solid var(--line);display:grid;grid-template-rows:auto auto minmax(0,1fr);overflow:hidden}.changes>header{padding:11px 10px 8px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 8px;align-items:center}.changes h2{margin:0;font-size:12.5px;font-weight:620}.changes .sum{grid-column:1/-1;color:var(--faint);font:10.5px ui-monospace,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.work-tabs{display:flex;gap:2px;padding:0 7px 7px;border-bottom:1px solid var(--line)}.work-tabs button{height:27px;border:0;background:transparent;color:var(--muted);font-size:10.5px;border-radius:7px;padding:0 8px}.work-tabs button.on{background:var(--hover);color:var(--fg)}.work-stack{min-height:0;overflow:hidden;display:grid}.work-stack>*{min-height:0;height:100%}.workspace{min-height:0;padding:7px;display:grid;gap:6px;overflow:hidden}.workspace input{width:100%;height:32px;background:var(--inset);border:1px solid var(--line);border-radius:8px;padding:0 9px;outline:0;font-size:11px}.tree{min-height:0;overflow:auto;overscroll-behavior:contain;font:11px ui-monospace,Menlo,monospace}.tree button{width:100%;min-height:27px;border:0;background:transparent;color:var(--muted);border-radius:6px;text-align:left;display:flex;align-items:center;gap:6px;padding:4px 6px}.tree button:hover,.tree button.on{background:var(--hover);color:var(--fg)}.tree .twist{width:9px;flex:0 0 auto;opacity:.55;font-size:8px}.tree .kind-untracked,.tree .kind-added{color:var(--green)}.tree .kind-modified{color:var(--amber)}.tree .kind-deleted{color:var(--red);text-decoration:line-through}.tree .kind-renamed{color:var(--blue)}
21643
+ #tab-tree{grid-template-rows:auto minmax(110px,.8fr) auto minmax(180px,1.25fr)}#tab-findings,#tab-timeline{grid-template-rows:auto minmax(0,1fr)}.diff-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:7px;align-items:center;padding:5px 2px}.diff-title-wrap{min-width:0;display:flex;align-items:center;gap:8px}.diff-back{flex:0 0 auto;border:0;background:transparent;color:var(--muted);padding:3px 5px;border-radius:6px;font-size:10.5px}.diff-back:hover{color:var(--fg);background:var(--hover)}.diff-head b,#diff-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:10.5px ui-monospace,Menlo,monospace;color:var(--fg)}.diff-actions{display:flex;gap:4px}.diff-actions button{height:27px;border:1px solid var(--line);background:transparent;border-radius:7px;padding:0 7px;color:var(--muted);font-size:10px}.diff-actions button:hover{background:var(--hover);color:var(--fg)}.diff-actions .danger{color:var(--red)}.diff{min-height:0;overflow:auto;overscroll-behavior:contain;padding-bottom:14px;border:1px solid var(--line);border-radius:8px;background:var(--code);font:11px/1.5 ui-monospace,Menlo,monospace}.diff-file+.diff-file{border-top:1px solid var(--line-strong);margin-top:10px;padding-top:5px}.diff-file-head{position:sticky;top:0;z-index:2;padding:8px 10px;background:var(--surface-2);border-bottom:1px solid var(--line);color:var(--fg);font:600 11px ui-monospace,Menlo,monospace}.diff-loading{padding:14px;color:var(--faint);font-size:11px}.diff .hunk{color:var(--faint);padding:6px 8px 3px;background:var(--wash)}.diff .row{display:grid;grid-template-columns:36px 36px minmax(0,1fr)}.diff .ln{color:var(--ln);text-align:right;padding:0 6px;user-select:none}.diff .add{background:var(--diff-add-bg);color:var(--diff-add-fg)}.diff .del{background:var(--diff-del-bg);color:var(--diff-del-fg)}.diff .ctx{color:var(--diff-ctx)}.diff .code{white-space:pre-wrap;word-break:break-word;padding-right:7px}#file-view{margin:0;white-space:pre-wrap;font:11px/1.5 ui-monospace,Menlo,monospace;padding:10px;color:var(--muted);overflow:auto;border:1px solid var(--line);border-radius:8px;background:var(--code)}#preview{width:100%;height:100%;min-height:0;border:0;background:#fff;border-radius:8px}.compare{display:grid;grid-template-columns:1fr 1fr;gap:7px;padding:8px;font:11px ui-monospace,Menlo,monospace;overflow:auto}.find-row{width:100%;text-align:left;border:0;background:transparent;color:var(--muted);padding:7px;border-radius:7px;font-size:11px}.find-row:hover{background:var(--hover);color:var(--fg)}.change-list{display:none}.gate{display:none}
21502
21644
 
21503
21645
  /* overlays */
21504
- .sheet{display:none;position:fixed;inset:0;z-index:50;background:rgba(0,0,0,.55);align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(4px)}.sheet.open{display:flex}.sheet .card{width:min(640px,95vw);max-height:84vh;overflow:auto;overscroll-behavior:contain;background:#161614;border:1px solid var(--line-strong);border-radius:14px;padding:16px;box-shadow:var(--shadow)}.sheet h3{margin:0 0 12px;font-size:15px}.sheet label{display:block;color:var(--muted);font-size:11px;margin:9px 0 4px}.sheet input,.sheet select,.sheet textarea.plain{width:100%;background:#11110f;border:1px solid var(--line);border-radius:8px;padding:8px;outline:0}.modal-actions{display:flex;justify-content:flex-end;gap:7px;flex-wrap:wrap;margin-top:12px}.modal-actions button{min-height:32px;border:1px solid var(--line);background:var(--surface);border-radius:8px;padding:0 10px;color:var(--muted)}.modal-actions button:hover{color:var(--fg);border-color:var(--line-strong)}.modal-actions .deny{color:var(--red)}
21505
- #modal{display:none;position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.58);align-items:center;justify-content:center;padding:20px}#modal.open{display:flex}.dialog{width:min(520px,100%);background:#161614;border:1px solid var(--line-strong);border-radius:14px;padding:16px;box-shadow:var(--shadow)}.dialog h3{margin:0 0 7px;font-size:15px}.dialog .purpose{color:var(--muted);font-size:12px}.preview{max-height:240px;overflow:auto;overscroll-behavior:contain;white-space:pre-wrap;word-break:break-word;margin:11px 0 14px;padding:10px;background:#10100f;border:1px solid var(--line);border-radius:8px;font:11px/1.5 ui-monospace,Menlo,monospace;color:var(--muted)}
21506
- .palette{display:none;position:fixed;z-index:65;inset:12vh 0 auto;margin:auto;width:min(560px,92vw);background:#161614;border:1px solid var(--line-strong);border-radius:13px;padding:7px;box-shadow:var(--shadow)}.palette.open{display:block}.palette input{width:100%;height:39px;background:#11110f;border:1px solid var(--line);border-radius:8px;padding:0 10px;outline:0}.palette button{width:100%;min-height:34px;border:0;background:transparent;color:var(--muted);text-align:left;border-radius:8px;padding:6px 8px;font-size:11.5px}.palette button:hover{background:var(--hover);color:var(--fg)}
21646
+ .sheet{display:none;position:fixed;inset:0;z-index:50;background:var(--overlay);align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(4px)}.sheet.open{display:flex}.sheet .card{width:min(640px,95vw);max-height:84vh;overflow:auto;overscroll-behavior:contain;background:var(--menu);border:1px solid var(--line-strong);border-radius:14px;padding:16px;box-shadow:var(--shadow)}.sheet h3{margin:0 0 12px;font-size:15px}.sheet label{display:block;color:var(--muted);font-size:11px;margin:9px 0 4px}.sheet input,.sheet select,.sheet textarea.plain{width:100%;background:var(--inset);border:1px solid var(--line);border-radius:8px;padding:8px;outline:0}.modal-actions{display:flex;justify-content:flex-end;gap:7px;flex-wrap:wrap;margin-top:12px}.modal-actions button{min-height:32px;border:1px solid var(--line);background:var(--surface);border-radius:8px;padding:0 10px;color:var(--muted)}.modal-actions button:hover{color:var(--fg);border-color:var(--line-strong)}.modal-actions .deny{color:var(--red)}
21647
+ #modal{display:none;position:fixed;inset:0;z-index:60;background:var(--overlay);align-items:center;justify-content:center;padding:20px}#modal.open{display:flex}.dialog{width:min(520px,100%);background:var(--menu);border:1px solid var(--line-strong);border-radius:14px;padding:16px;box-shadow:var(--shadow)}.dialog h3{margin:0 0 7px;font-size:15px}.dialog .purpose{color:var(--muted);font-size:12px}.preview{max-height:240px;overflow:auto;overscroll-behavior:contain;white-space:pre-wrap;word-break:break-word;margin:11px 0 14px;padding:10px;background:var(--inset);border:1px solid var(--line);border-radius:8px;font:11px/1.5 ui-monospace,Menlo,monospace;color:var(--muted)}
21648
+ .palette{display:none;position:fixed;z-index:65;inset:12vh 0 auto;margin:auto;width:min(560px,92vw);background:var(--menu);border:1px solid var(--line-strong);border-radius:13px;padding:7px;box-shadow:var(--shadow)}.palette.open{display:block}.palette input{width:100%;height:39px;background:var(--inset);border:1px solid var(--line);border-radius:8px;padding:0 10px;outline:0}.palette button{width:100%;min-height:34px;border:0;background:transparent;color:var(--muted);text-align:left;border-radius:8px;padding:6px 8px;font-size:11.5px}.palette button:hover{background:var(--hover);color:var(--fg)}
21507
21649
 
21508
21650
  /* command browser */
21509
- .command-card{width:min(720px,95vw)!important;padding:0!important;overflow:hidden!important}.sheet-head{height:50px;padding:0 15px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}.sheet-head h3{margin:0}.sheet-head button{border:0;background:transparent;color:var(--muted);width:30px;height:30px;border-radius:7px}.sheet-head button:hover{background:var(--hover);color:var(--fg)}.command-body{padding:12px;display:grid;grid-template-rows:auto minmax(0,1fr);gap:9px;max-height:72vh}.command-body input{margin:0}.command-list{min-height:0;overflow:auto;overscroll-behavior:contain;display:grid;gap:3px}.command-row{display:grid;grid-template-columns:140px minmax(0,1fr) auto;gap:10px;align-items:start;border:0;background:transparent;border-radius:9px;padding:9px 10px;text-align:left;color:var(--muted)}.command-row:hover{background:var(--hover);color:var(--fg)}.command-code{font:11px ui-monospace,Menlo,monospace;color:#d2cbc1}.command-desc{font-size:11.5px;line-height:1.4}.command-kind{font-size:9.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.06em}
21651
+ .command-card{width:min(720px,95vw)!important;padding:0!important;overflow:hidden!important}.sheet-head{height:50px;padding:0 15px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}.sheet-head h3{margin:0}.sheet-head button{border:0;background:transparent;color:var(--muted);width:30px;height:30px;border-radius:7px}.sheet-head button:hover{background:var(--hover);color:var(--fg)}.command-body{padding:12px;display:grid;grid-template-rows:auto minmax(0,1fr);gap:9px;max-height:72vh}.command-body input{margin:0}.command-list{min-height:0;overflow:auto;overscroll-behavior:contain;display:grid;gap:3px}.command-row{display:grid;grid-template-columns:140px minmax(0,1fr) auto;gap:10px;align-items:start;border:0;background:transparent;border-radius:9px;padding:9px 10px;text-align:left;color:var(--muted)}.command-row:hover{background:var(--hover);color:var(--fg)}.command-code{font:11px ui-monospace,Menlo,monospace;color:var(--mono)}.command-desc{font-size:11.5px;line-height:1.4}.command-kind{font-size:9.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.06em}
21510
21652
 
21511
21653
  /* settings */
21512
- .settings-card{width:min(980px,96vw)!important;height:min(720px,92vh);max-height:92vh!important;padding:0!important;overflow:hidden!important;display:grid;grid-template-columns:190px minmax(0,1fr);background:#151513!important}.settings-nav{border-right:1px solid var(--line);padding:13px 9px;display:flex;flex-direction:column;gap:3px;min-height:0}.settings-nav-head{display:flex;align-items:center;justify-content:space-between;padding:4px 7px 12px}.settings-nav-head b{font-size:13px}.settings-nav button[data-settings-tab]{height:33px;border:0;background:transparent;color:var(--muted);border-radius:8px;text-align:left;padding:0 9px;font-size:11.5px}.settings-nav button[data-settings-tab]:hover,.settings-nav button[data-settings-tab].on{background:var(--hover);color:var(--fg)}.settings-close{width:28px;height:28px;border:0;background:transparent;color:var(--muted);border-radius:7px}.settings-close:hover{background:var(--hover);color:var(--fg)}.settings-content{min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;padding:24px 26px 34px}.settings-panel{display:none;max-width:720px}.settings-panel.on{display:block}.settings-panel h2{margin:0 0 4px;font-size:18px;letter-spacing:-.03em}.settings-panel>.sub{margin:0 0 22px;color:var(--muted);font-size:12px;line-height:1.5}.setting-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:24px;align-items:center;padding:13px 0;border-top:1px solid var(--line)}.setting-copy b{display:block;font-size:12.5px;font-weight:580;margin-bottom:3px}.setting-copy span{display:block;color:var(--muted);font-size:11.5px;line-height:1.45}.setting-control{min-width:190px;display:flex;justify-content:flex-end}.setting-control .pick-menu{bottom:auto;top:36px;right:0;left:auto;max-height:300px;overflow:auto;overscroll-behavior:contain}.setting-control .pick-open{border-color:var(--line);background:rgba(255,255,255,.025);min-width:180px;justify-content:space-between}.setting-control input[type=password]{width:240px;height:34px;background:#11110f;border:1px solid var(--line);border-radius:8px;padding:0 9px}.settings-note{border:1px solid var(--line);background:rgba(255,255,255,.025);border-radius:9px;padding:10px 11px;color:var(--muted);font-size:11.5px;line-height:1.5}.settings-note.warn{border-color:rgba(224,170,67,.22);background:rgba(224,170,67,.045)}
21513
- .usage-cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin:14px 0 18px}.usage-card{border:1px solid var(--line);border-radius:10px;padding:10px;background:rgba(255,255,255,.018)}.usage-card span{display:block;color:var(--faint);font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}.usage-card b{font:600 17px ui-monospace,Menlo,monospace;color:var(--fg)}.usage-chart{height:180px;border:1px solid var(--line);border-radius:10px;background:#11110f;padding:9px;margin-bottom:16px}.usage-chart svg{display:block;width:100%;height:100%;overflow:visible}.usage-empty{height:100%;display:grid;place-items:center;color:var(--faint);font-size:11px}.breakdown{display:grid;gap:7px;margin:8px 0 20px}.break-row{display:grid;grid-template-columns:120px minmax(0,1fr) 90px;gap:9px;align-items:center;font-size:10.5px;color:var(--muted)}.break-track{height:6px;background:rgba(255,255,255,.05);border-radius:999px;overflow:hidden}.break-fill{height:100%;background:#847d74;border-radius:999px}.break-val{text-align:right;font:10.5px ui-monospace,Menlo,monospace;color:var(--faint)}
21514
- .skill-list{display:grid;gap:7px}.skill-row{border:1px solid var(--line);border-radius:9px;padding:10px 11px;background:rgba(255,255,255,.015)}.skill-head{display:flex;align-items:center;gap:8px;margin-bottom:4px}.skill-head b{font-size:12px}.skill-source{padding:2px 6px;border-radius:999px;background:rgba(255,255,255,.055);color:var(--faint);font-size:9.5px}.skill-desc{color:var(--muted);font-size:11.5px;line-height:1.45}.skill-origin{margin-top:5px;color:var(--faint);font:9.5px ui-monospace,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.audit-list{display:grid;gap:5px}.audit-row{display:grid;grid-template-columns:120px 90px minmax(0,1fr);gap:8px;padding:7px 0;border-top:1px solid var(--line);color:var(--muted);font-size:10.5px}.audit-row code{color:#c9c1b7;font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21654
+ .settings-card{width:min(980px,96vw)!important;height:min(720px,92vh);max-height:92vh!important;padding:0!important;overflow:hidden!important;display:grid;grid-template-columns:190px minmax(0,1fr);background:var(--menu)!important}.settings-nav{border-right:1px solid var(--line);padding:13px 9px;display:flex;flex-direction:column;gap:3px;min-height:0}.settings-nav-head{display:flex;align-items:center;justify-content:space-between;padding:4px 7px 12px}.settings-nav-head b{font-size:13px}.settings-nav button[data-settings-tab]{height:33px;border:0;background:transparent;color:var(--muted);border-radius:8px;text-align:left;padding:0 9px;font-size:11.5px}.settings-nav button[data-settings-tab]:hover,.settings-nav button[data-settings-tab].on{background:var(--hover);color:var(--fg)}.settings-close{width:28px;height:28px;border:0;background:transparent;color:var(--muted);border-radius:7px}.settings-close:hover{background:var(--hover);color:var(--fg)}.settings-content{min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;padding:24px 26px 34px}.settings-panel{display:none;max-width:720px}.settings-panel.on{display:block}.settings-panel h2{margin:0 0 4px;font-size:18px;letter-spacing:-.03em}.settings-panel>.sub{margin:0 0 22px;color:var(--muted);font-size:12px;line-height:1.5}.setting-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:24px;align-items:center;padding:13px 0;border-top:1px solid var(--line)}.setting-copy b{display:block;font-size:12.5px;font-weight:580;margin-bottom:3px}.setting-copy span{display:block;color:var(--muted);font-size:11.5px;line-height:1.45}.setting-control{min-width:190px;display:flex;justify-content:flex-end}.setting-control .pick-menu{bottom:auto;top:36px;right:0;left:auto;max-height:300px;overflow:auto;overscroll-behavior:contain}.setting-control .pick-open{border-color:var(--line);background:var(--wash);min-width:180px;justify-content:space-between}.setting-control input[type=password]{width:240px;height:34px;background:var(--inset);border:1px solid var(--line);border-radius:8px;padding:0 9px}.settings-note{border:1px solid var(--line);background:var(--wash);border-radius:9px;padding:10px 11px;color:var(--muted);font-size:11.5px;line-height:1.5}.settings-note.warn{border-color:rgba(224,170,67,.22);background:rgba(224,170,67,.045)}
21655
+ .usage-cards{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin:14px 0 18px}.usage-card{border:1px solid var(--line);border-radius:10px;padding:10px;background:var(--wash)}.usage-card span{display:block;color:var(--faint);font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}.usage-card b{font:600 17px ui-monospace,Menlo,monospace;color:var(--fg)}.usage-chart{height:180px;border:1px solid var(--line);border-radius:10px;background:var(--code);padding:9px;margin-bottom:16px}.usage-chart svg{display:block;width:100%;height:100%;overflow:visible;color:var(--muted)}.usage-empty{height:100%;display:grid;place-items:center;color:var(--faint);font-size:11px}.breakdown{display:grid;gap:7px;margin:8px 0 20px}.break-row{display:grid;grid-template-columns:120px minmax(0,1fr) 90px;gap:9px;align-items:center;font-size:10.5px;color:var(--muted)}.break-track{height:6px;background:var(--chip);border-radius:999px;overflow:hidden}.break-fill{height:100%;background:var(--muted);border-radius:999px}.break-val{text-align:right;font:10.5px ui-monospace,Menlo,monospace;color:var(--faint)}
21656
+ .skill-list{display:grid;gap:7px}.skill-row{border:1px solid var(--line);border-radius:9px;padding:10px 11px;background:var(--wash)}.skill-head{display:flex;align-items:center;gap:8px;margin-bottom:4px}.skill-head b{font-size:12px}.skill-source{padding:2px 6px;border-radius:999px;background:var(--chip);color:var(--faint);font-size:9.5px}.skill-desc{color:var(--muted);font-size:11.5px;line-height:1.45}.skill-origin{margin-top:5px;color:var(--faint);font:9.5px ui-monospace,Menlo,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.audit-list{display:grid;gap:5px}.audit-row{display:grid;grid-template-columns:120px 90px minmax(0,1fr);gap:8px;padding:7px 0;border-top:1px solid var(--line);color:var(--muted);font-size:10.5px}.audit-row code{color:var(--mono);font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21515
21657
  #mode,#autonomy,#thinking,#review-depth,#review-lens,#term-timeout,#studio-author,#studio-review,#key-kind{position:absolute;opacity:0;pointer-events:none;width:0;height:0}.hidden-config{display:none!important}
21516
21658
  .branch-menu{left:0;bottom:36px;min-width:220px;max-height:260px;overflow:auto;overscroll-behavior:contain;padding:5px}.branch-menu button,.ship-menu button{width:100%;border:0;background:transparent;color:var(--muted);padding:7px 9px;text-align:left;border-radius:7px;font-size:11px}.branch-menu button:hover,.ship-menu button:hover{background:var(--hover);color:var(--fg)}.ship-menu{right:0;bottom:36px;min-width:210px;padding:5px}.ship-wrap{position:relative}.meta-btn{height:29px;border:1px solid var(--line);background:transparent;border-radius:8px;padding:0 8px;color:var(--muted);font-size:10.5px}.meta-btn:hover{background:var(--hover);color:var(--fg)}
21517
21659
 
21518
21660
  /* resume picker */
21519
- .resume-picker{border-radius:14px;background:#161614;overflow:hidden;border:1px solid var(--line)}.resume-head{display:flex;justify-content:space-between;align-items:center;padding:11px 13px;border-bottom:1px solid var(--line)}.resume-head strong{font-size:12px;font-weight:600}.resume-head span{color:var(--faint);font-size:10.5px}.resume-list{padding:5px;max-height:330px;overflow:auto;overscroll-behavior:contain}.resume-chat{width:100%;border:0;background:transparent;color:var(--fg);padding:9px 10px;border-radius:8px;display:flex;align-items:center;gap:12px;text-align:left}.resume-chat:hover{background:var(--hover)}.resume-copy{min-width:0;flex:1;display:grid;gap:3px}.resume-copy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500}.resume-copy span{color:var(--faint);font-size:10px}.resume-arrow{color:var(--faint);font-size:18px}.resume-empty{padding:18px 12px;color:var(--faint);text-align:center;font-size:11px}
21661
+ .resume-picker{border-radius:14px;background:var(--menu);overflow:hidden;border:1px solid var(--line)}.resume-head{display:flex;justify-content:space-between;align-items:center;padding:11px 13px;border-bottom:1px solid var(--line)}.resume-head strong{font-size:12px;font-weight:600}.resume-head span{color:var(--faint);font-size:10.5px}.resume-list{padding:5px;max-height:330px;overflow:auto;overscroll-behavior:contain}.resume-chat{width:100%;border:0;background:transparent;color:var(--fg);padding:9px 10px;border-radius:8px;display:flex;align-items:center;gap:12px;text-align:left}.resume-chat:hover{background:var(--hover)}.resume-copy{min-width:0;flex:1;display:grid;gap:3px}.resume-copy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500}.resume-copy span{color:var(--faint);font-size:10px}.resume-arrow{color:var(--faint);font-size:18px}.resume-empty{padding:18px 12px;color:var(--faint);text-align:center;font-size:11px}
21520
21662
 
21521
21663
  @media(max-width:1100px){.shell,.shell.changes-off{grid-template-columns:var(--rail-w) minmax(0,1fr)}.shell.rail-off,.shell.rail-off.changes-off{grid-template-columns:minmax(0,1fr)}.gutter{display:none}.changes{display:none}.shell:not(.changes-off) .changes,.changes.open{display:grid;position:fixed;inset:0 0 0 auto;width:min(420px,100%);z-index:40;box-shadow:-18px 0 50px rgba(0,0,0,.45)}}
21522
21664
  @media(max-width:760px){:root{--top-h:46px}.shell,.shell.rail-off{grid-template-columns:1fr}.rail{display:none}.shell:not(.rail-off) .rail{display:flex;position:fixed;inset:0 auto 0 0;width:min(285px,88vw);z-index:45;box-shadow:20px 0 50px rgba(0,0,0,.5)}.topbar{padding:0 10px}.top-actions .toolbar-label{display:none}.top-actions{gap:2px}.toolbar-btn{padding:0 7px}.dock{padding:6px 9px calc(10px + env(safe-area-inset-bottom))}.composer{border-radius:15px}.composer-line textarea{font-size:16px;min-height:46px}.composer-foot{padding-top:2px}.composer-left .pick[data-pick="mode"],.composer-left .pick[data-pick="autonomy"]{display:none}#model-open{max-width:155px}.prompt{max-width:90%;font-size:13.5px}#feed{padding:22px 15px 30px}.settings-card{width:100vw!important;height:100dvh;max-height:100dvh!important;border-radius:0!important;border:0!important;grid-template-columns:1fr}.settings-nav{border-right:0;border-bottom:1px solid var(--line);padding:8px;display:grid;grid-template-columns:1fr auto;gap:5px}.settings-nav-head{grid-column:1/-1;padding:2px 4px 4px}.settings-nav button[data-settings-tab]{display:none}.settings-nav button[data-settings-tab].on{display:none}.settings-mobile-tabs{display:flex!important;grid-column:1/-1;gap:3px;overflow:auto;padding-bottom:1px}.settings-mobile-tabs button{display:block!important;flex:0 0 auto;height:29px!important;padding:0 8px!important}.settings-content{padding:18px 15px 30px}.setting-row{grid-template-columns:1fr;gap:8px}.setting-control{justify-content:flex-start;min-width:0}.setting-control .pick-open{width:100%;max-width:100%}.setting-control .pick{width:100%}.setting-control .pick-menu{left:0;right:auto;width:100%;max-width:100%}.usage-cards{grid-template-columns:1fr 1fr}.break-row{grid-template-columns:88px minmax(0,1fr) 68px}.command-row{grid-template-columns:110px minmax(0,1fr)}.command-kind{display:none}.sheet{padding:0}.command-card{width:100vw!important;height:100dvh;max-height:100dvh!important;border-radius:0!important}.command-body{max-height:calc(100dvh - 50px)}#empty{margin-top:12vh;padding:0 18px}.top-status{display:none}.diff-head{min-height:39px;gap:6px}.diff-title-wrap{overflow:hidden}#diff-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.diff-actions{overflow-x:auto;max-width:52vw;scrollbar-width:none}.diff-actions::-webkit-scrollbar{display:none}.diff-actions button{flex:0 0 auto}.diff,#file-view{border-radius:10px;font-size:10.5px}.diff .row{grid-template-columns:30px 30px minmax(0,1fr)}.diff .ln{padding:0 4px}.diff-file-head{padding:8px}.resume-picker{width:100%;margin:12px 0 24px;border-radius:12px}.resume-list{max-height:min(360px,48dvh)}.resume-chat{min-height:48px}}
@@ -21624,7 +21766,7 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21624
21766
  <button type="button" data-cmd="/login">Login / connect inference</button>
21625
21767
  </div>
21626
21768
  </div>
21627
- <textarea id="input" rows="1" placeholder="Message CritiqueCode\u2026" title="Type / for commands and skills, @ to add context"></textarea>
21769
+ <textarea id="input" rows="1" placeholder="Message CritiqueCode\u2026 Paste or drop an image." title="Type / for commands and skills, @ to add context. Paste a screenshot to attach it."></textarea>
21628
21770
  <button type="button" class="mic-btn" id="voice" title="Voice input" aria-label="Voice input">
21629
21771
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="5" y="2" width="4" height="7" rx="2" stroke="currentColor" stroke-width="1.2"/><path d="M3.5 7.5a3.5 3.5 0 0 0 7 0M7 11v1.5" stroke="currentColor" stroke-width="1.2"/></svg>
21630
21772
  </button>
@@ -21797,6 +21939,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
21797
21939
  var sessionTitleEl = document.getElementById("session-title");
21798
21940
  var activeSessionId = null;
21799
21941
  var refs = [];
21942
+ var pendingImages = [];
21800
21943
  var sessionMentions = [];
21801
21944
  var treePaths = [];
21802
21945
  var collapsedDirs = {};
@@ -21897,7 +22040,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
21897
22040
 
21898
22041
  function modelIconHtml(option) {
21899
22042
  var provider = option && (option.getAttribute("data-provider") || "") || "other";
21900
- var raw = option && (option.getAttribute("data-icon") || iconMap[provider] || "");
22043
+ var raw = iconMap[provider] || iconMap.other || "";
21901
22044
  if (raw) return raw;
21902
22045
  var label = option && (option.text || option.value) || provider || "model";
21903
22046
  var letter = String(label).replace(/^[^a-z0-9]+/i, "").charAt(0).toUpperCase() || "M";
@@ -22000,7 +22143,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22000
22143
  btn.type = "button";
22001
22144
  btn.className = option.selected ? "on" : "";
22002
22145
  btn.textContent = "";
22003
- var icon = option.getAttribute("data-icon") || option.getAttribute("data-provider") ? modelIconHtml(option) : "";
22146
+ var icon = option.getAttribute("data-provider") ? modelIconHtml(option) : "";
22004
22147
  if (icon) btn.insertAdjacentHTML("beforeend", icon);
22005
22148
  var name = document.createElement("span");
22006
22149
  name.textContent = option.text || option.value;
@@ -22287,7 +22430,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22287
22430
  promptQueue.forEach(function (item, index) {
22288
22431
  var row = document.createElement("div");
22289
22432
  row.className = "queue-item";
22290
- row.textContent = item;
22433
+ var labelText = typeof item === "string" ? item : (item.text || (item.images && item.images.length ? item.images.length + " image" : ""));
22434
+ row.textContent = labelText;
22291
22435
  var rm = document.createElement("button");
22292
22436
  rm.type = "button";
22293
22437
  rm.textContent = "Remove";
@@ -22302,12 +22446,13 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22302
22446
  label.textContent = n ? n + " Queued \u21B5 to Send" : "";
22303
22447
  }
22304
22448
 
22305
- function enqueuePrompt(text) {
22306
- promptQueue.push(text);
22449
+ function enqueuePrompt(text, images) {
22450
+ promptQueue.push({ text: text, images: images || [] });
22307
22451
  renderQueue();
22308
22452
  }
22309
22453
 
22310
- function dispatchPrompt(text) {
22454
+ function dispatchPrompt(text, images) {
22455
+ var payloadImages = images || [];
22311
22456
  var normalized = text.trim().toLowerCase();
22312
22457
  if (normalized === "/resume" || normalized === "resume") {
22313
22458
  showResumePicker();
@@ -22323,7 +22468,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22323
22468
  api("POST", isCmd ? "/api/command" : "/api/prompt", {
22324
22469
  text: text,
22325
22470
  refs: refs,
22326
- mentions: sessionMentions.map(function (item) { return { kind: "session", id: item.id }; })
22471
+ mentions: sessionMentions.map(function (item) { return { kind: "session", id: item.id }; }),
22472
+ images: payloadImages.map(function (item) { return { name: item.name, mimeType: item.mimeType, data: item.data }; })
22327
22473
  }).then(function (res) {
22328
22474
  if (!res.ok) {
22329
22475
  return readError(res, "Request failed").then(function (msg) { addBlock("alert", msg); });
@@ -22342,7 +22488,10 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22342
22488
  if (!steerNow && busy) return;
22343
22489
  var next = promptQueue.shift();
22344
22490
  renderQueue();
22345
- if (next) dispatchPrompt(next);
22491
+ if (next) {
22492
+ if (typeof next === "string") dispatchPrompt(next, []);
22493
+ else dispatchPrompt(next.text || "", next.images || []);
22494
+ }
22346
22495
  }
22347
22496
 
22348
22497
  function setAlways(on) {
@@ -22702,8 +22851,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22702
22851
  var vals = usageHistory.map(function (r) { return r.input + r.output + r.reasoning; });
22703
22852
  var max = Math.max.apply(null, vals.concat([1]));
22704
22853
  var w = 640, h = 150, gap = 2, bw = Math.max(2, (w - (vals.length - 1) * gap) / vals.length);
22705
- var rects = vals.map(function (v, i) { var bh = Math.max(2, v / max * 126); var x = i * (bw + gap); return "<rect x=\\"" + x.toFixed(1) + "\\" y=\\"" + (140 - bh).toFixed(1) + "\\" width=\\"" + bw.toFixed(1) + "\\" height=\\"" + bh.toFixed(1) + "\\" rx=\\"1.5\\" fill=\\"#827b72\\"><title>" + fmtNum(v) + " tokens</title></rect>"; }).join("");
22706
- chart.innerHTML = "<svg viewBox=\\"0 0 640 150\\" preserveAspectRatio=\\"none\\" aria-label=\\"Token usage by model call\\"><line x1=\\"0\\" y1=\\"140\\" x2=\\"640\\" y2=\\"140\\" stroke=\\"rgba(255,255,255,.08)\\"/>" + rects + "</svg>";
22854
+ var rects = vals.map(function (v, i) { var bh = Math.max(2, v / max * 126); var x = i * (bw + gap); return "<rect x=\\"" + x.toFixed(1) + "\\" y=\\"" + (140 - bh).toFixed(1) + "\\" width=\\"" + bw.toFixed(1) + "\\" height=\\"" + bh.toFixed(1) + "\\" rx=\\"1.5\\" fill=\\"currentColor\\"><title>" + fmtNum(v) + " tokens</title></rect>"; }).join("");
22855
+ chart.innerHTML = "<svg viewBox=\\"0 0 640 150\\" preserveAspectRatio=\\"none\\" aria-label=\\"Token usage by model call\\"><line x1=\\"0\\" y1=\\"140\\" x2=\\"640\\" y2=\\"140\\" stroke=\\"currentColor\\" opacity=\\".2\\"/>" + rects + "</svg>";
22707
22856
  }
22708
22857
  }
22709
22858
  var phaseRows = [];
@@ -23293,16 +23442,18 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23293
23442
 
23294
23443
  function submitText(raw, steerNow) {
23295
23444
  var text = (raw == null ? inputEl.value : raw).trim();
23296
- if (raw == null && text) { inputEl.value = ""; autosizeInput(); }
23297
- if (!text) {
23445
+ var images = raw == null ? pendingImages.slice() : [];
23446
+ if (raw == null && (text || images.length)) { inputEl.value = ""; autosizeInput(); }
23447
+ if (!text && !images.length) {
23298
23448
  if (steerNow) flushQueue(true);
23299
23449
  return;
23300
23450
  }
23451
+ if (raw == null) { pendingImages = []; renderChips(); }
23301
23452
  if (busy && !steerNow) {
23302
- enqueuePrompt(text);
23453
+ enqueuePrompt(text, images);
23303
23454
  return;
23304
23455
  }
23305
- dispatchPrompt(text);
23456
+ dispatchPrompt(text || "Look at the attached image.", images);
23306
23457
  }
23307
23458
 
23308
23459
  function decide(decision) {
@@ -23421,6 +23572,50 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23421
23572
  chip.addEventListener("click", function () { sessionMentions.splice(i, 1); renderChips(); });
23422
23573
  el.appendChild(chip);
23423
23574
  });
23575
+ pendingImages.forEach(function (item, i) {
23576
+ var chip = document.createElement("span");
23577
+ chip.className = "chip chip-img";
23578
+ var img = document.createElement("img");
23579
+ img.src = item.preview || ("data:" + item.mimeType + ";base64," + item.data);
23580
+ img.alt = item.name || "image";
23581
+ chip.appendChild(img);
23582
+ var label = document.createElement("span");
23583
+ label.textContent = (item.name || "image") + " \xD7";
23584
+ chip.appendChild(label);
23585
+ chip.addEventListener("click", function () { pendingImages.splice(i, 1); renderChips(); });
23586
+ el.appendChild(chip);
23587
+ });
23588
+ }
23589
+
23590
+ function isImageFile(file) {
23591
+ return file && /^image\\/(png|jpe?g|gif|webp)$/i.test(file.type || "");
23592
+ }
23593
+
23594
+ function addImageFile(file) {
23595
+ if (!isImageFile(file)) return false;
23596
+ if (pendingImages.length >= 8) {
23597
+ addBlock("alert", "You can attach up to 8 images.");
23598
+ return true;
23599
+ }
23600
+ if (file.size > 6 * 1024 * 1024) {
23601
+ addBlock("alert", "Image is too large (6MB max).");
23602
+ return true;
23603
+ }
23604
+ var reader = new FileReader();
23605
+ reader.onload = function () {
23606
+ var dataUrl = String(reader.result || "");
23607
+ var b64 = dataUrl.split(",")[1] || "";
23608
+ if (!b64) return;
23609
+ pendingImages.push({
23610
+ name: file.name || "paste.png",
23611
+ mimeType: file.type || "image/png",
23612
+ data: b64,
23613
+ preview: dataUrl
23614
+ });
23615
+ renderChips();
23616
+ };
23617
+ reader.readAsDataURL(file);
23618
+ return true;
23424
23619
  }
23425
23620
 
23426
23621
  function addRef(path) {
@@ -23750,6 +23945,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23750
23945
  var files = e.dataTransfer && e.dataTransfer.files;
23751
23946
  if (!files) return;
23752
23947
  Array.from(files).forEach(function (file) {
23948
+ if (addImageFile(file)) return;
23753
23949
  var reader = new FileReader();
23754
23950
  reader.onload = function () {
23755
23951
  pendingImport = { name: file.name, contents: String(reader.result || "") };
@@ -23761,6 +23957,26 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23761
23957
  reader.readAsText(file);
23762
23958
  });
23763
23959
  });
23960
+ inputEl.addEventListener("paste", function (e) {
23961
+ var clip = e.clipboardData;
23962
+ if (!clip) return;
23963
+ var attached = false;
23964
+ if (clip.items) {
23965
+ for (var i = 0; i < clip.items.length; i++) {
23966
+ var item = clip.items[i];
23967
+ if (item.kind === "file" && String(item.type || "").indexOf("image/") === 0) {
23968
+ var file = item.getAsFile();
23969
+ if (file && addImageFile(file)) attached = true;
23970
+ }
23971
+ }
23972
+ }
23973
+ if (!attached && clip.files) {
23974
+ Array.from(clip.files).forEach(function (file) {
23975
+ if (addImageFile(file)) attached = true;
23976
+ });
23977
+ }
23978
+ if (attached && !(clip.getData("text/plain") || "").trim()) e.preventDefault();
23979
+ });
23764
23980
  if (filesBadge) filesBadge.addEventListener("click", function () {
23765
23981
  layout.changes = true;
23766
23982
  applyLayout();
@@ -25077,14 +25293,14 @@ async function loadPiTui() {
25077
25293
  return await import(specifier);
25078
25294
  } catch (error) {
25079
25295
  const { existsSync: existsSync3 } = await import("node:fs");
25080
- const { dirname: dirname11, join: join22 } = await import("node:path");
25296
+ const { dirname: dirname11, join: join23 } = await import("node:path");
25081
25297
  const { fileURLToPath: fileURLToPath3, pathToFileURL: pathToFileURL3 } = await import("node:url");
25082
25298
  const roots = [
25083
- join22(dirname11(fileURLToPath3(import.meta.url)), "../../packages/critique-code/.vendor"),
25084
- join22(dirname11(fileURLToPath3(import.meta.url)), "../../packages/critique-code")
25299
+ join23(dirname11(fileURLToPath3(import.meta.url)), "../../packages/critique-code/.vendor"),
25300
+ join23(dirname11(fileURLToPath3(import.meta.url)), "../../packages/critique-code")
25085
25301
  ];
25086
25302
  for (const root of roots) {
25087
- const candidate = join22(root, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui/dist/index.js");
25303
+ const candidate = join23(root, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui/dist/index.js");
25088
25304
  if (!existsSync3(candidate)) continue;
25089
25305
  return await import(pathToFileURL3(candidate).href);
25090
25306
  }
@@ -25236,7 +25452,7 @@ async function createInteractiveSettingsUi(input) {
25236
25452
  init_zod();
25237
25453
  import { spawn as spawn3 } from "node:child_process";
25238
25454
  import { accessSync, constants } from "node:fs";
25239
- import { delimiter as delimiter2, join as join15 } from "node:path";
25455
+ import { delimiter as delimiter2, join as join16 } from "node:path";
25240
25456
  var CRITIQUE_CLI_MISSING_HINT = "Critique CLI is not on PATH. Install `@critiquedotsh/cli` (binary `critique`) or set CRITIQUE_CLI to that executable. `/critique` and `critique_cli` spawn the sidecar; they do not replace `/review`.";
25241
25457
  var TOOL_COMMANDS = [
25242
25458
  "review",
@@ -25500,7 +25716,7 @@ function resolveCritiqueCliBinary(env = process.env) {
25500
25716
  for (const directory of pathEnv.split(delimiter2)) {
25501
25717
  if (!directory) continue;
25502
25718
  for (const name of names) {
25503
- const candidate = join15(directory, name);
25719
+ const candidate = join16(directory, name);
25504
25720
  try {
25505
25721
  accessSync(candidate, mode);
25506
25722
  return candidate;
@@ -25736,7 +25952,7 @@ var AUTHOR_PALETTE_ITEMS = [
25736
25952
  { value: "/repeat", label: "Repeat", description: "Re-run the last command" },
25737
25953
  { value: "/theme", label: "Theme", description: "Color and compact layout for this TUI" },
25738
25954
  { value: "/alias", label: "Aliases", description: "Bind shortcuts to command chains" },
25739
- { value: "/remote", label: "Remote", description: "Pair a phone; this laptop stays the agent" },
25955
+ { value: "/remote", label: "Remote", description: "Attach critique.sh/code via Critique Remote" },
25740
25956
  { value: "/connect", label: "Connect website", description: "Attach critique.sh/code to this laptop" },
25741
25957
  { value: "/slack", label: "Slack", description: "Attach Slack Socket Mode to this laptop session" },
25742
25958
  { value: "/telegram", label: "Telegram", description: "Attach a Telegram bot to this laptop session" },
@@ -25787,7 +26003,7 @@ function formatAuthorHelp() {
25787
26003
  " /context tokens and loaded files",
25788
26004
  " /status /logs /cost /doctor",
25789
26005
  " /undo /repeat /theme /alias",
25790
- " /remote pair a phone to this laptop (LAN PIN, or --anywhere tunnel / Slack)",
26006
+ " /remote attach critique.sh/code (Critique Remote). /remote lan for Wi-Fi PIN",
25791
26007
  " /connect attach critique.sh/code to this laptop (paste the website code)",
25792
26008
  " /slack attach Slack Socket Mode; DMs run on this laptop",
25793
26009
  " /telegram attach a Telegram bot; chats run on this laptop",
@@ -25976,6 +26192,7 @@ function parseAuthorCommand(line, aliases = {}) {
25976
26192
  if (lower.startsWith("/alias ")) return parseAlias(expanded);
25977
26193
  if (lower === "/remote" || lower === "remote") return { kind: "remote" };
25978
26194
  if (lower === "/remote anywhere" || lower === "/remote --anywhere") return { kind: "remote", anywhere: true };
26195
+ if (lower === "/remote lan" || lower === "/remote --lan") return { kind: "remote", lan: true };
25979
26196
  if (lower === "/connect" || lower === "connect" || lower === "/site" || lower === "site") return { kind: "site" };
25980
26197
  if (lower.startsWith("/connect ")) return { kind: "site", offerCode: expanded.slice(9).trim() };
25981
26198
  if (lower === "/slack" || lower === "slack") return { kind: "slack" };
@@ -25992,7 +26209,7 @@ function parseAuthorCommand(line, aliases = {}) {
25992
26209
  // lib/finish/critique-code-web-changes.ts
25993
26210
  import { execFileSync } from "node:child_process";
25994
26211
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
25995
- import { readFile as readFile17 } from "node:fs/promises";
26212
+ import { readFile as readFile18 } from "node:fs/promises";
25996
26213
  import { dirname as dirname8, isAbsolute as isAbsolute16, relative as relative9, resolve as resolve11 } from "node:path";
25997
26214
  var SKIP_TREE_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", ".critique"]);
25998
26215
  function git3(root, args) {
@@ -26155,7 +26372,7 @@ async function workingTreeFilePatch(root, path2) {
26155
26372
  } catch {
26156
26373
  }
26157
26374
  try {
26158
- const contents = await readFile17(`${root}/${path2}`, "utf8");
26375
+ const contents = await readFile18(`${root}/${path2}`, "utf8");
26159
26376
  const lines = contents.split("\n");
26160
26377
  const body = lines.map((line) => `+${line}`).join("\n");
26161
26378
  return {
@@ -26377,9 +26594,9 @@ function commitMessageFromReview(input) {
26377
26594
 
26378
26595
  // lib/finish/critique-code-skills.ts
26379
26596
  init_critique_code_runtime_config();
26380
- import { cp, mkdir as mkdir12, readdir as readdir2, readFile as readFile18, writeFile as writeFile11 } from "node:fs/promises";
26597
+ import { cp, mkdir as mkdir13, readdir as readdir2, readFile as readFile19, writeFile as writeFile12 } from "node:fs/promises";
26381
26598
  import { homedir as homedir2 } from "node:os";
26382
- import { isAbsolute as isAbsolute17, join as join16, relative as relative10, resolve as resolve12, sep as sep10 } from "node:path";
26599
+ import { isAbsolute as isAbsolute17, join as join17, relative as relative10, resolve as resolve12, sep as sep10 } from "node:path";
26383
26600
  var CRITIQUE_CODE_SKILL_SCHEMA = "critique.code-skill.v1";
26384
26601
  var MAX_SKILL_BYTES = 64 * 1024;
26385
26602
  var MAX_IMPORTED_SKILLS = 80;
@@ -26601,16 +26818,16 @@ var PROJECT_SKILL_DIRS = [
26601
26818
  ];
26602
26819
  function userSkillDirs(home, userHome) {
26603
26820
  return [
26604
- { root: join16(home, "skills"), source: "imported" },
26605
- { root: join16(userHome, ".claude", "skills"), source: "claude-code" },
26606
- { root: join16(userHome, ".codex", "skills"), source: "codex" },
26607
- { root: join16(userHome, ".agents", "skills"), source: "agents" },
26608
- { root: join16(userHome, ".opencode", "skills"), source: "opencode" },
26609
- { root: join16(userHome, ".cursor", "skills"), source: "cursor" }
26821
+ { root: join17(home, "skills"), source: "imported" },
26822
+ { root: join17(userHome, ".claude", "skills"), source: "claude-code" },
26823
+ { root: join17(userHome, ".codex", "skills"), source: "codex" },
26824
+ { root: join17(userHome, ".agents", "skills"), source: "agents" },
26825
+ { root: join17(userHome, ".opencode", "skills"), source: "opencode" },
26826
+ { root: join17(userHome, ".cursor", "skills"), source: "cursor" }
26610
26827
  ];
26611
26828
  }
26612
26829
  function critiqueCodeImportedSkillsPath(home) {
26613
- return join16(home, "skills");
26830
+ return join17(home, "skills");
26614
26831
  }
26615
26832
  function skillId(value) {
26616
26833
  const id4 = value.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -26637,7 +26854,7 @@ function parseFrontmatter(markdown) {
26637
26854
  async function readSkillFile(file, source, origin, fallbackId) {
26638
26855
  let markdown;
26639
26856
  try {
26640
- markdown = await readFile18(file, "utf8");
26857
+ markdown = await readFile19(file, "utf8");
26641
26858
  } catch {
26642
26859
  return void 0;
26643
26860
  }
@@ -26665,7 +26882,7 @@ async function loadSkillsFromRoot(root, source) {
26665
26882
  }
26666
26883
  const skills = [];
26667
26884
  for (const entry of entries) {
26668
- const nested4 = join16(root, entry, "SKILL.md");
26885
+ const nested4 = join17(root, entry, "SKILL.md");
26669
26886
  const skill = await readSkillFile(nested4, source, nested4, entry);
26670
26887
  if (skill) skills.push(skill);
26671
26888
  }
@@ -26715,7 +26932,7 @@ async function discoverCritiqueCodeSkills(input) {
26715
26932
  }
26716
26933
  let imported = 0;
26717
26934
  for (const dir of PROJECT_SKILL_DIRS) {
26718
- const root = join16(cwd, dir.relative);
26935
+ const root = join17(cwd, dir.relative);
26719
26936
  if (!contained(cwd, root)) continue;
26720
26937
  for (const skill of await loadSkillsFromRoot(root, dir.source)) {
26721
26938
  if (skill.source !== "builtin" && skill.source !== "imported") {
@@ -26747,20 +26964,20 @@ function formatCritiqueCodeSkillsList(skills) {
26747
26964
  `;
26748
26965
  }
26749
26966
  async function stageCritiqueCodeSkills(input) {
26750
- const dest = join16(input.agent_root, "skills");
26751
- await mkdir12(dest, { recursive: true });
26967
+ const dest = join17(input.agent_root, "skills");
26968
+ await mkdir13(dest, { recursive: true });
26752
26969
  for (const skill of input.skills) {
26753
26970
  const id4 = skillId(skill.id);
26754
26971
  if (!id4) continue;
26755
- const folder = join16(dest, id4);
26756
- await mkdir12(folder, { recursive: true });
26972
+ const folder = join17(dest, id4);
26973
+ await mkdir13(folder, { recursive: true });
26757
26974
  const header = `---
26758
26975
  name: ${skill.id}
26759
26976
  description: ${skill.description.replace(/\n/g, " ").slice(0, 1024)}
26760
26977
  ---
26761
26978
 
26762
26979
  `;
26763
- await writeFile11(join16(folder, "SKILL.md"), `${header}${skill.body.trim()}
26980
+ await writeFile12(join17(folder, "SKILL.md"), `${header}${skill.body.trim()}
26764
26981
  `, "utf8");
26765
26982
  }
26766
26983
  return dest;
@@ -26769,7 +26986,7 @@ async function importCritiqueCodeSkills(input) {
26769
26986
  const env = input.env ?? process.env;
26770
26987
  const home = critiqueCodeHome(env);
26771
26988
  const destRoot = critiqueCodeImportedSkillsPath(home);
26772
- await mkdir12(destRoot, { recursive: true });
26989
+ await mkdir13(destRoot, { recursive: true });
26773
26990
  const discovered = await discoverCritiqueCodeSkills({
26774
26991
  cwd: input.cwd,
26775
26992
  env,
@@ -26782,18 +26999,18 @@ async function importCritiqueCodeSkills(input) {
26782
26999
  skipped.push(skill.id);
26783
27000
  continue;
26784
27001
  }
26785
- const folder = join16(destRoot, skill.id);
26786
- await mkdir12(folder, { recursive: true });
27002
+ const folder = join17(destRoot, skill.id);
27003
+ await mkdir13(folder, { recursive: true });
26787
27004
  if (skill.origin.endsWith(`${sep10}SKILL.md`)) {
26788
27005
  try {
26789
- await cp(skill.origin, join16(folder, "SKILL.md"));
26790
- imported.push({ ...skill, source: "imported", origin: join16(folder, "SKILL.md") });
27006
+ await cp(skill.origin, join17(folder, "SKILL.md"));
27007
+ imported.push({ ...skill, source: "imported", origin: join17(folder, "SKILL.md") });
26791
27008
  continue;
26792
27009
  } catch {
26793
27010
  }
26794
27011
  }
26795
- await writeFile11(
26796
- join16(folder, "SKILL.md"),
27012
+ await writeFile12(
27013
+ join17(folder, "SKILL.md"),
26797
27014
  `---
26798
27015
  name: ${skill.id}
26799
27016
  description: ${skill.description.replace(/\n/g, " ")}
@@ -26803,7 +27020,7 @@ ${skill.body.trim()}
26803
27020
  `,
26804
27021
  "utf8"
26805
27022
  );
26806
- imported.push({ ...skill, source: "imported", origin: join16(folder, "SKILL.md") });
27023
+ imported.push({ ...skill, source: "imported", origin: join17(folder, "SKILL.md") });
26807
27024
  }
26808
27025
  return { home, imported, skipped };
26809
27026
  }
@@ -26812,9 +27029,9 @@ ${skill.body.trim()}
26812
27029
  import { spawn as spawn4 } from "node:child_process";
26813
27030
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
26814
27031
  import { createRequire } from "node:module";
26815
- import { mkdtemp as mkdtemp3, readFile as readFile19, rm as rm3 } from "node:fs/promises";
27032
+ import { mkdtemp as mkdtemp3, readFile as readFile20, rm as rm3 } from "node:fs/promises";
26816
27033
  import { tmpdir as tmpdir5 } from "node:os";
26817
- import { dirname as dirname9, join as join17 } from "node:path";
27034
+ import { dirname as dirname9, join as join18 } from "node:path";
26818
27035
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
26819
27036
 
26820
27037
  // lib/inference/qwen3-asr.ts
@@ -26908,8 +27125,8 @@ function resolveBundledFfmpegPath(from = import.meta.url) {
26908
27125
  try {
26909
27126
  let dir = dirname9(fileURLToPath2(from));
26910
27127
  for (let i = 0; i < 10; i++) {
26911
- const harnessPkg = join17(dir, "packages/critique-code/package.json");
26912
- const pkg = join17(dir, "package.json");
27128
+ const harnessPkg = join18(dir, "packages/critique-code/package.json");
27129
+ const pkg = join18(dir, "package.json");
26913
27130
  if (existsSync2(harnessPkg)) bases.push(pathToFileURL2(harnessPkg).href);
26914
27131
  if (existsSync2(pkg)) {
26915
27132
  try {
@@ -26946,8 +27163,8 @@ function listVoiceRecorderCandidates(input) {
26946
27163
  }
26947
27164
  async function recordMicrophoneWav(input) {
26948
27165
  const run = input.spawn ?? spawn4;
26949
- const dir = await mkdtemp3(join17(tmpdir5(), "critique-code-voice-"));
26950
- const file = join17(dir, "clip.wav");
27166
+ const dir = await mkdtemp3(join18(tmpdir5(), "critique-code-voice-"));
27167
+ const file = join18(dir, "clip.wav");
26951
27168
  const stop = input.waitForStop ?? (input.stdin ? () => waitForEnter(input.stdin) : void 0);
26952
27169
  if (!stop) {
26953
27170
  await rm3(dir, { recursive: true, force: true });
@@ -26966,7 +27183,7 @@ async function recordMicrophoneWav(input) {
26966
27183
  settled = true;
26967
27184
  clearTimeout(timeout);
26968
27185
  try {
26969
- const wav = await readFile19(file);
27186
+ const wav = await readFile20(file);
26970
27187
  if (wav.length >= 44) {
26971
27188
  resolve14({ wav });
26972
27189
  return;
@@ -27283,7 +27500,7 @@ function createPiAuthorTaskProjectedTool(projection) {
27283
27500
  init_pi_source_tool();
27284
27501
 
27285
27502
  // lib/finish/critique-code-workspace-memory.ts
27286
- import { chmod as chmod3, mkdir as mkdir13, readFile as readFile20, writeFile as writeFile12 } from "node:fs/promises";
27503
+ import { chmod as chmod3, mkdir as mkdir14, readFile as readFile21, writeFile as writeFile13 } from "node:fs/promises";
27287
27504
  import { dirname as dirname10 } from "node:path";
27288
27505
  var MAX_MEMORY_RECORDS = 200;
27289
27506
  var MAX_MEMORY_VALUE_CHARS = 8192;
@@ -27347,7 +27564,7 @@ var CritiqueCodeWorkspaceMemory = class {
27347
27564
  }
27348
27565
  async load() {
27349
27566
  try {
27350
- const raw = await readFile20(this.#filePath, "utf8");
27567
+ const raw = await readFile21(this.#filePath, "utf8");
27351
27568
  const parsed = JSON.parse(raw);
27352
27569
  this.#records = Array.isArray(parsed.records) ? parsed.records.filter(isMemoryRecord).slice(-MAX_MEMORY_RECORDS) : [];
27353
27570
  } catch (error) {
@@ -27375,8 +27592,8 @@ var CritiqueCodeWorkspaceMemory = class {
27375
27592
  if (this.#records.length > MAX_MEMORY_RECORDS) {
27376
27593
  this.#records = this.#records.slice(-MAX_MEMORY_RECORDS);
27377
27594
  }
27378
- await mkdir13(dirname10(this.#filePath), { recursive: true });
27379
- await writeFile12(this.#filePath, `${JSON.stringify({ records: this.#records })}
27595
+ await mkdir14(dirname10(this.#filePath), { recursive: true });
27596
+ await writeFile13(this.#filePath, `${JSON.stringify({ records: this.#records })}
27380
27597
  `, { mode: 384 });
27381
27598
  await chmod3(this.#filePath, 384);
27382
27599
  }
@@ -27398,7 +27615,7 @@ var CritiqueCodeWorkspaceMemory = class {
27398
27615
 
27399
27616
  // lib/finish/critique-code-workspace-tool.ts
27400
27617
  init_zod();
27401
- import { readFile as readFile21 } from "node:fs/promises";
27618
+ import { readFile as readFile22 } from "node:fs/promises";
27402
27619
 
27403
27620
  // lib/finish/critique-code-ts-edit.ts
27404
27621
  import ts2 from "typescript";
@@ -27442,7 +27659,7 @@ function renameTsIdentifier(input) {
27442
27659
  import { execFileSync as execFileSync2 } from "node:child_process";
27443
27660
  import { readFileSync as readFileSync3, readdirSync } from "node:fs";
27444
27661
  import { createServer } from "node:net";
27445
- import { join as join18 } from "node:path";
27662
+ import { join as join19 } from "node:path";
27446
27663
  var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".critique", "dist"]);
27447
27664
  var SUBPROCESS_TIMEOUT_MS = 4e3;
27448
27665
  var FETCH_TIMEOUT_MS = 8e3;
@@ -27531,7 +27748,7 @@ function walkWorkspaceFiles(root, pattern, maxHits) {
27531
27748
  if (hits.length >= maxHits) return;
27532
27749
  if (SKIP_DIR_NAMES.has(entry.name)) continue;
27533
27750
  const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
27534
- const abs = join18(absDir, entry.name);
27751
+ const abs = join19(absDir, entry.name);
27535
27752
  if (entry.isDirectory()) {
27536
27753
  visit(abs, rel);
27537
27754
  continue;
@@ -27582,7 +27799,7 @@ function grepWorkspaceJs(root, query, maxHits) {
27582
27799
  if (hits.length >= maxHits) break;
27583
27800
  let bytes;
27584
27801
  try {
27585
- bytes = readFileSync3(join18(root, rel));
27802
+ bytes = readFileSync3(join19(root, rel));
27586
27803
  } catch {
27587
27804
  continue;
27588
27805
  }
@@ -27894,19 +28111,19 @@ var PiWorkspaceToolProjection = class {
27894
28111
  return { hits: grepWorkspaceIntel(this.#root, input.query) };
27895
28112
  case "diff": {
27896
28113
  if (!input.path) throw new Error("diff requires path.");
27897
- const current = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28114
+ const current = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27898
28115
  const previous = input.before ?? "";
27899
28116
  return { path: input.path, patch: unifiedDiff(previous, input.after ?? current, input.path) };
27900
28117
  }
27901
28118
  case "parse_config": {
27902
28119
  if (!input.path) throw new Error("parse_config requires path.");
27903
- const text = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28120
+ const text = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27904
28121
  return parseStructuredConfig(text, input.kind ?? "auto");
27905
28122
  }
27906
28123
  case "env_keys": {
27907
28124
  const rel = input.path ?? ".env";
27908
28125
  try {
27909
- const text = await readFile21(safeRepoPath(this.#root, rel), "utf8");
28126
+ const text = await readFile22(safeRepoPath(this.#root, rel), "utf8");
27910
28127
  return { path: rel, keys: parseDotenvKeys(text), limitation: "Values are never returned." };
27911
28128
  } catch {
27912
28129
  return { path: rel, keys: [], limitation: "File missing. Values are never returned." };
@@ -27925,7 +28142,7 @@ var PiWorkspaceToolProjection = class {
27925
28142
  return { records: this.#memory.recall(input.query ?? input.key ?? "") };
27926
28143
  case "rename_ts": {
27927
28144
  if (!input.path || !input.from || !input.to) throw new Error("rename_ts requires path, from, and to.");
27928
- const source = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28145
+ const source = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27929
28146
  const renamed = renameTsIdentifier({ source, fileName: input.path, from: input.from, to: input.to });
27930
28147
  return {
27931
28148
  ...renamed,
@@ -28140,7 +28357,7 @@ function wrapAuthorWrite(inner, onWrite, workspaceRoot) {
28140
28357
  let previous = null;
28141
28358
  if (path2) {
28142
28359
  try {
28143
- previous = await readFile22(join19(workspaceRoot, path2), "utf8");
28360
+ previous = await readFile23(join20(workspaceRoot, path2), "utf8");
28144
28361
  } catch {
28145
28362
  previous = null;
28146
28363
  }
@@ -28159,7 +28376,7 @@ function liveSourceState(state) {
28159
28376
  }
28160
28377
  async function loadReleasedFile(repositoryRoot, name, maxChars) {
28161
28378
  try {
28162
- const text = (await readFile22(join19(repositoryRoot, name), "utf8")).trim();
28379
+ const text = (await readFile23(join20(repositoryRoot, name), "utf8")).trim();
28163
28380
  if (!text) return void 0;
28164
28381
  const release = await patternSecretEgress().release({ kind: "model_prompt", content: text.slice(0, maxChars) });
28165
28382
  return release.content;
@@ -28241,14 +28458,14 @@ async function runLocalCritiqueCodeAuthor(input) {
28241
28458
  const storeRoot = input.store_root ?? defaultCritiqueCodeStoreRoot(repositoryRoot);
28242
28459
  const sessionOps = new FileSessionOpsStore(storeRoot);
28243
28460
  await sessionOps.load();
28244
- const workspaceMemory = new CritiqueCodeWorkspaceMemory(join19(storeRoot, "workspace-memory.json"));
28461
+ const workspaceMemory = new CritiqueCodeWorkspaceMemory(join20(storeRoot, "workspace-memory.json"));
28245
28462
  await workspaceMemory.load();
28246
- const agentRoot = input.agent_root ?? join19(tmpdir6(), "critique-code-pi-agent");
28463
+ const agentRoot = input.agent_root ?? join20(tmpdir6(), "critique-code-pi-agent");
28247
28464
  if (!isAbsolute19(agentRoot)) {
28248
28465
  reader.close();
28249
28466
  return { kind: "kernel_required", limitation: "Pi agent root must be an absolute controller-owned directory." };
28250
28467
  }
28251
- if (!input.agent_root) await mkdir14(agentRoot, { recursive: true });
28468
+ if (!input.agent_root) await mkdir15(agentRoot, { recursive: true });
28252
28469
  const conversationIdSeed = `pi_${createHash38("sha256").update(repositoryRoot).digest("hex").slice(0, 24)}`;
28253
28470
  const review = input.review ?? runLocalCritiqueCodeReview;
28254
28471
  const checkpoints = new FileReviewCheckpointStore(storeRoot);
@@ -28690,7 +28907,7 @@ ${prior}`.trim();
28690
28907
  emitReviewController({ reviewing: false });
28691
28908
  }
28692
28909
  };
28693
- const promptAuthor = async (prompt) => {
28910
+ const promptAuthor = async (prompt, images) => {
28694
28911
  await ensureSession();
28695
28912
  noteWithheld(prompt);
28696
28913
  reader.pause();
@@ -28708,7 +28925,8 @@ ${prior}`.trim();
28708
28925
  const turn = await authors.prompt({
28709
28926
  conversation_id: conversationId2,
28710
28927
  prompt: outbound,
28711
- timeout_ms: timeoutMs
28928
+ timeout_ms: timeoutMs,
28929
+ ...images && images.length > 0 ? { images } : {}
28712
28930
  });
28713
28931
  await sessionOps.recordCost({
28714
28932
  at: now().toISOString(),
@@ -28761,10 +28979,17 @@ ${prior}`.trim();
28761
28979
  ensureStarted(input.intent.trim());
28762
28980
  }
28763
28981
  for (; ; ) {
28764
- const line = await takeLine();
28765
- if (line === void 0) break;
28982
+ const lineRaw = await takeLine();
28983
+ if (lineRaw === void 0) break;
28984
+ const unpacked = unwrapPromptImagePaths(lineRaw);
28985
+ const line = unpacked.text;
28986
+ const packedImages = unpacked.paths.length > 0 ? (await loadPromptImagesFromPaths(unpacked.paths)).map((image) => ({
28987
+ type: "image",
28988
+ data: image.data,
28989
+ mimeType: image.mimeType
28990
+ })) : [];
28766
28991
  let command = authorCommand(line, sessionOps.state().aliases, skills);
28767
- if (!command && !line.trim()) continue;
28992
+ if (!command && !line.trim() && packedImages.length === 0) continue;
28768
28993
  if (command && command.kind !== "repeat" && command.kind !== "palette" && command.kind !== "help") {
28769
28994
  await sessionOps.setLastCommand(line.trim());
28770
28995
  }
@@ -28813,14 +29038,14 @@ ${prior}`.trim();
28813
29038
  }
28814
29039
  if (command?.kind === "remote" || command?.kind === "slack" || command?.kind === "telegram" || command?.kind === "site") {
28815
29040
  if (!input.on_surface) {
28816
- say(command.kind === "remote" ? "Phone pairing is `critique-code remote` in this repo. Leave that process running on this laptop.\n" : command.kind === "site" ? "Website connect is `critique-code connect [CODE]` after critique-code login. Kernel stays on this laptop.\n" : `Connect ${command.kind} with \`critique-code ${command.kind}\` in this repo. The kernel stays on this laptop.
29041
+ say(command.kind === "remote" ? "Critique Remote is `critique-code remote` after `critique-code login`. Leave that process running. Use `--lan` for same-Wi-Fi PIN pairing.\n" : command.kind === "site" ? "Website connect is `critique-code connect [CODE]` after critique-code login. Kernel stays on this laptop.\n" : `Connect ${command.kind} with \`critique-code ${command.kind}\` in this repo. The kernel stays on this laptop.
28817
29042
  `);
28818
29043
  continue;
28819
29044
  }
28820
29045
  try {
28821
29046
  await input.on_surface(
28822
29047
  command.kind,
28823
- command.kind === "remote" ? { anywhere: command.anywhere } : command.kind === "site" ? { offerCode: command.offerCode } : {}
29048
+ command.kind === "remote" ? { anywhere: command.anywhere, lan: command.lan } : command.kind === "site" ? { offerCode: command.offerCode } : {}
28824
29049
  );
28825
29050
  } catch (error) {
28826
29051
  say(`${error instanceof Error ? error.message : String(error)}
@@ -28871,7 +29096,7 @@ ${prior}`.trim();
28871
29096
  }
28872
29097
  }
28873
29098
  if (thread.action === "fork") {
28874
- const id5 = randomBytes(6).toString("hex");
29099
+ const id5 = randomBytes2(6).toString("hex");
28875
29100
  const briefing = withheld.trim();
28876
29101
  const from = activeThread;
28877
29102
  switchThread(id5, true);
@@ -28891,7 +29116,7 @@ ${briefing}
28891
29116
  `);
28892
29117
  continue;
28893
29118
  }
28894
- const id4 = thread.id?.trim() || randomBytes(6).toString("hex");
29119
+ const id4 = thread.id?.trim() || randomBytes2(6).toString("hex");
28895
29120
  switchThread(id4, thread.action === "new");
28896
29121
  await sessionOps.touchSession({
28897
29122
  id: id4,
@@ -28985,7 +29210,7 @@ ${briefing}
28985
29210
  `);
28986
29211
  continue;
28987
29212
  }
28988
- const id4 = randomBytes(4).toString("hex");
29213
+ const id4 = randomBytes2(4).toString("hex");
28989
29214
  await sessionOps.addSnapshot({
28990
29215
  id: id4,
28991
29216
  label: command.label?.trim() || `checkpoint ${id4}`,
@@ -29051,10 +29276,10 @@ ${briefing}
29051
29276
  const html = command.format === "html" ? `<pre>${markdown.replaceAll("&", "&amp;").replaceAll("<", "&lt;")}</pre>
29052
29277
  ` : markdown;
29053
29278
  const name = `session-${activeThread}.${command.format === "html" ? "html" : "md"}`;
29054
- const directory = join19(storeRoot, "exports");
29055
- await mkdir14(directory, { recursive: true, mode: 448 });
29056
- const target = join19(directory, name);
29057
- await writeFile13(target, html, { mode: 384 });
29279
+ const directory = join20(storeRoot, "exports");
29280
+ await mkdir15(directory, { recursive: true, mode: 448 });
29281
+ const target = join20(directory, name);
29282
+ await writeFile14(target, html, { mode: 384 });
29058
29283
  say(`Exported ${target}
29059
29284
  `);
29060
29285
  continue;
@@ -29406,9 +29631,9 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29406
29631
  say("Nothing to undo. /rollback restores a full checkpoint.\n");
29407
29632
  continue;
29408
29633
  }
29409
- const target = join19(repositoryRoot, entry.path);
29634
+ const target = join20(repositoryRoot, entry.path);
29410
29635
  if (entry.previous === null) await unlink4(target).catch(() => void 0);
29411
- else await writeFile13(target, entry.previous);
29636
+ else await writeFile14(target, entry.previous);
29412
29637
  say(`Undid ${entry.path}.
29413
29638
  `);
29414
29639
  continue;
@@ -29544,8 +29769,8 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29544
29769
  if (voiceMode) listenNow = true;
29545
29770
  continue;
29546
29771
  }
29547
- ensureStarted(line.trim());
29548
- await promptAuthor(line);
29772
+ ensureStarted(line.trim() || "Look at the attached image.");
29773
+ await promptAuthor(line.trim() || "Look at the attached image.", packedImages.length > 0 ? packedImages : void 0);
29549
29774
  if (voiceMode) listenNow = true;
29550
29775
  }
29551
29776
  } finally {
@@ -29571,10 +29796,10 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29571
29796
 
29572
29797
  // lib/finish/critique-code-web-server.ts
29573
29798
  init_change_capsule();
29574
- import { randomBytes as randomBytes2 } from "node:crypto";
29799
+ import { randomBytes as randomBytes3 } from "node:crypto";
29575
29800
  import { createServer as createServer2 } from "node:http";
29576
- import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile15 } from "node:fs/promises";
29577
- import { basename as basename4, join as join21 } from "node:path";
29801
+ import { mkdir as mkdir17, readFile as readFile25, writeFile as writeFile16 } from "node:fs/promises";
29802
+ import { basename as basename4, join as join22 } from "node:path";
29578
29803
  import { execFileSync as execFileSync3, spawn as spawn7 } from "node:child_process";
29579
29804
 
29580
29805
  // lib/finish/critique-code-line-hub.ts
@@ -29788,23 +30013,39 @@ function originAllowed(input) {
29788
30013
  return parsed.host.toLowerCase() === host;
29789
30014
  }
29790
30015
  function formatRemoteBanner(input) {
29791
- const lan = input.urls.lan.length > 0 ? input.urls.lan.join("\n ") : "no LAN IPv4 yet \u2014 stay on loopback or use a portal";
29792
- const portals = input.portals && input.portals.length > 0 ? `Portals: ${input.portals.join(", ")}. DM the bot from your phone. The kernel stays on this laptop.` : "Add --slack or --telegram to control this same session from those apps.";
29793
- const tunnel = input.urls.tunnel ? `Anywhere URL:
29794
- ${input.urls.tunnel}` : "Off-Wi-Fi without Slack/Telegram: pass --anywhere (uses your cloudflared or ngrok). Not Critique Cloud.";
29795
- return [
29796
- `CritiqueCode remote \xB7 ${input.repository_name}`,
29797
- "This laptop is the agent. Phone and portals are remotes. Nothing executes in Critique Cloud.",
29798
- `Pairing PIN ${input.pin_display}`,
29799
- `Loopback
29800
- ${input.urls.loopback}`,
29801
- `On this Wi-Fi
29802
- ${lan}`,
29803
- tunnel,
29804
- portals,
29805
- "Leave this process running. Sleep or closing the terminal drops the phone.",
29806
- ""
29807
- ].join("\n");
30016
+ const lines = [`CritiqueCode Remote \xB7 ${input.repository_name}`];
30017
+ if (input.cloud_url) {
30018
+ lines.push(
30019
+ "\u25CF Connected to Critique Remote",
30020
+ "Open from any device:",
30021
+ ` ${input.cloud_url}`,
30022
+ "Repository and commands remain on this laptop.",
30023
+ "Closing CritiqueCode disconnects remote access."
30024
+ );
30025
+ } else {
30026
+ lines.push("This laptop is the agent. Phone and portals are remotes.");
30027
+ }
30028
+ lines.push(`Local:
30029
+ ${input.urls.loopback}`);
30030
+ if (input.pin_display) {
30031
+ lines.push(`Pairing PIN ${input.pin_display}`);
30032
+ if (input.urls.lan.length > 0) {
30033
+ lines.push(`On this Wi-Fi
30034
+ ${input.urls.lan.join("\n ")}`);
30035
+ }
30036
+ }
30037
+ if (input.urls.tunnel) {
30038
+ lines.push(`Tunnel URL:
30039
+ ${input.urls.tunnel}`);
30040
+ }
30041
+ if (input.portals && input.portals.length > 0) {
30042
+ lines.push(`Portals: ${input.portals.join(", ")}. DM the bot from your phone.`);
30043
+ }
30044
+ if (!input.cloud_url && !input.urls.tunnel) {
30045
+ lines.push("Off-Wi-Fi: run `critique-code remote` after `critique-code login`, or pass --anywhere.");
30046
+ }
30047
+ lines.push("Leave this process running. Sleep or closing the terminal drops remotes.", "");
30048
+ return lines.join("\n");
29808
30049
  }
29809
30050
  function withToken(url, token) {
29810
30051
  const parsed = new URL(url);
@@ -29886,8 +30127,8 @@ function spawnTunnel(command, args) {
29886
30127
 
29887
30128
  // lib/finish/critique-code-portals.ts
29888
30129
  init_critique_code_runtime_config();
29889
- import { mkdir as mkdir15, readFile as readFile23, writeFile as writeFile14 } from "node:fs/promises";
29890
- import { join as join20 } from "node:path";
30130
+ import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile15 } from "node:fs/promises";
30131
+ import { join as join21 } from "node:path";
29891
30132
  var CRITIQUE_CODE_PORTALS_SCHEMA = "critique.code-portals.v1";
29892
30133
  function isRecord2(value) {
29893
30134
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -29952,7 +30193,7 @@ async function loadCritiqueCodePortals(env = process.env) {
29952
30193
  const fromEnv = portalsFromEnv(env);
29953
30194
  const home = critiqueCodeHome(env);
29954
30195
  try {
29955
- const raw = JSON.parse(await readFile23(join20(home, "portals.json"), "utf8"));
30196
+ const raw = JSON.parse(await readFile24(join21(home, "portals.json"), "utf8"));
29956
30197
  const stored = parseStoredPortals(raw);
29957
30198
  return {
29958
30199
  schema_version: CRITIQUE_CODE_PORTALS_SCHEMA,
@@ -30289,27 +30530,31 @@ async function startCritiqueCodeSiteRelay(input) {
30289
30530
  repository: input.repository?.trim() || basename3(input.cwd),
30290
30531
  cwd: input.cwd
30291
30532
  };
30292
- const claimed = input.offerCode ? await postJson(fetchImpl, `${origin}/api/new/v1/code/claim`, headers, {
30293
- code: input.offerCode,
30294
- ...machine
30295
- }) : await postJson(fetchImpl, `${origin}/api/new/v1/code/presence`, headers, machine);
30533
+ const claimed = await postJson(fetchImpl, `${origin}/api/new/v1/code/relay/sessions`, headers, {
30534
+ ...machine,
30535
+ ...input.offerCode ? { code: input.offerCode } : {}
30536
+ });
30296
30537
  if (!claimed.ok) {
30297
30538
  return { limitation: claimed.error || "critique.sh did not accept this PC." };
30298
30539
  }
30299
- const agentId = String(claimed.body.agentId ?? "");
30540
+ const agentId = String(claimed.body.id ?? claimed.body.agentId ?? "");
30300
30541
  const agentToken = String(claimed.body.agentToken ?? "");
30542
+ const browserUrl = String(claimed.body.browser_url ?? `${origin}/code`);
30301
30543
  if (!agentId || !agentToken) {
30302
30544
  return { limitation: "critique.sh did not return a machine token." };
30303
30545
  }
30304
- input.note?.(`critique.sh/code sees ${machine.hostname} \xB7 ${machine.repository}. Kernel stays on this laptop.`);
30546
+ input.note?.(`Connected to Critique Remote. Open ${browserUrl} \xB7 ${machine.hostname} \xB7 ${machine.repository}. Kernel stays on this laptop.`);
30305
30547
  const abort = new AbortController();
30548
+ const baseInterval = input.intervalMs ?? 800;
30549
+ const maxInterval = input.maxIntervalMs ?? 3e4;
30306
30550
  const loop = (async () => {
30307
30551
  const sleep = input.sleep ?? defaultSleep2;
30552
+ let delay = baseInterval;
30308
30553
  while (!abort.signal.aborted) {
30309
30554
  const events = input.takeEvents().map((event) => ({ ...event }));
30310
30555
  const synced = await postJson(
30311
30556
  fetchImpl,
30312
- `${origin}/api/new/v1/code/sync`,
30557
+ `${origin}/api/new/v1/code/relay/agent/${agentId}`,
30313
30558
  {
30314
30559
  authorization: `Bearer ${agentToken}`,
30315
30560
  "content-type": "application/json"
@@ -30320,17 +30565,21 @@ async function startCritiqueCodeSiteRelay(input) {
30320
30565
  }
30321
30566
  );
30322
30567
  if (synced.ok && Array.isArray(synced.body.commands)) {
30568
+ delay = baseInterval;
30323
30569
  for (const command of synced.body.commands) {
30324
30570
  applySiteCommand(input.host, command);
30325
30571
  }
30572
+ } else {
30573
+ delay = Math.min(delay * 2, maxInterval);
30326
30574
  }
30327
- await sleep(input.intervalMs ?? 800);
30575
+ await sleep(delay);
30328
30576
  }
30329
30577
  })();
30330
30578
  void loop.catch(() => void 0);
30331
30579
  return {
30332
30580
  agentId,
30333
30581
  origin,
30582
+ url: browserUrl,
30334
30583
  async close() {
30335
30584
  abort.abort();
30336
30585
  }
@@ -30772,7 +31021,7 @@ async function startCritiqueCodeWebServer(input) {
30772
31021
  return { kind: "kernel_required", limitation: error instanceof Error ? error.message : String(error) };
30773
31022
  }
30774
31023
  const repositoryRoot = await findRepositoryRoot(input.cwd);
30775
- const token = randomBytes2(18).toString("base64url");
31024
+ const token = randomBytes3(18).toString("base64url");
30776
31025
  const hub = new CritiqueCodeLineHub();
30777
31026
  const pushLine = (line) => input.attach?.push(line) ?? hub.push(line);
30778
31027
  const clients = /* @__PURE__ */ new Set();
@@ -30801,16 +31050,16 @@ async function startCritiqueCodeWebServer(input) {
30801
31050
  let reviewModels = [...initialReviewModels];
30802
31051
  let pendingExec;
30803
31052
  const storeRoot = input.store_root ?? defaultCritiqueCodeStoreRoot(repositoryRoot);
30804
- const chatsPath = join21(storeRoot, "web-chats.json");
31053
+ const chatsPath = join22(storeRoot, "web-chats.json");
30805
31054
  let chats = [{
30806
- id: randomBytes2(6).toString("hex"),
31055
+ id: randomBytes3(6).toString("hex"),
30807
31056
  title: "New session",
30808
31057
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
30809
31058
  events: []
30810
31059
  }];
30811
31060
  let activeId = chats[0].id;
30812
31061
  try {
30813
- const raw = JSON.parse(await readFile24(chatsPath, "utf8"));
31062
+ const raw = JSON.parse(await readFile25(chatsPath, "utf8"));
30814
31063
  if (Array.isArray(raw.chats) && raw.chats.length > 0) {
30815
31064
  chats = raw.chats.map((chat) => ({
30816
31065
  id: String(chat.id),
@@ -30825,7 +31074,7 @@ async function startCritiqueCodeWebServer(input) {
30825
31074
  const sessionSummaries = () => chats.map((chat) => ({ id: chat.id, title: chat.title, created_at: chat.created_at }));
30826
31075
  const activeChat = () => chats.find((chat) => chat.id === activeId) ?? chats[0];
30827
31076
  const persistChats = () => {
30828
- void mkdir16(storeRoot, { recursive: true, mode: 448 }).then(() => writeFile15(
31077
+ void mkdir17(storeRoot, { recursive: true, mode: 448 }).then(() => writeFile16(
30829
31078
  chatsPath,
30830
31079
  `${JSON.stringify({ active_id: activeId, chats }, null, 2)}
30831
31080
  `,
@@ -31013,7 +31262,7 @@ data: ${JSON.stringify(stamped)}
31013
31262
  return "approve";
31014
31263
  }
31015
31264
  return new Promise((resolve14) => {
31016
- const id4 = randomBytes2(9).toString("hex");
31265
+ const id4 = randomBytes3(9).toString("hex");
31017
31266
  const pending = { id: id4, purpose: request.purpose, preview: request.preview, resolve: resolve14 };
31018
31267
  execs.set(id4, pending);
31019
31268
  pendingExec = { id: id4, purpose: request.purpose, preview: request.preview };
@@ -31261,7 +31510,7 @@ exit ${code2 ?? "killed"}
31261
31510
  }
31262
31511
  if (req.method === "POST" && path2 === "/api/sessions") {
31263
31512
  const chat = {
31264
- id: randomBytes2(6).toString("hex"),
31513
+ id: randomBytes3(6).toString("hex"),
31265
31514
  title: "New session",
31266
31515
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
31267
31516
  events: []
@@ -31277,7 +31526,7 @@ exit ${code2 ?? "killed"}
31277
31526
  if (req.method === "POST" && path2 === "/api/sessions/fork") {
31278
31527
  const source = activeChat();
31279
31528
  const chat = {
31280
- id: randomBytes2(6).toString("hex"),
31529
+ id: randomBytes3(6).toString("hex"),
31281
31530
  title: `${source.title === "New session" ? "Fork" : source.title} \xB7 fork`,
31282
31531
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
31283
31532
  events: source.events.map((event) => ({ ...event }))
@@ -31432,7 +31681,8 @@ exit ${code2 ?? "killed"}
31432
31681
  }
31433
31682
  if (req.method === "POST" && (path2 === "/api/prompt" || path2 === "/api/command")) {
31434
31683
  const body = await readJson(req);
31435
- const text = String(body.text ?? body.prompt ?? "").trim();
31684
+ const images = parseWebPromptImages(body.images);
31685
+ const text = String(body.text ?? body.prompt ?? "").trim() || (images.length > 0 ? "Look at the attached image." : "");
31436
31686
  if (!text) {
31437
31687
  send(res, 400, { error: "text required" });
31438
31688
  return;
@@ -31486,7 +31736,12 @@ ${extras.join("\n\n")}` : text);
31486
31736
  nameActiveChat(text);
31487
31737
  emitGate();
31488
31738
  }
31489
- const queued = pushLine(outbound);
31739
+ const queued = pushLine(
31740
+ images.length > 0 && !text.startsWith("/") ? wrapPromptWithImagePaths(
31741
+ outbound,
31742
+ await writeWebPromptImages({ home: critiqueCodeHome(env), images })
31743
+ ) : outbound
31744
+ );
31490
31745
  send(res, queued ? 202 : 409, { ok: queued });
31491
31746
  return;
31492
31747
  }
@@ -31857,15 +32112,16 @@ ${extras.join("\n\n")}` : text);
31857
32112
  else siteRelay = startedSite;
31858
32113
  }
31859
32114
  }
31860
- if (access3 === "pair") {
32115
+ if (access3 === "pair" || siteRelay) {
31861
32116
  input.stderr.write(formatRemoteBanner({
31862
32117
  repository_name: basename4(repositoryRoot),
31863
- pin_display: pairing?.pin_display ?? "",
32118
+ ...pairing ? { pin_display: pairing.pin_display } : {},
31864
32119
  urls: {
31865
32120
  loopback: urls.loopback,
31866
32121
  lan: urls.lan,
31867
32122
  ...urls.tunnel ? { tunnel: urls.tunnel } : {}
31868
32123
  },
32124
+ ...siteRelay ? { cloud_url: siteRelay.url } : {},
31869
32125
  portals: connectedPortals
31870
32126
  }));
31871
32127
  } else {
@@ -32010,7 +32266,7 @@ Loopback only. The author session stays in this process.
32010
32266
  ...urls.lan.length > 0 ? { lan_urls: urls.lan } : {},
32011
32267
  ...urls.tunnel ? { tunnel_url: urls.tunnel } : {},
32012
32268
  ...connectedPortals.length > 0 ? { portals: connectedPortals } : {},
32013
- ...siteRelay ? { site: { agentId: siteRelay.agentId, origin: siteRelay.origin } } : {},
32269
+ ...siteRelay ? { site: { agentId: siteRelay.agentId, origin: siteRelay.origin, url: siteRelay.url } } : {},
32014
32270
  async close() {
32015
32271
  hub.close();
32016
32272
  for (const adapter of portalAdapters) await adapter.stop().catch(() => void 0);
@@ -32051,7 +32307,7 @@ Usage:
32051
32307
  pnpm --filter @critiquedotsh/harness start
32052
32308
  critique-code | critique-code chat [--intent <text>] [--models provider/model,...] [--cwd <dir>] [--store <dir>] [--voice] [--json]
32053
32309
  critique-code web [--port <n>] [--cwd <dir>] [--store <dir>] [--models ...]
32054
- critique-code remote [--anywhere] [--site] [--slack] [--telegram] [--port <n>] [--cwd <dir>] [--store <dir>] [--models ...]
32310
+ critique-code remote [--lan] [--anywhere] [--slack] [--telegram] [--port <n>] [--cwd <dir>] [--store <dir>] [--models ...]
32055
32311
  critique-code connect [CODE] [--port <n>] [--cwd <dir>]
32056
32312
  critique-code slack [--port <n>] [--cwd <dir>]
32057
32313
  critique-code telegram [--port <n>] [--cwd <dir>]
@@ -32079,7 +32335,7 @@ Interactive author commands:
32079
32335
  /watch on Review on file save
32080
32336
  /repair Verified repair of promoted findings (not auto-applied chatter)
32081
32337
  /ship Review, then end if the controller allows
32082
- /remote Pair a phone; this laptop stays the agent (LAN PIN)
32338
+ /remote Attach critique.sh/code to this laptop (Critique Remote)
32083
32339
  /connect [CODE] Attach critique.sh/code to this laptop
32084
32340
  /slack Attach Slack Socket Mode to this session
32085
32341
  /telegram Attach a Telegram bot to this session
@@ -32093,7 +32349,7 @@ Interactive author commands:
32093
32349
 
32094
32350
  Interactive TTY author sessions keep stdout quiet; pass --json for the session envelope.
32095
32351
  \`critique-code web\` serves a loopback browser UI on 127.0.0.1. The author kernel still runs in this process.
32096
- \`critique-code remote\` pairs a phone to that same kernel on this laptop (LAN PIN, optional --anywhere tunnel). Not Critique Cloud.
32352
+ \`critique-code remote\` opens Critique Remote: this laptop stays the agent, critique.sh/code is the remote. Needs \`critique-code login\`. Use --lan for same-Wi-Fi PIN pairing, --anywhere for your cloudflared/ngrok.
32097
32353
  \`critique-code connect [CODE]\` registers this PC on critique.sh/code. The website is a remote; the kernel stays here.
32098
32354
  \`critique-code slack\` / \`critique-code telegram\` open outbound bots so you can DM the agent from those apps. The kernel stays here.
32099
32355
  Other commands print JSON on stdout. Live chrome goes to stderr.
@@ -32177,6 +32433,8 @@ function parseInvocation(argv) {
32177
32433
  repair: { type: "string" },
32178
32434
  port: { type: "string" },
32179
32435
  anywhere: { type: "boolean" },
32436
+ lan: { type: "boolean" },
32437
+ cloud: { type: "boolean" },
32180
32438
  slack: { type: "boolean" },
32181
32439
  telegram: { type: "boolean" },
32182
32440
  pair: { type: "boolean" },
@@ -32315,9 +32573,11 @@ function parseInvocation(argv) {
32315
32573
  const portals = [];
32316
32574
  if (command === "slack" || parsed.values.slack) portals.push("slack");
32317
32575
  if (command === "telegram" || parsed.values.telegram) portals.push("telegram");
32318
- const access3 = command === "remote" || parsed.values.pair ? "pair" : "loopback";
32576
+ const lan = Boolean(parsed.values.lan || parsed.values.pair);
32577
+ const access3 = lan ? "pair" : "loopback";
32319
32578
  const offerCode = command === "connect" ? parsed.positionals[1]?.trim() : void 0;
32320
- const site = command === "connect" ? offerCode ? { offerCode } : true : parsed.values.site ? true : void 0;
32579
+ const wantCloud = command === "remote" ? Boolean(parsed.values.cloud || parsed.values.site || !lan) : Boolean(parsed.values.site || parsed.values.cloud);
32580
+ const site = command === "connect" ? offerCode ? { offerCode } : true : wantCloud ? true : void 0;
32321
32581
  const surface = command === "web" ? void 0 : command;
32322
32582
  return {
32323
32583
  command: "web",
@@ -32485,16 +32745,18 @@ ${helpText}`);
32485
32745
  `);
32486
32746
  return;
32487
32747
  }
32748
+ const lan = kind === "remote" && options?.lan === true;
32488
32749
  const started = await (io.web ?? startCritiqueCodeWebServer)({
32489
32750
  cwd: invocation.cwd ?? io.cwd,
32490
32751
  env: io.env ?? process.env,
32491
32752
  stdout: io.stdout,
32492
32753
  stderr: io.stderr,
32493
32754
  open: false,
32494
- access: kind === "remote" ? "pair" : "loopback",
32755
+ access: lan ? "pair" : "loopback",
32495
32756
  ...options?.anywhere ? { anywhere: true } : {},
32496
32757
  ...kind === "slack" ? { portals: ["slack"] } : {},
32497
32758
  ...kind === "telegram" ? { portals: ["telegram"] } : {},
32759
+ ...kind === "remote" && !lan ? { site: true } : {},
32498
32760
  ...kind === "site" ? { site: options?.offerCode ? { offerCode: options.offerCode } : true } : {},
32499
32761
  ...inject ? { attach: { push: inject } } : {},
32500
32762
  ...io.driver ? { driver: io.driver } : {},
@@ -32510,8 +32772,8 @@ ${helpText}`);
32510
32772
  return;
32511
32773
  }
32512
32774
  if ("url" in started) {
32513
- io.stderr.write(kind === "remote" ? `Phone pairing is up. PIN is in the banner above. Kernel stays on this laptop.
32514
- ` : kind === "site" ? `critique.sh/code is attached. Kernel stays on this laptop.
32775
+ io.stderr.write(kind === "remote" && lan ? `Phone pairing is up. PIN is in the banner above. Kernel stays on this laptop.
32776
+ ` : kind === "remote" || kind === "site" ? `Critique Remote is attached at critique.sh/code. Kernel stays on this laptop.
32515
32777
  ` : `${kind} is attached. DM the bot. Kernel stays on this laptop.
32516
32778
  `);
32517
32779
  }