@critiquedotsh/harness 0.1.11 → 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 +493 -230
  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}
@@ -21365,10 +21507,11 @@ button,input,textarea,select{font:inherit;color:inherit}
21365
21507
  button{cursor:pointer}
21366
21508
  button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid rgba(143,183,255,.75);outline-offset:2px}
21367
21509
  .shell{display:grid;grid-template-columns:var(--rail-w) minmax(0,1fr) 1px var(--changes-w);height:100dvh;overflow:hidden}
21368
- .shell.rail-off{--rail-w:0px}
21510
+ .shell.rail-off{grid-template-columns:minmax(0,1fr) 1px var(--changes-w)}
21369
21511
  .shell.rail-off .rail{display:none}
21370
- .shell.changes-off{--changes-w:0px}
21512
+ .shell.changes-off{grid-template-columns:var(--rail-w) minmax(0,1fr)}
21371
21513
  .shell.changes-off .gutter,.shell.changes-off .changes{display:none}
21514
+ .shell.rail-off.changes-off{grid-template-columns:minmax(0,1fr)}
21372
21515
  .shell.term-off .term{display:none}
21373
21516
  .gutter{background:var(--line);cursor:col-resize}
21374
21517
 
@@ -21383,7 +21526,7 @@ button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px soli
21383
21526
  .rail-search{position:relative}
21384
21527
  .rail-search svg{position:absolute;left:10px;top:9px;color:var(--faint);pointer-events:none}
21385
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}
21386
- .rail .search:focus{border-color:var(--line);background:rgba(255,255,255,.025)}
21529
+ .rail .search:focus{border-color:var(--line);background:var(--wash)}
21387
21530
  #sessions{display:flex;flex-direction:column;gap:2px;min-height:0;overflow:auto;overscroll-behavior:contain;padding-right:1px;flex:1}
21388
21531
  .session-row{display:grid;grid-template-columns:minmax(0,1fr) 28px;gap:2px;align-items:center;border-radius:8px}
21389
21532
  .session-row:hover,.session-row.on{background:var(--hover)}
@@ -21399,14 +21542,14 @@ button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px soli
21399
21542
 
21400
21543
  /* main */
21401
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)}
21402
- .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}
21403
21546
  .topbar-left{display:flex;align-items:center;gap:9px;min-width:0;flex:1}
21404
21547
  .session-title{min-width:0;font-size:12.5px;font-weight:600;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
21405
21548
  .top-actions{display:flex;align-items:center;gap:5px;min-width:0}
21406
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}
21407
21550
  .icon-btn{width:30px;padding:0}
21408
21551
  .icon-btn:hover,.toolbar-btn:hover,.icon-btn.on,.toolbar-btn.on{background:var(--hover);color:var(--fg);border-color:var(--line)}
21409
- .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}
21410
21553
  .top-status,.badge{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--faint);font-size:11px}
21411
21554
  .badge.err{color:var(--red)}
21412
21555
  #stop{color:var(--red);background:rgba(239,119,112,.08)}
@@ -21421,20 +21564,20 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21421
21564
  #feed.on{display:block}
21422
21565
  .turn{margin:0 0 34px;position:relative}
21423
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}
21424
- .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}
21425
21568
  .reply>*:first-child{margin-top:0}.reply>*:last-child{margin-bottom:0}
21426
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}
21427
- .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)}
21428
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)}
21429
- .reply a{color:#a9c8ff;text-decoration:none}.reply a:hover{text-decoration:underline}
21430
- .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}
21431
- .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)}
21432
21575
  .reply hr{border:0;border-top:1px solid var(--line);margin:16px 0}
21433
- .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}
21434
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}
21435
21578
  .reply .md-code code{background:none;border:0;padding:0}
21436
- .md-table{margin:10px 0 14px;overflow:auto;border:1px solid var(--line);border-radius:10px;background:#121210}
21437
- .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}
21438
21581
 
21439
21582
  /* activity: each tool is its own row */
21440
21583
  .activity{display:grid;gap:5px;margin:9px 0 13px}
@@ -21446,24 +21589,24 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21446
21589
  .tool-event.running .tool-dot{background:var(--amber);box-shadow:0 0 0 3px rgba(224,170,67,.08)}
21447
21590
  .tool-event.done .tool-dot{background:var(--green)}
21448
21591
  .tool-event.failed .tool-dot{background:var(--red)}
21449
- .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}
21450
21593
  .tool-phase{color:var(--faint)}
21451
- .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}
21452
21595
  .think{margin:6px 0;color:var(--faint)}
21453
- .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)}
21454
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}
21455
21598
  .note{padding:5px 2px;background:transparent;border:0;color:var(--faint);font-family:ui-monospace,Menlo,monospace}
21456
- .alert{background:rgba(239,119,112,.07);border:1px solid rgba(239,119,112,.18);color:#f0b8b4}
21457
- .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)}
21458
21601
  .trust{display:none}
21459
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}
21460
21603
 
21461
21604
  /* composer */
21462
- .dock{padding:8px 18px 14px;background:linear-gradient(180deg,transparent,rgba(15,15,14,.96) 18%)}
21463
- .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)}
21464
21607
  .composer-line{display:flex;align-items:flex-end;gap:5px}
21465
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)}
21466
- .composer-line textarea::placeholder{color:#716b63}
21609
+ .composer-line textarea::placeholder{color:var(--placeholder)}
21467
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}
21468
21611
  .round-btn:hover,.plus-btn:hover,.mic-btn:hover,.mic-btn.on{background:var(--hover);color:var(--fg)}
21469
21612
  .composer-foot{display:flex;align-items:center;gap:6px;padding:3px 3px 1px;min-height:36px}
@@ -21473,12 +21616,12 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21473
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)}
21474
21617
  .pick-open .caret,#model-open .caret{opacity:.45;font-size:9px}
21475
21618
  .model-ico{width:15px!important;height:15px!important;display:block;flex:0 0 auto;border-radius:4px}
21476
- .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}
21477
- .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}
21478
21621
  .pick-menu.open,#model-menu.open,#mention.open,.cmd-pop.open,.branch-menu.open,.ship-menu.open{display:grid}
21479
21622
  .pick-menu{bottom:36px;left:0;min-width:210px;max-height:300px;overflow:auto;overscroll-behavior:contain;padding:5px}
21480
21623
  #model-menu{right:0;bottom:36px;width:310px;max-height:360px;grid-template-rows:auto minmax(0,1fr);overscroll-behavior:contain}
21481
- #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}
21482
21625
  #model-list{overflow:auto;overscroll-behavior:contain;max-height:310px;padding:5px}
21483
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}
21484
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)}
@@ -21487,37 +21630,37 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21487
21630
  #send:hover{transform:translateY(-1px);filter:brightness(1.04)}#send.steer{background:#bfe8cf;color:#142219}#send:disabled{opacity:.45;cursor:not-allowed}
21488
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)}
21489
21632
  .autonomy-pill.auto{color:var(--amber);border-color:rgba(224,170,67,.24);background:rgba(224,170,67,.055)}
21490
- .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}
21491
- .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)}
21492
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)}
21493
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}
21494
21637
 
21495
21638
  /* terminal */
21496
- .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)}
21497
21640
 
21498
21641
  /* workspace */
21499
- .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)}
21500
- #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}
21501
21644
 
21502
21645
  /* overlays */
21503
- .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)}
21504
- #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)}
21505
- .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)}
21506
21649
 
21507
21650
  /* command browser */
21508
- .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}
21509
21652
 
21510
21653
  /* settings */
21511
- .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)}
21512
- .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)}
21513
- .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}
21514
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}
21515
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)}
21516
21659
 
21517
21660
  /* resume picker */
21518
- .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}
21519
21662
 
21520
- @media(max-width:1100px){.shell,.shell.rail-off{grid-template-columns:var(--rail-w) 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)}}
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)}}
21521
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}}
21522
21665
  </style>
21523
21666
  </head>
@@ -21623,7 +21766,7 @@ main{min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:sta
21623
21766
  <button type="button" data-cmd="/login">Login / connect inference</button>
21624
21767
  </div>
21625
21768
  </div>
21626
- <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>
21627
21770
  <button type="button" class="mic-btn" id="voice" title="Voice input" aria-label="Voice input">
21628
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>
21629
21772
  </button>
@@ -21796,6 +21939,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
21796
21939
  var sessionTitleEl = document.getElementById("session-title");
21797
21940
  var activeSessionId = null;
21798
21941
  var refs = [];
21942
+ var pendingImages = [];
21799
21943
  var sessionMentions = [];
21800
21944
  var treePaths = [];
21801
21945
  var collapsedDirs = {};
@@ -21896,7 +22040,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
21896
22040
 
21897
22041
  function modelIconHtml(option) {
21898
22042
  var provider = option && (option.getAttribute("data-provider") || "") || "other";
21899
- var raw = option && (option.getAttribute("data-icon") || iconMap[provider] || "");
22043
+ var raw = iconMap[provider] || iconMap.other || "";
21900
22044
  if (raw) return raw;
21901
22045
  var label = option && (option.text || option.value) || provider || "model";
21902
22046
  var letter = String(label).replace(/^[^a-z0-9]+/i, "").charAt(0).toUpperCase() || "M";
@@ -21999,7 +22143,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
21999
22143
  btn.type = "button";
22000
22144
  btn.className = option.selected ? "on" : "";
22001
22145
  btn.textContent = "";
22002
- var icon = option.getAttribute("data-icon") || option.getAttribute("data-provider") ? modelIconHtml(option) : "";
22146
+ var icon = option.getAttribute("data-provider") ? modelIconHtml(option) : "";
22003
22147
  if (icon) btn.insertAdjacentHTML("beforeend", icon);
22004
22148
  var name = document.createElement("span");
22005
22149
  name.textContent = option.text || option.value;
@@ -22286,7 +22430,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22286
22430
  promptQueue.forEach(function (item, index) {
22287
22431
  var row = document.createElement("div");
22288
22432
  row.className = "queue-item";
22289
- 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;
22290
22435
  var rm = document.createElement("button");
22291
22436
  rm.type = "button";
22292
22437
  rm.textContent = "Remove";
@@ -22301,12 +22446,13 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22301
22446
  label.textContent = n ? n + " Queued \u21B5 to Send" : "";
22302
22447
  }
22303
22448
 
22304
- function enqueuePrompt(text) {
22305
- promptQueue.push(text);
22449
+ function enqueuePrompt(text, images) {
22450
+ promptQueue.push({ text: text, images: images || [] });
22306
22451
  renderQueue();
22307
22452
  }
22308
22453
 
22309
- function dispatchPrompt(text) {
22454
+ function dispatchPrompt(text, images) {
22455
+ var payloadImages = images || [];
22310
22456
  var normalized = text.trim().toLowerCase();
22311
22457
  if (normalized === "/resume" || normalized === "resume") {
22312
22458
  showResumePicker();
@@ -22322,7 +22468,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22322
22468
  api("POST", isCmd ? "/api/command" : "/api/prompt", {
22323
22469
  text: text,
22324
22470
  refs: refs,
22325
- 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 }; })
22326
22473
  }).then(function (res) {
22327
22474
  if (!res.ok) {
22328
22475
  return readError(res, "Request failed").then(function (msg) { addBlock("alert", msg); });
@@ -22341,7 +22488,10 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22341
22488
  if (!steerNow && busy) return;
22342
22489
  var next = promptQueue.shift();
22343
22490
  renderQueue();
22344
- if (next) dispatchPrompt(next);
22491
+ if (next) {
22492
+ if (typeof next === "string") dispatchPrompt(next, []);
22493
+ else dispatchPrompt(next.text || "", next.images || []);
22494
+ }
22345
22495
  }
22346
22496
 
22347
22497
  function setAlways(on) {
@@ -22701,8 +22851,8 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
22701
22851
  var vals = usageHistory.map(function (r) { return r.input + r.output + r.reasoning; });
22702
22852
  var max = Math.max.apply(null, vals.concat([1]));
22703
22853
  var w = 640, h = 150, gap = 2, bw = Math.max(2, (w - (vals.length - 1) * gap) / vals.length);
22704
- 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("");
22705
- 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>";
22706
22856
  }
22707
22857
  }
22708
22858
  var phaseRows = [];
@@ -23292,16 +23442,18 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23292
23442
 
23293
23443
  function submitText(raw, steerNow) {
23294
23444
  var text = (raw == null ? inputEl.value : raw).trim();
23295
- if (raw == null && text) { inputEl.value = ""; autosizeInput(); }
23296
- if (!text) {
23445
+ var images = raw == null ? pendingImages.slice() : [];
23446
+ if (raw == null && (text || images.length)) { inputEl.value = ""; autosizeInput(); }
23447
+ if (!text && !images.length) {
23297
23448
  if (steerNow) flushQueue(true);
23298
23449
  return;
23299
23450
  }
23451
+ if (raw == null) { pendingImages = []; renderChips(); }
23300
23452
  if (busy && !steerNow) {
23301
- enqueuePrompt(text);
23453
+ enqueuePrompt(text, images);
23302
23454
  return;
23303
23455
  }
23304
- dispatchPrompt(text);
23456
+ dispatchPrompt(text || "Look at the attached image.", images);
23305
23457
  }
23306
23458
 
23307
23459
  function decide(decision) {
@@ -23420,6 +23572,50 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23420
23572
  chip.addEventListener("click", function () { sessionMentions.splice(i, 1); renderChips(); });
23421
23573
  el.appendChild(chip);
23422
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;
23423
23619
  }
23424
23620
 
23425
23621
  function addRef(path) {
@@ -23749,6 +23945,7 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23749
23945
  var files = e.dataTransfer && e.dataTransfer.files;
23750
23946
  if (!files) return;
23751
23947
  Array.from(files).forEach(function (file) {
23948
+ if (addImageFile(file)) return;
23752
23949
  var reader = new FileReader();
23753
23950
  reader.onload = function () {
23754
23951
  pendingImport = { name: file.name, contents: String(reader.result || "") };
@@ -23760,6 +23957,26 @@ window.CRITIQUE_CODE_TOKEN = "__CRITIQUE_CODE_TOKEN__";
23760
23957
  reader.readAsText(file);
23761
23958
  });
23762
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
+ });
23763
23980
  if (filesBadge) filesBadge.addEventListener("click", function () {
23764
23981
  layout.changes = true;
23765
23982
  applyLayout();
@@ -25076,14 +25293,14 @@ async function loadPiTui() {
25076
25293
  return await import(specifier);
25077
25294
  } catch (error) {
25078
25295
  const { existsSync: existsSync3 } = await import("node:fs");
25079
- const { dirname: dirname11, join: join22 } = await import("node:path");
25296
+ const { dirname: dirname11, join: join23 } = await import("node:path");
25080
25297
  const { fileURLToPath: fileURLToPath3, pathToFileURL: pathToFileURL3 } = await import("node:url");
25081
25298
  const roots = [
25082
- join22(dirname11(fileURLToPath3(import.meta.url)), "../../packages/critique-code/.vendor"),
25083
- 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")
25084
25301
  ];
25085
25302
  for (const root of roots) {
25086
- 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");
25087
25304
  if (!existsSync3(candidate)) continue;
25088
25305
  return await import(pathToFileURL3(candidate).href);
25089
25306
  }
@@ -25235,7 +25452,7 @@ async function createInteractiveSettingsUi(input) {
25235
25452
  init_zod();
25236
25453
  import { spawn as spawn3 } from "node:child_process";
25237
25454
  import { accessSync, constants } from "node:fs";
25238
- import { delimiter as delimiter2, join as join15 } from "node:path";
25455
+ import { delimiter as delimiter2, join as join16 } from "node:path";
25239
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`.";
25240
25457
  var TOOL_COMMANDS = [
25241
25458
  "review",
@@ -25499,7 +25716,7 @@ function resolveCritiqueCliBinary(env = process.env) {
25499
25716
  for (const directory of pathEnv.split(delimiter2)) {
25500
25717
  if (!directory) continue;
25501
25718
  for (const name of names) {
25502
- const candidate = join15(directory, name);
25719
+ const candidate = join16(directory, name);
25503
25720
  try {
25504
25721
  accessSync(candidate, mode);
25505
25722
  return candidate;
@@ -25735,7 +25952,7 @@ var AUTHOR_PALETTE_ITEMS = [
25735
25952
  { value: "/repeat", label: "Repeat", description: "Re-run the last command" },
25736
25953
  { value: "/theme", label: "Theme", description: "Color and compact layout for this TUI" },
25737
25954
  { value: "/alias", label: "Aliases", description: "Bind shortcuts to command chains" },
25738
- { 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" },
25739
25956
  { value: "/connect", label: "Connect website", description: "Attach critique.sh/code to this laptop" },
25740
25957
  { value: "/slack", label: "Slack", description: "Attach Slack Socket Mode to this laptop session" },
25741
25958
  { value: "/telegram", label: "Telegram", description: "Attach a Telegram bot to this laptop session" },
@@ -25786,7 +26003,7 @@ function formatAuthorHelp() {
25786
26003
  " /context tokens and loaded files",
25787
26004
  " /status /logs /cost /doctor",
25788
26005
  " /undo /repeat /theme /alias",
25789
- " /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",
25790
26007
  " /connect attach critique.sh/code to this laptop (paste the website code)",
25791
26008
  " /slack attach Slack Socket Mode; DMs run on this laptop",
25792
26009
  " /telegram attach a Telegram bot; chats run on this laptop",
@@ -25975,6 +26192,7 @@ function parseAuthorCommand(line, aliases = {}) {
25975
26192
  if (lower.startsWith("/alias ")) return parseAlias(expanded);
25976
26193
  if (lower === "/remote" || lower === "remote") return { kind: "remote" };
25977
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 };
25978
26196
  if (lower === "/connect" || lower === "connect" || lower === "/site" || lower === "site") return { kind: "site" };
25979
26197
  if (lower.startsWith("/connect ")) return { kind: "site", offerCode: expanded.slice(9).trim() };
25980
26198
  if (lower === "/slack" || lower === "slack") return { kind: "slack" };
@@ -25991,7 +26209,7 @@ function parseAuthorCommand(line, aliases = {}) {
25991
26209
  // lib/finish/critique-code-web-changes.ts
25992
26210
  import { execFileSync } from "node:child_process";
25993
26211
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
25994
- import { readFile as readFile17 } from "node:fs/promises";
26212
+ import { readFile as readFile18 } from "node:fs/promises";
25995
26213
  import { dirname as dirname8, isAbsolute as isAbsolute16, relative as relative9, resolve as resolve11 } from "node:path";
25996
26214
  var SKIP_TREE_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", ".critique"]);
25997
26215
  function git3(root, args) {
@@ -26154,7 +26372,7 @@ async function workingTreeFilePatch(root, path2) {
26154
26372
  } catch {
26155
26373
  }
26156
26374
  try {
26157
- const contents = await readFile17(`${root}/${path2}`, "utf8");
26375
+ const contents = await readFile18(`${root}/${path2}`, "utf8");
26158
26376
  const lines = contents.split("\n");
26159
26377
  const body = lines.map((line) => `+${line}`).join("\n");
26160
26378
  return {
@@ -26376,9 +26594,9 @@ function commitMessageFromReview(input) {
26376
26594
 
26377
26595
  // lib/finish/critique-code-skills.ts
26378
26596
  init_critique_code_runtime_config();
26379
- 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";
26380
26598
  import { homedir as homedir2 } from "node:os";
26381
- 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";
26382
26600
  var CRITIQUE_CODE_SKILL_SCHEMA = "critique.code-skill.v1";
26383
26601
  var MAX_SKILL_BYTES = 64 * 1024;
26384
26602
  var MAX_IMPORTED_SKILLS = 80;
@@ -26600,16 +26818,16 @@ var PROJECT_SKILL_DIRS = [
26600
26818
  ];
26601
26819
  function userSkillDirs(home, userHome) {
26602
26820
  return [
26603
- { root: join16(home, "skills"), source: "imported" },
26604
- { root: join16(userHome, ".claude", "skills"), source: "claude-code" },
26605
- { root: join16(userHome, ".codex", "skills"), source: "codex" },
26606
- { root: join16(userHome, ".agents", "skills"), source: "agents" },
26607
- { root: join16(userHome, ".opencode", "skills"), source: "opencode" },
26608
- { 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" }
26609
26827
  ];
26610
26828
  }
26611
26829
  function critiqueCodeImportedSkillsPath(home) {
26612
- return join16(home, "skills");
26830
+ return join17(home, "skills");
26613
26831
  }
26614
26832
  function skillId(value) {
26615
26833
  const id4 = value.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -26636,7 +26854,7 @@ function parseFrontmatter(markdown) {
26636
26854
  async function readSkillFile(file, source, origin, fallbackId) {
26637
26855
  let markdown;
26638
26856
  try {
26639
- markdown = await readFile18(file, "utf8");
26857
+ markdown = await readFile19(file, "utf8");
26640
26858
  } catch {
26641
26859
  return void 0;
26642
26860
  }
@@ -26664,7 +26882,7 @@ async function loadSkillsFromRoot(root, source) {
26664
26882
  }
26665
26883
  const skills = [];
26666
26884
  for (const entry of entries) {
26667
- const nested4 = join16(root, entry, "SKILL.md");
26885
+ const nested4 = join17(root, entry, "SKILL.md");
26668
26886
  const skill = await readSkillFile(nested4, source, nested4, entry);
26669
26887
  if (skill) skills.push(skill);
26670
26888
  }
@@ -26714,7 +26932,7 @@ async function discoverCritiqueCodeSkills(input) {
26714
26932
  }
26715
26933
  let imported = 0;
26716
26934
  for (const dir of PROJECT_SKILL_DIRS) {
26717
- const root = join16(cwd, dir.relative);
26935
+ const root = join17(cwd, dir.relative);
26718
26936
  if (!contained(cwd, root)) continue;
26719
26937
  for (const skill of await loadSkillsFromRoot(root, dir.source)) {
26720
26938
  if (skill.source !== "builtin" && skill.source !== "imported") {
@@ -26746,20 +26964,20 @@ function formatCritiqueCodeSkillsList(skills) {
26746
26964
  `;
26747
26965
  }
26748
26966
  async function stageCritiqueCodeSkills(input) {
26749
- const dest = join16(input.agent_root, "skills");
26750
- await mkdir12(dest, { recursive: true });
26967
+ const dest = join17(input.agent_root, "skills");
26968
+ await mkdir13(dest, { recursive: true });
26751
26969
  for (const skill of input.skills) {
26752
26970
  const id4 = skillId(skill.id);
26753
26971
  if (!id4) continue;
26754
- const folder = join16(dest, id4);
26755
- await mkdir12(folder, { recursive: true });
26972
+ const folder = join17(dest, id4);
26973
+ await mkdir13(folder, { recursive: true });
26756
26974
  const header = `---
26757
26975
  name: ${skill.id}
26758
26976
  description: ${skill.description.replace(/\n/g, " ").slice(0, 1024)}
26759
26977
  ---
26760
26978
 
26761
26979
  `;
26762
- await writeFile11(join16(folder, "SKILL.md"), `${header}${skill.body.trim()}
26980
+ await writeFile12(join17(folder, "SKILL.md"), `${header}${skill.body.trim()}
26763
26981
  `, "utf8");
26764
26982
  }
26765
26983
  return dest;
@@ -26768,7 +26986,7 @@ async function importCritiqueCodeSkills(input) {
26768
26986
  const env = input.env ?? process.env;
26769
26987
  const home = critiqueCodeHome(env);
26770
26988
  const destRoot = critiqueCodeImportedSkillsPath(home);
26771
- await mkdir12(destRoot, { recursive: true });
26989
+ await mkdir13(destRoot, { recursive: true });
26772
26990
  const discovered = await discoverCritiqueCodeSkills({
26773
26991
  cwd: input.cwd,
26774
26992
  env,
@@ -26781,18 +26999,18 @@ async function importCritiqueCodeSkills(input) {
26781
26999
  skipped.push(skill.id);
26782
27000
  continue;
26783
27001
  }
26784
- const folder = join16(destRoot, skill.id);
26785
- await mkdir12(folder, { recursive: true });
27002
+ const folder = join17(destRoot, skill.id);
27003
+ await mkdir13(folder, { recursive: true });
26786
27004
  if (skill.origin.endsWith(`${sep10}SKILL.md`)) {
26787
27005
  try {
26788
- await cp(skill.origin, join16(folder, "SKILL.md"));
26789
- 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") });
26790
27008
  continue;
26791
27009
  } catch {
26792
27010
  }
26793
27011
  }
26794
- await writeFile11(
26795
- join16(folder, "SKILL.md"),
27012
+ await writeFile12(
27013
+ join17(folder, "SKILL.md"),
26796
27014
  `---
26797
27015
  name: ${skill.id}
26798
27016
  description: ${skill.description.replace(/\n/g, " ")}
@@ -26802,7 +27020,7 @@ ${skill.body.trim()}
26802
27020
  `,
26803
27021
  "utf8"
26804
27022
  );
26805
- imported.push({ ...skill, source: "imported", origin: join16(folder, "SKILL.md") });
27023
+ imported.push({ ...skill, source: "imported", origin: join17(folder, "SKILL.md") });
26806
27024
  }
26807
27025
  return { home, imported, skipped };
26808
27026
  }
@@ -26811,9 +27029,9 @@ ${skill.body.trim()}
26811
27029
  import { spawn as spawn4 } from "node:child_process";
26812
27030
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
26813
27031
  import { createRequire } from "node:module";
26814
- 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";
26815
27033
  import { tmpdir as tmpdir5 } from "node:os";
26816
- import { dirname as dirname9, join as join17 } from "node:path";
27034
+ import { dirname as dirname9, join as join18 } from "node:path";
26817
27035
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
26818
27036
 
26819
27037
  // lib/inference/qwen3-asr.ts
@@ -26907,8 +27125,8 @@ function resolveBundledFfmpegPath(from = import.meta.url) {
26907
27125
  try {
26908
27126
  let dir = dirname9(fileURLToPath2(from));
26909
27127
  for (let i = 0; i < 10; i++) {
26910
- const harnessPkg = join17(dir, "packages/critique-code/package.json");
26911
- const pkg = join17(dir, "package.json");
27128
+ const harnessPkg = join18(dir, "packages/critique-code/package.json");
27129
+ const pkg = join18(dir, "package.json");
26912
27130
  if (existsSync2(harnessPkg)) bases.push(pathToFileURL2(harnessPkg).href);
26913
27131
  if (existsSync2(pkg)) {
26914
27132
  try {
@@ -26945,8 +27163,8 @@ function listVoiceRecorderCandidates(input) {
26945
27163
  }
26946
27164
  async function recordMicrophoneWav(input) {
26947
27165
  const run = input.spawn ?? spawn4;
26948
- const dir = await mkdtemp3(join17(tmpdir5(), "critique-code-voice-"));
26949
- const file = join17(dir, "clip.wav");
27166
+ const dir = await mkdtemp3(join18(tmpdir5(), "critique-code-voice-"));
27167
+ const file = join18(dir, "clip.wav");
26950
27168
  const stop = input.waitForStop ?? (input.stdin ? () => waitForEnter(input.stdin) : void 0);
26951
27169
  if (!stop) {
26952
27170
  await rm3(dir, { recursive: true, force: true });
@@ -26965,7 +27183,7 @@ async function recordMicrophoneWav(input) {
26965
27183
  settled = true;
26966
27184
  clearTimeout(timeout);
26967
27185
  try {
26968
- const wav = await readFile19(file);
27186
+ const wav = await readFile20(file);
26969
27187
  if (wav.length >= 44) {
26970
27188
  resolve14({ wav });
26971
27189
  return;
@@ -27282,7 +27500,7 @@ function createPiAuthorTaskProjectedTool(projection) {
27282
27500
  init_pi_source_tool();
27283
27501
 
27284
27502
  // lib/finish/critique-code-workspace-memory.ts
27285
- 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";
27286
27504
  import { dirname as dirname10 } from "node:path";
27287
27505
  var MAX_MEMORY_RECORDS = 200;
27288
27506
  var MAX_MEMORY_VALUE_CHARS = 8192;
@@ -27346,7 +27564,7 @@ var CritiqueCodeWorkspaceMemory = class {
27346
27564
  }
27347
27565
  async load() {
27348
27566
  try {
27349
- const raw = await readFile20(this.#filePath, "utf8");
27567
+ const raw = await readFile21(this.#filePath, "utf8");
27350
27568
  const parsed = JSON.parse(raw);
27351
27569
  this.#records = Array.isArray(parsed.records) ? parsed.records.filter(isMemoryRecord).slice(-MAX_MEMORY_RECORDS) : [];
27352
27570
  } catch (error) {
@@ -27374,8 +27592,8 @@ var CritiqueCodeWorkspaceMemory = class {
27374
27592
  if (this.#records.length > MAX_MEMORY_RECORDS) {
27375
27593
  this.#records = this.#records.slice(-MAX_MEMORY_RECORDS);
27376
27594
  }
27377
- await mkdir13(dirname10(this.#filePath), { recursive: true });
27378
- 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 })}
27379
27597
  `, { mode: 384 });
27380
27598
  await chmod3(this.#filePath, 384);
27381
27599
  }
@@ -27397,7 +27615,7 @@ var CritiqueCodeWorkspaceMemory = class {
27397
27615
 
27398
27616
  // lib/finish/critique-code-workspace-tool.ts
27399
27617
  init_zod();
27400
- import { readFile as readFile21 } from "node:fs/promises";
27618
+ import { readFile as readFile22 } from "node:fs/promises";
27401
27619
 
27402
27620
  // lib/finish/critique-code-ts-edit.ts
27403
27621
  import ts2 from "typescript";
@@ -27441,7 +27659,7 @@ function renameTsIdentifier(input) {
27441
27659
  import { execFileSync as execFileSync2 } from "node:child_process";
27442
27660
  import { readFileSync as readFileSync3, readdirSync } from "node:fs";
27443
27661
  import { createServer } from "node:net";
27444
- import { join as join18 } from "node:path";
27662
+ import { join as join19 } from "node:path";
27445
27663
  var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".critique", "dist"]);
27446
27664
  var SUBPROCESS_TIMEOUT_MS = 4e3;
27447
27665
  var FETCH_TIMEOUT_MS = 8e3;
@@ -27530,7 +27748,7 @@ function walkWorkspaceFiles(root, pattern, maxHits) {
27530
27748
  if (hits.length >= maxHits) return;
27531
27749
  if (SKIP_DIR_NAMES.has(entry.name)) continue;
27532
27750
  const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
27533
- const abs = join18(absDir, entry.name);
27751
+ const abs = join19(absDir, entry.name);
27534
27752
  if (entry.isDirectory()) {
27535
27753
  visit(abs, rel);
27536
27754
  continue;
@@ -27581,7 +27799,7 @@ function grepWorkspaceJs(root, query, maxHits) {
27581
27799
  if (hits.length >= maxHits) break;
27582
27800
  let bytes;
27583
27801
  try {
27584
- bytes = readFileSync3(join18(root, rel));
27802
+ bytes = readFileSync3(join19(root, rel));
27585
27803
  } catch {
27586
27804
  continue;
27587
27805
  }
@@ -27893,19 +28111,19 @@ var PiWorkspaceToolProjection = class {
27893
28111
  return { hits: grepWorkspaceIntel(this.#root, input.query) };
27894
28112
  case "diff": {
27895
28113
  if (!input.path) throw new Error("diff requires path.");
27896
- const current = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28114
+ const current = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27897
28115
  const previous = input.before ?? "";
27898
28116
  return { path: input.path, patch: unifiedDiff(previous, input.after ?? current, input.path) };
27899
28117
  }
27900
28118
  case "parse_config": {
27901
28119
  if (!input.path) throw new Error("parse_config requires path.");
27902
- const text = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28120
+ const text = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27903
28121
  return parseStructuredConfig(text, input.kind ?? "auto");
27904
28122
  }
27905
28123
  case "env_keys": {
27906
28124
  const rel = input.path ?? ".env";
27907
28125
  try {
27908
- const text = await readFile21(safeRepoPath(this.#root, rel), "utf8");
28126
+ const text = await readFile22(safeRepoPath(this.#root, rel), "utf8");
27909
28127
  return { path: rel, keys: parseDotenvKeys(text), limitation: "Values are never returned." };
27910
28128
  } catch {
27911
28129
  return { path: rel, keys: [], limitation: "File missing. Values are never returned." };
@@ -27924,7 +28142,7 @@ var PiWorkspaceToolProjection = class {
27924
28142
  return { records: this.#memory.recall(input.query ?? input.key ?? "") };
27925
28143
  case "rename_ts": {
27926
28144
  if (!input.path || !input.from || !input.to) throw new Error("rename_ts requires path, from, and to.");
27927
- const source = await readFile21(safeRepoPath(this.#root, input.path), "utf8");
28145
+ const source = await readFile22(safeRepoPath(this.#root, input.path), "utf8");
27928
28146
  const renamed = renameTsIdentifier({ source, fileName: input.path, from: input.from, to: input.to });
27929
28147
  return {
27930
28148
  ...renamed,
@@ -28139,7 +28357,7 @@ function wrapAuthorWrite(inner, onWrite, workspaceRoot) {
28139
28357
  let previous = null;
28140
28358
  if (path2) {
28141
28359
  try {
28142
- previous = await readFile22(join19(workspaceRoot, path2), "utf8");
28360
+ previous = await readFile23(join20(workspaceRoot, path2), "utf8");
28143
28361
  } catch {
28144
28362
  previous = null;
28145
28363
  }
@@ -28158,7 +28376,7 @@ function liveSourceState(state) {
28158
28376
  }
28159
28377
  async function loadReleasedFile(repositoryRoot, name, maxChars) {
28160
28378
  try {
28161
- const text = (await readFile22(join19(repositoryRoot, name), "utf8")).trim();
28379
+ const text = (await readFile23(join20(repositoryRoot, name), "utf8")).trim();
28162
28380
  if (!text) return void 0;
28163
28381
  const release = await patternSecretEgress().release({ kind: "model_prompt", content: text.slice(0, maxChars) });
28164
28382
  return release.content;
@@ -28240,14 +28458,14 @@ async function runLocalCritiqueCodeAuthor(input) {
28240
28458
  const storeRoot = input.store_root ?? defaultCritiqueCodeStoreRoot(repositoryRoot);
28241
28459
  const sessionOps = new FileSessionOpsStore(storeRoot);
28242
28460
  await sessionOps.load();
28243
- const workspaceMemory = new CritiqueCodeWorkspaceMemory(join19(storeRoot, "workspace-memory.json"));
28461
+ const workspaceMemory = new CritiqueCodeWorkspaceMemory(join20(storeRoot, "workspace-memory.json"));
28244
28462
  await workspaceMemory.load();
28245
- const agentRoot = input.agent_root ?? join19(tmpdir6(), "critique-code-pi-agent");
28463
+ const agentRoot = input.agent_root ?? join20(tmpdir6(), "critique-code-pi-agent");
28246
28464
  if (!isAbsolute19(agentRoot)) {
28247
28465
  reader.close();
28248
28466
  return { kind: "kernel_required", limitation: "Pi agent root must be an absolute controller-owned directory." };
28249
28467
  }
28250
- if (!input.agent_root) await mkdir14(agentRoot, { recursive: true });
28468
+ if (!input.agent_root) await mkdir15(agentRoot, { recursive: true });
28251
28469
  const conversationIdSeed = `pi_${createHash38("sha256").update(repositoryRoot).digest("hex").slice(0, 24)}`;
28252
28470
  const review = input.review ?? runLocalCritiqueCodeReview;
28253
28471
  const checkpoints = new FileReviewCheckpointStore(storeRoot);
@@ -28689,7 +28907,7 @@ ${prior}`.trim();
28689
28907
  emitReviewController({ reviewing: false });
28690
28908
  }
28691
28909
  };
28692
- const promptAuthor = async (prompt) => {
28910
+ const promptAuthor = async (prompt, images) => {
28693
28911
  await ensureSession();
28694
28912
  noteWithheld(prompt);
28695
28913
  reader.pause();
@@ -28707,7 +28925,8 @@ ${prior}`.trim();
28707
28925
  const turn = await authors.prompt({
28708
28926
  conversation_id: conversationId2,
28709
28927
  prompt: outbound,
28710
- timeout_ms: timeoutMs
28928
+ timeout_ms: timeoutMs,
28929
+ ...images && images.length > 0 ? { images } : {}
28711
28930
  });
28712
28931
  await sessionOps.recordCost({
28713
28932
  at: now().toISOString(),
@@ -28760,10 +28979,17 @@ ${prior}`.trim();
28760
28979
  ensureStarted(input.intent.trim());
28761
28980
  }
28762
28981
  for (; ; ) {
28763
- const line = await takeLine();
28764
- 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
+ })) : [];
28765
28991
  let command = authorCommand(line, sessionOps.state().aliases, skills);
28766
- if (!command && !line.trim()) continue;
28992
+ if (!command && !line.trim() && packedImages.length === 0) continue;
28767
28993
  if (command && command.kind !== "repeat" && command.kind !== "palette" && command.kind !== "help") {
28768
28994
  await sessionOps.setLastCommand(line.trim());
28769
28995
  }
@@ -28812,14 +29038,14 @@ ${prior}`.trim();
28812
29038
  }
28813
29039
  if (command?.kind === "remote" || command?.kind === "slack" || command?.kind === "telegram" || command?.kind === "site") {
28814
29040
  if (!input.on_surface) {
28815
- 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.
28816
29042
  `);
28817
29043
  continue;
28818
29044
  }
28819
29045
  try {
28820
29046
  await input.on_surface(
28821
29047
  command.kind,
28822
- 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 } : {}
28823
29049
  );
28824
29050
  } catch (error) {
28825
29051
  say(`${error instanceof Error ? error.message : String(error)}
@@ -28870,7 +29096,7 @@ ${prior}`.trim();
28870
29096
  }
28871
29097
  }
28872
29098
  if (thread.action === "fork") {
28873
- const id5 = randomBytes(6).toString("hex");
29099
+ const id5 = randomBytes2(6).toString("hex");
28874
29100
  const briefing = withheld.trim();
28875
29101
  const from = activeThread;
28876
29102
  switchThread(id5, true);
@@ -28890,7 +29116,7 @@ ${briefing}
28890
29116
  `);
28891
29117
  continue;
28892
29118
  }
28893
- const id4 = thread.id?.trim() || randomBytes(6).toString("hex");
29119
+ const id4 = thread.id?.trim() || randomBytes2(6).toString("hex");
28894
29120
  switchThread(id4, thread.action === "new");
28895
29121
  await sessionOps.touchSession({
28896
29122
  id: id4,
@@ -28984,7 +29210,7 @@ ${briefing}
28984
29210
  `);
28985
29211
  continue;
28986
29212
  }
28987
- const id4 = randomBytes(4).toString("hex");
29213
+ const id4 = randomBytes2(4).toString("hex");
28988
29214
  await sessionOps.addSnapshot({
28989
29215
  id: id4,
28990
29216
  label: command.label?.trim() || `checkpoint ${id4}`,
@@ -29050,10 +29276,10 @@ ${briefing}
29050
29276
  const html = command.format === "html" ? `<pre>${markdown.replaceAll("&", "&amp;").replaceAll("<", "&lt;")}</pre>
29051
29277
  ` : markdown;
29052
29278
  const name = `session-${activeThread}.${command.format === "html" ? "html" : "md"}`;
29053
- const directory = join19(storeRoot, "exports");
29054
- await mkdir14(directory, { recursive: true, mode: 448 });
29055
- const target = join19(directory, name);
29056
- 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 });
29057
29283
  say(`Exported ${target}
29058
29284
  `);
29059
29285
  continue;
@@ -29405,9 +29631,9 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29405
29631
  say("Nothing to undo. /rollback restores a full checkpoint.\n");
29406
29632
  continue;
29407
29633
  }
29408
- const target = join19(repositoryRoot, entry.path);
29634
+ const target = join20(repositoryRoot, entry.path);
29409
29635
  if (entry.previous === null) await unlink4(target).catch(() => void 0);
29410
- else await writeFile13(target, entry.previous);
29636
+ else await writeFile14(target, entry.previous);
29411
29637
  say(`Undid ${entry.path}.
29412
29638
  `);
29413
29639
  continue;
@@ -29543,8 +29769,8 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29543
29769
  if (voiceMode) listenNow = true;
29544
29770
  continue;
29545
29771
  }
29546
- ensureStarted(line.trim());
29547
- 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);
29548
29774
  if (voiceMode) listenNow = true;
29549
29775
  }
29550
29776
  } finally {
@@ -29570,10 +29796,10 @@ ${current.map((path3) => `- ${path3}`).join("\n")}
29570
29796
 
29571
29797
  // lib/finish/critique-code-web-server.ts
29572
29798
  init_change_capsule();
29573
- import { randomBytes as randomBytes2 } from "node:crypto";
29799
+ import { randomBytes as randomBytes3 } from "node:crypto";
29574
29800
  import { createServer as createServer2 } from "node:http";
29575
- import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile15 } from "node:fs/promises";
29576
- 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";
29577
29803
  import { execFileSync as execFileSync3, spawn as spawn7 } from "node:child_process";
29578
29804
 
29579
29805
  // lib/finish/critique-code-line-hub.ts
@@ -29787,23 +30013,39 @@ function originAllowed(input) {
29787
30013
  return parsed.host.toLowerCase() === host;
29788
30014
  }
29789
30015
  function formatRemoteBanner(input) {
29790
- const lan = input.urls.lan.length > 0 ? input.urls.lan.join("\n ") : "no LAN IPv4 yet \u2014 stay on loopback or use a portal";
29791
- 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.";
29792
- const tunnel = input.urls.tunnel ? `Anywhere URL:
29793
- ${input.urls.tunnel}` : "Off-Wi-Fi without Slack/Telegram: pass --anywhere (uses your cloudflared or ngrok). Not Critique Cloud.";
29794
- return [
29795
- `CritiqueCode remote \xB7 ${input.repository_name}`,
29796
- "This laptop is the agent. Phone and portals are remotes. Nothing executes in Critique Cloud.",
29797
- `Pairing PIN ${input.pin_display}`,
29798
- `Loopback
29799
- ${input.urls.loopback}`,
29800
- `On this Wi-Fi
29801
- ${lan}`,
29802
- tunnel,
29803
- portals,
29804
- "Leave this process running. Sleep or closing the terminal drops the phone.",
29805
- ""
29806
- ].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");
29807
30049
  }
29808
30050
  function withToken(url, token) {
29809
30051
  const parsed = new URL(url);
@@ -29885,8 +30127,8 @@ function spawnTunnel(command, args) {
29885
30127
 
29886
30128
  // lib/finish/critique-code-portals.ts
29887
30129
  init_critique_code_runtime_config();
29888
- import { mkdir as mkdir15, readFile as readFile23, writeFile as writeFile14 } from "node:fs/promises";
29889
- 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";
29890
30132
  var CRITIQUE_CODE_PORTALS_SCHEMA = "critique.code-portals.v1";
29891
30133
  function isRecord2(value) {
29892
30134
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -29951,7 +30193,7 @@ async function loadCritiqueCodePortals(env = process.env) {
29951
30193
  const fromEnv = portalsFromEnv(env);
29952
30194
  const home = critiqueCodeHome(env);
29953
30195
  try {
29954
- const raw = JSON.parse(await readFile23(join20(home, "portals.json"), "utf8"));
30196
+ const raw = JSON.parse(await readFile24(join21(home, "portals.json"), "utf8"));
29955
30197
  const stored = parseStoredPortals(raw);
29956
30198
  return {
29957
30199
  schema_version: CRITIQUE_CODE_PORTALS_SCHEMA,
@@ -30288,27 +30530,31 @@ async function startCritiqueCodeSiteRelay(input) {
30288
30530
  repository: input.repository?.trim() || basename3(input.cwd),
30289
30531
  cwd: input.cwd
30290
30532
  };
30291
- const claimed = input.offerCode ? await postJson(fetchImpl, `${origin}/api/new/v1/code/claim`, headers, {
30292
- code: input.offerCode,
30293
- ...machine
30294
- }) : 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
+ });
30295
30537
  if (!claimed.ok) {
30296
30538
  return { limitation: claimed.error || "critique.sh did not accept this PC." };
30297
30539
  }
30298
- const agentId = String(claimed.body.agentId ?? "");
30540
+ const agentId = String(claimed.body.id ?? claimed.body.agentId ?? "");
30299
30541
  const agentToken = String(claimed.body.agentToken ?? "");
30542
+ const browserUrl = String(claimed.body.browser_url ?? `${origin}/code`);
30300
30543
  if (!agentId || !agentToken) {
30301
30544
  return { limitation: "critique.sh did not return a machine token." };
30302
30545
  }
30303
- 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.`);
30304
30547
  const abort = new AbortController();
30548
+ const baseInterval = input.intervalMs ?? 800;
30549
+ const maxInterval = input.maxIntervalMs ?? 3e4;
30305
30550
  const loop = (async () => {
30306
30551
  const sleep = input.sleep ?? defaultSleep2;
30552
+ let delay = baseInterval;
30307
30553
  while (!abort.signal.aborted) {
30308
30554
  const events = input.takeEvents().map((event) => ({ ...event }));
30309
30555
  const synced = await postJson(
30310
30556
  fetchImpl,
30311
- `${origin}/api/new/v1/code/sync`,
30557
+ `${origin}/api/new/v1/code/relay/agent/${agentId}`,
30312
30558
  {
30313
30559
  authorization: `Bearer ${agentToken}`,
30314
30560
  "content-type": "application/json"
@@ -30319,17 +30565,21 @@ async function startCritiqueCodeSiteRelay(input) {
30319
30565
  }
30320
30566
  );
30321
30567
  if (synced.ok && Array.isArray(synced.body.commands)) {
30568
+ delay = baseInterval;
30322
30569
  for (const command of synced.body.commands) {
30323
30570
  applySiteCommand(input.host, command);
30324
30571
  }
30572
+ } else {
30573
+ delay = Math.min(delay * 2, maxInterval);
30325
30574
  }
30326
- await sleep(input.intervalMs ?? 800);
30575
+ await sleep(delay);
30327
30576
  }
30328
30577
  })();
30329
30578
  void loop.catch(() => void 0);
30330
30579
  return {
30331
30580
  agentId,
30332
30581
  origin,
30582
+ url: browserUrl,
30333
30583
  async close() {
30334
30584
  abort.abort();
30335
30585
  }
@@ -30771,7 +31021,7 @@ async function startCritiqueCodeWebServer(input) {
30771
31021
  return { kind: "kernel_required", limitation: error instanceof Error ? error.message : String(error) };
30772
31022
  }
30773
31023
  const repositoryRoot = await findRepositoryRoot(input.cwd);
30774
- const token = randomBytes2(18).toString("base64url");
31024
+ const token = randomBytes3(18).toString("base64url");
30775
31025
  const hub = new CritiqueCodeLineHub();
30776
31026
  const pushLine = (line) => input.attach?.push(line) ?? hub.push(line);
30777
31027
  const clients = /* @__PURE__ */ new Set();
@@ -30800,16 +31050,16 @@ async function startCritiqueCodeWebServer(input) {
30800
31050
  let reviewModels = [...initialReviewModels];
30801
31051
  let pendingExec;
30802
31052
  const storeRoot = input.store_root ?? defaultCritiqueCodeStoreRoot(repositoryRoot);
30803
- const chatsPath = join21(storeRoot, "web-chats.json");
31053
+ const chatsPath = join22(storeRoot, "web-chats.json");
30804
31054
  let chats = [{
30805
- id: randomBytes2(6).toString("hex"),
31055
+ id: randomBytes3(6).toString("hex"),
30806
31056
  title: "New session",
30807
31057
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
30808
31058
  events: []
30809
31059
  }];
30810
31060
  let activeId = chats[0].id;
30811
31061
  try {
30812
- const raw = JSON.parse(await readFile24(chatsPath, "utf8"));
31062
+ const raw = JSON.parse(await readFile25(chatsPath, "utf8"));
30813
31063
  if (Array.isArray(raw.chats) && raw.chats.length > 0) {
30814
31064
  chats = raw.chats.map((chat) => ({
30815
31065
  id: String(chat.id),
@@ -30824,7 +31074,7 @@ async function startCritiqueCodeWebServer(input) {
30824
31074
  const sessionSummaries = () => chats.map((chat) => ({ id: chat.id, title: chat.title, created_at: chat.created_at }));
30825
31075
  const activeChat = () => chats.find((chat) => chat.id === activeId) ?? chats[0];
30826
31076
  const persistChats = () => {
30827
- void mkdir16(storeRoot, { recursive: true, mode: 448 }).then(() => writeFile15(
31077
+ void mkdir17(storeRoot, { recursive: true, mode: 448 }).then(() => writeFile16(
30828
31078
  chatsPath,
30829
31079
  `${JSON.stringify({ active_id: activeId, chats }, null, 2)}
30830
31080
  `,
@@ -31012,7 +31262,7 @@ data: ${JSON.stringify(stamped)}
31012
31262
  return "approve";
31013
31263
  }
31014
31264
  return new Promise((resolve14) => {
31015
- const id4 = randomBytes2(9).toString("hex");
31265
+ const id4 = randomBytes3(9).toString("hex");
31016
31266
  const pending = { id: id4, purpose: request.purpose, preview: request.preview, resolve: resolve14 };
31017
31267
  execs.set(id4, pending);
31018
31268
  pendingExec = { id: id4, purpose: request.purpose, preview: request.preview };
@@ -31260,7 +31510,7 @@ exit ${code2 ?? "killed"}
31260
31510
  }
31261
31511
  if (req.method === "POST" && path2 === "/api/sessions") {
31262
31512
  const chat = {
31263
- id: randomBytes2(6).toString("hex"),
31513
+ id: randomBytes3(6).toString("hex"),
31264
31514
  title: "New session",
31265
31515
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
31266
31516
  events: []
@@ -31276,7 +31526,7 @@ exit ${code2 ?? "killed"}
31276
31526
  if (req.method === "POST" && path2 === "/api/sessions/fork") {
31277
31527
  const source = activeChat();
31278
31528
  const chat = {
31279
- id: randomBytes2(6).toString("hex"),
31529
+ id: randomBytes3(6).toString("hex"),
31280
31530
  title: `${source.title === "New session" ? "Fork" : source.title} \xB7 fork`,
31281
31531
  created_at: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
31282
31532
  events: source.events.map((event) => ({ ...event }))
@@ -31431,7 +31681,8 @@ exit ${code2 ?? "killed"}
31431
31681
  }
31432
31682
  if (req.method === "POST" && (path2 === "/api/prompt" || path2 === "/api/command")) {
31433
31683
  const body = await readJson(req);
31434
- 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." : "");
31435
31686
  if (!text) {
31436
31687
  send(res, 400, { error: "text required" });
31437
31688
  return;
@@ -31485,7 +31736,12 @@ ${extras.join("\n\n")}` : text);
31485
31736
  nameActiveChat(text);
31486
31737
  emitGate();
31487
31738
  }
31488
- 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
+ );
31489
31745
  send(res, queued ? 202 : 409, { ok: queued });
31490
31746
  return;
31491
31747
  }
@@ -31856,15 +32112,16 @@ ${extras.join("\n\n")}` : text);
31856
32112
  else siteRelay = startedSite;
31857
32113
  }
31858
32114
  }
31859
- if (access3 === "pair") {
32115
+ if (access3 === "pair" || siteRelay) {
31860
32116
  input.stderr.write(formatRemoteBanner({
31861
32117
  repository_name: basename4(repositoryRoot),
31862
- pin_display: pairing?.pin_display ?? "",
32118
+ ...pairing ? { pin_display: pairing.pin_display } : {},
31863
32119
  urls: {
31864
32120
  loopback: urls.loopback,
31865
32121
  lan: urls.lan,
31866
32122
  ...urls.tunnel ? { tunnel: urls.tunnel } : {}
31867
32123
  },
32124
+ ...siteRelay ? { cloud_url: siteRelay.url } : {},
31868
32125
  portals: connectedPortals
31869
32126
  }));
31870
32127
  } else {
@@ -32009,7 +32266,7 @@ Loopback only. The author session stays in this process.
32009
32266
  ...urls.lan.length > 0 ? { lan_urls: urls.lan } : {},
32010
32267
  ...urls.tunnel ? { tunnel_url: urls.tunnel } : {},
32011
32268
  ...connectedPortals.length > 0 ? { portals: connectedPortals } : {},
32012
- ...siteRelay ? { site: { agentId: siteRelay.agentId, origin: siteRelay.origin } } : {},
32269
+ ...siteRelay ? { site: { agentId: siteRelay.agentId, origin: siteRelay.origin, url: siteRelay.url } } : {},
32013
32270
  async close() {
32014
32271
  hub.close();
32015
32272
  for (const adapter of portalAdapters) await adapter.stop().catch(() => void 0);
@@ -32050,7 +32307,7 @@ Usage:
32050
32307
  pnpm --filter @critiquedotsh/harness start
32051
32308
  critique-code | critique-code chat [--intent <text>] [--models provider/model,...] [--cwd <dir>] [--store <dir>] [--voice] [--json]
32052
32309
  critique-code web [--port <n>] [--cwd <dir>] [--store <dir>] [--models ...]
32053
- 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 ...]
32054
32311
  critique-code connect [CODE] [--port <n>] [--cwd <dir>]
32055
32312
  critique-code slack [--port <n>] [--cwd <dir>]
32056
32313
  critique-code telegram [--port <n>] [--cwd <dir>]
@@ -32078,7 +32335,7 @@ Interactive author commands:
32078
32335
  /watch on Review on file save
32079
32336
  /repair Verified repair of promoted findings (not auto-applied chatter)
32080
32337
  /ship Review, then end if the controller allows
32081
- /remote Pair a phone; this laptop stays the agent (LAN PIN)
32338
+ /remote Attach critique.sh/code to this laptop (Critique Remote)
32082
32339
  /connect [CODE] Attach critique.sh/code to this laptop
32083
32340
  /slack Attach Slack Socket Mode to this session
32084
32341
  /telegram Attach a Telegram bot to this session
@@ -32092,7 +32349,7 @@ Interactive author commands:
32092
32349
 
32093
32350
  Interactive TTY author sessions keep stdout quiet; pass --json for the session envelope.
32094
32351
  \`critique-code web\` serves a loopback browser UI on 127.0.0.1. The author kernel still runs in this process.
32095
- \`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.
32096
32353
  \`critique-code connect [CODE]\` registers this PC on critique.sh/code. The website is a remote; the kernel stays here.
32097
32354
  \`critique-code slack\` / \`critique-code telegram\` open outbound bots so you can DM the agent from those apps. The kernel stays here.
32098
32355
  Other commands print JSON on stdout. Live chrome goes to stderr.
@@ -32176,6 +32433,8 @@ function parseInvocation(argv) {
32176
32433
  repair: { type: "string" },
32177
32434
  port: { type: "string" },
32178
32435
  anywhere: { type: "boolean" },
32436
+ lan: { type: "boolean" },
32437
+ cloud: { type: "boolean" },
32179
32438
  slack: { type: "boolean" },
32180
32439
  telegram: { type: "boolean" },
32181
32440
  pair: { type: "boolean" },
@@ -32314,9 +32573,11 @@ function parseInvocation(argv) {
32314
32573
  const portals = [];
32315
32574
  if (command === "slack" || parsed.values.slack) portals.push("slack");
32316
32575
  if (command === "telegram" || parsed.values.telegram) portals.push("telegram");
32317
- 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";
32318
32578
  const offerCode = command === "connect" ? parsed.positionals[1]?.trim() : void 0;
32319
- 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;
32320
32581
  const surface = command === "web" ? void 0 : command;
32321
32582
  return {
32322
32583
  command: "web",
@@ -32484,16 +32745,18 @@ ${helpText}`);
32484
32745
  `);
32485
32746
  return;
32486
32747
  }
32748
+ const lan = kind === "remote" && options?.lan === true;
32487
32749
  const started = await (io.web ?? startCritiqueCodeWebServer)({
32488
32750
  cwd: invocation.cwd ?? io.cwd,
32489
32751
  env: io.env ?? process.env,
32490
32752
  stdout: io.stdout,
32491
32753
  stderr: io.stderr,
32492
32754
  open: false,
32493
- access: kind === "remote" ? "pair" : "loopback",
32755
+ access: lan ? "pair" : "loopback",
32494
32756
  ...options?.anywhere ? { anywhere: true } : {},
32495
32757
  ...kind === "slack" ? { portals: ["slack"] } : {},
32496
32758
  ...kind === "telegram" ? { portals: ["telegram"] } : {},
32759
+ ...kind === "remote" && !lan ? { site: true } : {},
32497
32760
  ...kind === "site" ? { site: options?.offerCode ? { offerCode: options.offerCode } : true } : {},
32498
32761
  ...inject ? { attach: { push: inject } } : {},
32499
32762
  ...io.driver ? { driver: io.driver } : {},
@@ -32509,8 +32772,8 @@ ${helpText}`);
32509
32772
  return;
32510
32773
  }
32511
32774
  if ("url" in started) {
32512
- io.stderr.write(kind === "remote" ? `Phone pairing is up. PIN is in the banner above. Kernel stays on this laptop.
32513
- ` : 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.
32514
32777
  ` : `${kind} is attached. DM the bot. Kernel stays on this laptop.
32515
32778
  `);
32516
32779
  }