omp-fabric 1.25.35 → 1.26.1

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 (43) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +1 -0
  3. package/dist/chunks/{chunk-UKB5RJDP.js → chunk-BEFZRH76.js} +61 -1
  4. package/dist/chunks/{chunk-UKB5RJDP.js.map → chunk-BEFZRH76.js.map} +2 -2
  5. package/dist/chunks/{chunk-2C5YN36Y.js → chunk-GFUWAUL2.js} +15 -1
  6. package/dist/chunks/chunk-GFUWAUL2.js.map +7 -0
  7. package/dist/chunks/{chunk-JWC7B67Z.js → chunk-GGQ5MHXB.js} +62 -1
  8. package/dist/chunks/chunk-GGQ5MHXB.js.map +7 -0
  9. package/dist/chunks/{chunk-I2GBVR67.js → chunk-YHFQDI36.js} +3 -3
  10. package/dist/chunks/{chunk-53ZZTYHQ.js → chunk-YI4HU67V.js} +2 -2
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/core/system-guidance.d.ts.map +1 -1
  14. package/dist/fabric-runtime-state.d.ts.map +1 -1
  15. package/dist/fabric-runtime-state.js +804 -136
  16. package/dist/fabric-runtime-state.js.map +4 -4
  17. package/dist/index.js +3 -3
  18. package/dist/index.js.map +2 -2
  19. package/dist/providers/image-provider.d.ts +46 -0
  20. package/dist/providers/image-provider.d.ts.map +1 -0
  21. package/dist/residency/host.js +3 -3
  22. package/dist/residency/omp-entry.js +3 -3
  23. package/dist/runtime/core-override-guest-types.js +1 -1
  24. package/dist/runtime/guest-types.d.ts +1 -1
  25. package/dist/runtime/guest-types.d.ts.map +1 -1
  26. package/dist/runtime/guest-types.js +1 -1
  27. package/dist/runtime/node-process-runtime.js +1 -1
  28. package/dist/runtime/quickjs-runtime.d.ts +1 -1
  29. package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
  30. package/dist/runtime/quickjs-runtime.js +1 -1
  31. package/dist/ui/settings.d.ts +1 -1
  32. package/dist/ui/settings.d.ts.map +1 -1
  33. package/dist/ui/settings.js +126 -11
  34. package/dist/ui/settings.js.map +3 -3
  35. package/docs/architecture.md +1 -1
  36. package/docs/certification.md +1 -1
  37. package/docs/configuration.md +13 -1
  38. package/docs/image.md +79 -0
  39. package/package.json +1 -1
  40. package/dist/chunks/chunk-2C5YN36Y.js.map +0 -7
  41. package/dist/chunks/chunk-JWC7B67Z.js.map +0 -7
  42. /package/dist/chunks/{chunk-I2GBVR67.js.map → chunk-YHFQDI36.js.map} +0 -0
  43. /package/dist/chunks/{chunk-53ZZTYHQ.js.map → chunk-YI4HU67V.js.map} +0 -0
@@ -97,7 +97,7 @@ import {
97
97
  resolveAgentCwd,
98
98
  resolveOmpBinary,
99
99
  validateAgentCwdRequest
100
- } from "./chunks/chunk-53ZZTYHQ.js";
100
+ } from "./chunks/chunk-YI4HU67V.js";
101
101
  import "./chunks/chunk-VPPIJRNZ.js";
102
102
  import {
103
103
  executeFile,
@@ -117,7 +117,7 @@ import {
117
117
  resolveAgentDir,
118
118
  resolveAvailableOmpModel,
119
119
  resolveFabricModel
120
- } from "./chunks/chunk-JWC7B67Z.js";
120
+ } from "./chunks/chunk-GGQ5MHXB.js";
121
121
  import {
122
122
  FABRIC_ACTOR_HOST_EVENTS,
123
123
  isFabricActorHostEvent,
@@ -182,7 +182,7 @@ import {
182
182
  } from "./chunks/chunk-ATTYOQSI.js";
183
183
 
184
184
  // src/fabric-runtime-state.ts
185
- import path19 from "node:path";
185
+ import path20 from "node:path";
186
186
  import { fileURLToPath as fileURLToPath2 } from "node:url";
187
187
 
188
188
  // src/actors/global-registry.ts
@@ -2235,10 +2235,10 @@ var FabricComponentSupervisor = class {
2235
2235
  adjacent.set(edge.from, targets);
2236
2236
  }
2237
2237
  const cycles = /* @__PURE__ */ new Map();
2238
- const visit = (node, path20, positions) => {
2238
+ const visit = (node, path21, positions) => {
2239
2239
  const position = positions.get(node);
2240
2240
  if (position !== void 0) {
2241
- const cycle = path20.slice(position);
2241
+ const cycle = path21.slice(position);
2242
2242
  const rotations = cycle.map((_, index) => [
2243
2243
  ...cycle.slice(index),
2244
2244
  ...cycle.slice(0, index)
@@ -2248,10 +2248,10 @@ var FabricComponentSupervisor = class {
2248
2248
  cycles.set(canonical.join("\0"), canonical);
2249
2249
  return;
2250
2250
  }
2251
- if (path20.length > this.#components.size) return;
2252
- const nextPositions = new Map(positions).set(node, path20.length);
2251
+ if (path21.length > this.#components.size) return;
2252
+ const nextPositions = new Map(positions).set(node, path21.length);
2253
2253
  for (const target of adjacent.get(node) ?? []) {
2254
- visit(target, [...path20, node], nextPositions);
2254
+ visit(target, [...path21, node], nextPositions);
2255
2255
  }
2256
2256
  };
2257
2257
  for (const id of this.#components.keys()) visit(id, [], /* @__PURE__ */ new Map());
@@ -7060,9 +7060,9 @@ var CapturedToolsProvider = class {
7060
7060
  #allowedTools = readChildToolAllowlist();
7061
7061
  async list(request, _context) {
7062
7062
  const query = request.query?.trim().toLowerCase();
7063
- const descriptors8 = this.catalog.list().filter((entry) => !this.#allowedTools || this.#allowedTools.has(entry.name)).map(descriptorFrom);
7064
- if (!query) return descriptors8;
7065
- return descriptors8.filter(
7063
+ const descriptors9 = this.catalog.list().filter((entry) => !this.#allowedTools || this.#allowedTools.has(entry.name)).map(descriptorFrom);
7064
+ if (!query) return descriptors9;
7065
+ return descriptors9.filter(
7066
7066
  (descriptor) => `${descriptor.name} ${descriptor.description} ${descriptor.namespace ?? ""}`.toLowerCase().includes(query)
7067
7067
  );
7068
7068
  }
@@ -8498,6 +8498,657 @@ var CodemapProvider = class {
8498
8498
  }
8499
8499
  };
8500
8500
 
8501
+ // src/providers/image-provider.ts
8502
+ import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
8503
+ import os from "node:os";
8504
+ import path10 from "node:path";
8505
+ var IMAGE_MODEL_API = /image|imagen|dall-e|flux|stable-diffusion|sdxl|seedream|kolors|wanx|cogview|recraft|ideogram|midjourney|horde|banana/i;
8506
+ var generateSchema = {
8507
+ type: "object",
8508
+ additionalProperties: false,
8509
+ required: ["prompt"],
8510
+ properties: {
8511
+ prompt: { type: "string", description: "Full image description; the only required argument." },
8512
+ model: {
8513
+ type: "string",
8514
+ description: 'Optional provider/id to try first, e.g. "sub2api-agy/gemini-3.1-flash-image". Falls back to the configured chain.'
8515
+ },
8516
+ aspectRatio: {
8517
+ type: "string",
8518
+ description: "Aspect ratio forwarded as aspect_ratio or imageConfig.aspectRatio, e.g. 1:1, 16:9, 9:16."
8519
+ },
8520
+ size: {
8521
+ type: "string",
8522
+ enum: ["1024x1024", "1536x1024", "1024x1536"],
8523
+ description: "Pixel size for gateways that accept an images-API size field."
8524
+ },
8525
+ count: { type: "number", description: "Images to request from one model; only the images API honours more than one." },
8526
+ saveTo: { type: "string", description: "Absolute or cwd-relative file path for a single image." }
8527
+ }
8528
+ };
8529
+ var editSchema = {
8530
+ type: "object",
8531
+ additionalProperties: false,
8532
+ required: ["prompt", "images"],
8533
+ properties: {
8534
+ prompt: { type: "string", description: "What to change in the source images." },
8535
+ images: {
8536
+ type: "array",
8537
+ minItems: 1,
8538
+ description: "Source images as file paths or data URLs.",
8539
+ items: { type: "string" }
8540
+ },
8541
+ model: { type: "string", description: "Optional provider/id to try first." },
8542
+ aspectRatio: { type: "string", description: "Aspect ratio forwarded to the backend." },
8543
+ size: {
8544
+ type: "string",
8545
+ enum: ["1024x1024", "1536x1024", "1024x1536"],
8546
+ description: "Pixel size for gateways that accept an images-API size field."
8547
+ },
8548
+ saveTo: { type: "string", description: "Absolute or cwd-relative file path for a single image." }
8549
+ }
8550
+ };
8551
+ var modelsSchema = { type: "object", additionalProperties: false, properties: {} };
8552
+ var descriptors2 = [
8553
+ {
8554
+ name: "generate",
8555
+ description: "Generate an image from a prompt. Tries each configured model in priority order and returns the first success with its path, bytes, model, and dialect. Returns { ok: false, reason, failures } instead of throwing when every model fails.",
8556
+ inputSchema: generateSchema,
8557
+ risk: "write"
8558
+ },
8559
+ {
8560
+ name: "edit",
8561
+ description: "Edit existing images with a prompt. Tries each configured model in priority order and returns the first success or { ok: false, reason, failures }. Source images travel in the shape each dialect expects: input_references or image/images for the images API, image_url parts for chat, input_image for responses, inlineData for gemini.",
8562
+ inputSchema: editSchema,
8563
+ risk: "write"
8564
+ },
8565
+ {
8566
+ name: "models",
8567
+ description: "Show the resolved priority chain: each entry's provider, model id, api, inferred or overridden dialect, and whether credentials resolve. No network call.",
8568
+ inputSchema: modelsSchema,
8569
+ risk: "read"
8570
+ }
8571
+ ];
8572
+ var splitModelSelector = (selector) => {
8573
+ const trimmed = selector.trim();
8574
+ const separator = trimmed.indexOf("/");
8575
+ if (separator <= 0 || separator === trimmed.length - 1) return void 0;
8576
+ return { provider: trimmed.slice(0, separator), id: trimmed.slice(separator + 1) };
8577
+ };
8578
+ var inferImageDialect = (api, modelId) => {
8579
+ if (api === "openai-images" || api === "openrouter-images") return "images";
8580
+ if (api === "google-generative-ai") return "gemini";
8581
+ if (api === "openai-responses" || api === "openai-codex-responses") {
8582
+ return IMAGE_MODEL_API.test(modelId) ? "images" : "responses";
8583
+ }
8584
+ return "chat";
8585
+ };
8586
+ var GPT_IMAGE_FAMILY = /gpt-image|chatgpt-image/i;
8587
+ var mimeFromMagic = (bytes) => {
8588
+ if (bytes.length > 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
8589
+ if (bytes.length > 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return "image/png";
8590
+ if (bytes.length > 6 && bytes.subarray(0, 3).toString("latin1") === "GIF") return "image/gif";
8591
+ if (bytes.length > 12 && bytes.subarray(0, 4).toString("latin1") === "RIFF" && bytes.subarray(8, 12).toString("latin1") === "WEBP") {
8592
+ return "image/webp";
8593
+ }
8594
+ return void 0;
8595
+ };
8596
+ var extensionForMime = (mimeType) => {
8597
+ if (mimeType === "image/jpeg") return "jpg";
8598
+ if (mimeType === "image/webp") return "webp";
8599
+ if (mimeType === "image/gif") return "gif";
8600
+ return "png";
8601
+ };
8602
+ var MAGIC_SNIFF_BASE64_CHARS = 64;
8603
+ var decodeImage = (value) => {
8604
+ if (typeof value !== "string" || value.length === 0) return void 0;
8605
+ let base64 = value;
8606
+ if (value.startsWith("data:")) {
8607
+ const comma = value.indexOf(",");
8608
+ if (comma < 0) return void 0;
8609
+ base64 = value.slice(comma + 1);
8610
+ }
8611
+ const head = Buffer.from(base64.slice(0, MAGIC_SNIFF_BASE64_CHARS), "base64");
8612
+ if (head.length < 16) return void 0;
8613
+ const mimeType = mimeFromMagic(head);
8614
+ if (mimeType === void 0) return void 0;
8615
+ return { data: base64, mimeType };
8616
+ };
8617
+ var collectFromUrlList = (value) => {
8618
+ if (!Array.isArray(value)) return [];
8619
+ const images = [];
8620
+ for (const entry of value) {
8621
+ if (typeof entry === "string") {
8622
+ const decoded2 = decodeImage(entry);
8623
+ if (decoded2) images.push(decoded2);
8624
+ continue;
8625
+ }
8626
+ if (typeof entry !== "object" || entry === null) continue;
8627
+ const record2 = entry;
8628
+ const nested = record2.image_url;
8629
+ const url = typeof nested === "string" ? nested : typeof nested === "object" && nested !== null ? nested.url : record2.url;
8630
+ const decoded = decodeImage(url);
8631
+ if (decoded) images.push(decoded);
8632
+ }
8633
+ return images;
8634
+ };
8635
+ var DATA_URL_PATTERN = /data:(image\/[a-z+]+);base64,([A-Za-z0-9+/=]+)/gi;
8636
+ var parseImagesApiPayload = (payload) => {
8637
+ const data = payload?.data;
8638
+ if (!Array.isArray(data)) return [];
8639
+ const images = [];
8640
+ for (const entry of data) {
8641
+ if (typeof entry !== "object" || entry === null) continue;
8642
+ const record2 = entry;
8643
+ const decoded = decodeImage(record2.b64_json) ?? decodeImage(record2.url);
8644
+ if (decoded) images.push(decoded);
8645
+ }
8646
+ return images;
8647
+ };
8648
+ var parseResponsesPayload = (payload) => {
8649
+ const output = payload?.output;
8650
+ if (!Array.isArray(output)) return [];
8651
+ const images = [];
8652
+ for (const entry of output) {
8653
+ if (typeof entry !== "object" || entry === null) continue;
8654
+ const record2 = entry;
8655
+ if (record2.type !== "image_generation_call") continue;
8656
+ const decoded = decodeImage(record2.result);
8657
+ if (decoded) images.push(decoded);
8658
+ }
8659
+ return images;
8660
+ };
8661
+ var parseChatPayload = (payload) => {
8662
+ const choices = payload?.choices;
8663
+ if (!Array.isArray(choices) || choices.length === 0) return [];
8664
+ const message2 = choices[0]?.message;
8665
+ if (!message2) return [];
8666
+ const direct = collectFromUrlList(message2.images);
8667
+ if (direct.length > 0) return direct;
8668
+ const content = message2.content;
8669
+ const text = typeof content === "string" ? content : Array.isArray(content) ? content.map(
8670
+ (part) => typeof part === "object" && part !== null && typeof part.text === "string" ? String(part.text) : ""
8671
+ ).join("\n") : "";
8672
+ const images = [];
8673
+ for (const match of text.matchAll(DATA_URL_PATTERN)) {
8674
+ const decoded = decodeImage(match[0]);
8675
+ if (decoded) images.push(decoded);
8676
+ }
8677
+ if (images.length > 0) return images;
8678
+ return collectFromUrlList(
8679
+ Array.isArray(content) ? content.filter(
8680
+ (part) => typeof part === "object" && part !== null && part.type === "image_url"
8681
+ ) : []
8682
+ );
8683
+ };
8684
+ var parseGeminiPayload = (payload) => {
8685
+ const candidates = payload?.candidates;
8686
+ if (!Array.isArray(candidates)) return [];
8687
+ const images = [];
8688
+ for (const candidate of candidates) {
8689
+ const parts = candidate?.content?.parts;
8690
+ if (!Array.isArray(parts)) continue;
8691
+ for (const part of parts) {
8692
+ if (typeof part !== "object" || part === null) continue;
8693
+ const inline = part.inlineData ?? part.inline_data;
8694
+ if (typeof inline !== "object" || inline === null) continue;
8695
+ const record2 = inline;
8696
+ const decoded = decodeImage(record2.data);
8697
+ if (decoded) images.push(decoded);
8698
+ }
8699
+ }
8700
+ return images;
8701
+ };
8702
+ var joinUrl = (baseUrl, suffix) => `${baseUrl.replace(/\/+$/, "")}${suffix.startsWith("/") ? suffix : "/" + suffix}`;
8703
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8704
+ var MAX_RESPONSE_BYTES = 96 * 1024 * 1024;
8705
+ var postJson = async (env, url, body, extraHeaders = {}) => {
8706
+ const response = await env.fetchImpl(url, {
8707
+ method: "POST",
8708
+ headers: { ...env.headers, "Content-Type": "application/json", ...extraHeaders },
8709
+ body: JSON.stringify(body),
8710
+ ...env.signal ? { signal: env.signal } : {}
8711
+ });
8712
+ const declared = Number(response.headers.get("content-length") ?? "");
8713
+ if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {
8714
+ throw Object.assign(new Error(`${url} declared ${declared} bytes, over the ${MAX_RESPONSE_BYTES} ceiling`), {
8715
+ status: 413
8716
+ });
8717
+ }
8718
+ const text = await response.text();
8719
+ if (!response.ok) {
8720
+ throw Object.assign(new Error(`${url} returned ${response.status}: ${text.slice(0, 400)}`), {
8721
+ status: response.status
8722
+ });
8723
+ }
8724
+ const contentType = response.headers.get("content-type") ?? "";
8725
+ if (contentType.includes("text/event-stream")) {
8726
+ return { payload: parseSsePayload(text), status: response.status, contentType };
8727
+ }
8728
+ try {
8729
+ return { payload: JSON.parse(text), status: response.status, contentType };
8730
+ } catch {
8731
+ throw new Error(`${url} returned non-JSON: ${text.slice(0, 200)}`);
8732
+ }
8733
+ };
8734
+ var parseSsePayload = (text) => {
8735
+ let last;
8736
+ for (const line of text.split("\n")) {
8737
+ if (!line.startsWith("data:")) continue;
8738
+ const data = line.slice(5).trim();
8739
+ if (data.length === 0 || data === "[DONE]") continue;
8740
+ let event;
8741
+ try {
8742
+ event = JSON.parse(data);
8743
+ } catch {
8744
+ continue;
8745
+ }
8746
+ if (!isRecord3(event)) continue;
8747
+ const nested = event.response;
8748
+ if (isRecord3(nested) && Array.isArray(nested.output)) {
8749
+ last = nested;
8750
+ continue;
8751
+ }
8752
+ if (Array.isArray(event.output)) last = event;
8753
+ }
8754
+ if (last === void 0) throw new Error("event stream carried no response payload");
8755
+ return last;
8756
+ };
8757
+ var dataUrl = (image) => `data:${image.mimeType};base64,${image.data}`;
8758
+ var requestImages = async (env, model, options) => {
8759
+ const openrouter = model.api === "openrouter-images";
8760
+ const body = { model: model.id, prompt: options.prompt, n: options.count };
8761
+ if (!model.gptImage) body.response_format = "b64_json";
8762
+ if (options.size !== void 0) {
8763
+ if (openrouter) body.image_size = options.size;
8764
+ else body.size = options.size;
8765
+ }
8766
+ if (options.aspectRatio !== void 0) body.aspect_ratio = options.aspectRatio;
8767
+ const references = options.inputImages.map((image) => ({ type: "image_url", url: dataUrl(image) }));
8768
+ if (options.inputImages.length === 0) {
8769
+ const url2 = joinUrl(env.baseUrl, openrouter ? "/images" : "/images/generations");
8770
+ const { payload } = await postJson(env, url2, body);
8771
+ return parseImagesApiPayload(payload);
8772
+ }
8773
+ if (model.provider === "openai") {
8774
+ const form = new FormData();
8775
+ form.set("model", model.id);
8776
+ form.set("prompt", options.prompt);
8777
+ form.set("n", String(options.count));
8778
+ if (!model.gptImage) form.set("response_format", "b64_json");
8779
+ if (options.size !== void 0) form.set("size", options.size);
8780
+ for (const image of options.inputImages) {
8781
+ form.append("image", new File([Buffer.from(image.data, "base64")], "image", { type: image.mimeType }));
8782
+ }
8783
+ const response = await env.fetchImpl(joinUrl(env.baseUrl, "/images/edits"), {
8784
+ method: "POST",
8785
+ headers: env.headers,
8786
+ body: form,
8787
+ ...env.signal ? { signal: env.signal } : {}
8788
+ });
8789
+ const text = await response.text();
8790
+ if (!response.ok) {
8791
+ throw Object.assign(new Error(`images edit returned ${response.status}: ${text.slice(0, 400)}`), {
8792
+ status: response.status
8793
+ });
8794
+ }
8795
+ return parseImagesApiPayload(JSON.parse(text));
8796
+ }
8797
+ if (model.xai) {
8798
+ body.image = references[0];
8799
+ if (references.length > 1) body.images = references;
8800
+ } else {
8801
+ body.input_references = references;
8802
+ }
8803
+ const url = joinUrl(env.baseUrl, openrouter ? "/images" : "/images/edits");
8804
+ try {
8805
+ const { payload } = await postJson(env, url, body);
8806
+ return parseImagesApiPayload(payload);
8807
+ } catch (error) {
8808
+ if (error.status !== 404) throw error;
8809
+ const { payload } = await postJson(env, joinUrl(env.baseUrl, "/images/generations"), body);
8810
+ return parseImagesApiPayload(payload);
8811
+ }
8812
+ };
8813
+ var requestResponses = async (env, model, options) => {
8814
+ const content = [{ type: "input_text", text: options.prompt }];
8815
+ for (const image of options.inputImages) {
8816
+ content.push({ type: "input_image", image_url: dataUrl(image) });
8817
+ }
8818
+ const body = {
8819
+ model: model.id,
8820
+ input: [{ role: "user", content }],
8821
+ tools: [{ type: "image_generation" }],
8822
+ tool_choice: { type: "image_generation" },
8823
+ store: false
8824
+ };
8825
+ const url = model.codex ? joinUrl(env.baseUrl, "/codex/responses") : joinUrl(env.baseUrl, "/responses");
8826
+ if (model.codex) body.stream = true;
8827
+ const { payload } = await postJson(env, url, body, model.codex ? { Accept: "text/event-stream" } : {});
8828
+ const status = payload?.status;
8829
+ const images = parseResponsesPayload(payload);
8830
+ if (images.length === 0 && status === "failed") {
8831
+ const error = payload?.error;
8832
+ throw new Error(
8833
+ `responses backend reported status=failed${error ? `: ${JSON.stringify(error).slice(0, 200)}` : ""}`
8834
+ );
8835
+ }
8836
+ return images;
8837
+ };
8838
+ var requestChat = async (env, model, options) => {
8839
+ const content = [{ type: "text", text: options.prompt }];
8840
+ for (const image of options.inputImages) {
8841
+ content.push({ type: "image_url", image_url: { url: `data:${image.mimeType};base64,${image.data}` } });
8842
+ }
8843
+ const body = {
8844
+ model: model.id,
8845
+ messages: [{ role: "user", content }],
8846
+ modalities: ["image", "text"]
8847
+ };
8848
+ const { payload } = await postJson(env, joinUrl(env.baseUrl, "/chat/completions"), body);
8849
+ return parseChatPayload(payload);
8850
+ };
8851
+ var requestGemini = async (env, model, options) => {
8852
+ const parts = [{ text: options.prompt }];
8853
+ for (const image of options.inputImages) {
8854
+ parts.push({ inlineData: { mimeType: image.mimeType, data: image.data } });
8855
+ }
8856
+ const imageConfig = {};
8857
+ if (options.aspectRatio !== void 0) imageConfig.aspectRatio = options.aspectRatio;
8858
+ if (options.size !== void 0) imageConfig.imageSize = options.size;
8859
+ const body = {
8860
+ contents: [{ role: "user", parts }],
8861
+ generationConfig: {
8862
+ responseModalities: ["IMAGE"],
8863
+ candidateCount: 1,
8864
+ ...Object.keys(imageConfig).length > 0 ? { imageConfig } : {}
8865
+ }
8866
+ };
8867
+ const base = env.baseUrl.replace(/\/+$/, "");
8868
+ const root = /\/v1beta\d*$/.test(base) ? base : `${base}/v1beta`;
8869
+ const { payload } = await postJson(env, `${root}/models/${model.id}:generateContent`, body);
8870
+ return parseGeminiPayload(payload);
8871
+ };
8872
+ var geminiHeaders = (apiKey) => ({ "x-goog-api-key": apiKey });
8873
+ var callDialect = (dialect, env, model, options) => {
8874
+ switch (dialect) {
8875
+ case "images":
8876
+ return requestImages(env, model, options);
8877
+ case "responses":
8878
+ return requestResponses(env, model, options);
8879
+ case "chat":
8880
+ return requestChat(env, model, options);
8881
+ case "gemini":
8882
+ return requestGemini(env, model, options);
8883
+ }
8884
+ };
8885
+ var readSourceImage = async (source, cwd) => {
8886
+ if (source.startsWith("data:")) {
8887
+ const decoded = decodeImage(source);
8888
+ if (!decoded) throw new Error(`image source is not a usable data URL: ${source.slice(0, 40)}`);
8889
+ return decoded;
8890
+ }
8891
+ const resolved = path10.isAbsolute(source) ? source : path10.resolve(cwd, source);
8892
+ const bytes = await readFile2(resolved);
8893
+ const mimeType = mimeFromMagic(bytes);
8894
+ if (!mimeType) throw new Error(`unsupported or missing image file: ${resolved}`);
8895
+ return { data: bytes.toString("base64"), mimeType };
8896
+ };
8897
+ var transient = (error) => {
8898
+ const status = error?.status;
8899
+ if (typeof status === "number") return status >= 500 || status === 408 || status === 409 || status === 429;
8900
+ return true;
8901
+ };
8902
+ var describeError = (error) => error instanceof Error ? error.message : String(error);
8903
+ var ImageProvider = class {
8904
+ constructor(config, options = {}) {
8905
+ this.config = config;
8906
+ this.options = options;
8907
+ }
8908
+ name = "image";
8909
+ description = "Image generation and editing across a configurable priority chain of image models";
8910
+ async list(request, _context) {
8911
+ const query = request.query?.toLowerCase();
8912
+ if (!query) return descriptors2;
8913
+ return descriptors2.filter(
8914
+ (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
8915
+ );
8916
+ }
8917
+ async describe(actionName, _context) {
8918
+ return descriptors2.find((descriptor) => descriptor.name === actionName);
8919
+ }
8920
+ async invoke(actionName, args, context) {
8921
+ switch (actionName) {
8922
+ case "generate":
8923
+ return this.run("generate", args, context);
8924
+ case "edit":
8925
+ return this.run("edit", args, context);
8926
+ case "models":
8927
+ return this.chain(context);
8928
+ default:
8929
+ throw new Error(`Unknown image action: ${actionName}`);
8930
+ }
8931
+ }
8932
+ registry(context) {
8933
+ const injected = this.options.registry;
8934
+ if (injected) return injected;
8935
+ const registry = context.extensionContext.modelRegistry;
8936
+ if (!registry) throw new Error("image provider needs a model registry on the extension context");
8937
+ return registry;
8938
+ }
8939
+ sessionId(context) {
8940
+ const injected = this.options.getSessionId;
8941
+ if (injected) return injected();
8942
+ return context.extensionContext?.sessionManager?.getSessionId?.() ?? void 0;
8943
+ }
8944
+ resolveModel(registry, selector) {
8945
+ const parsed = splitModelSelector(selector);
8946
+ if (!parsed) return void 0;
8947
+ const found = registry.find(parsed.provider, parsed.id);
8948
+ if (!isRecord3(found)) return void 0;
8949
+ const api = typeof found.api === "string" ? found.api : "";
8950
+ const baseUrl = typeof found.baseUrl === "string" ? found.baseUrl : "";
8951
+ if (api.length === 0 || baseUrl.length === 0) return void 0;
8952
+ return {
8953
+ provider: parsed.provider,
8954
+ id: parsed.id,
8955
+ api,
8956
+ baseUrl,
8957
+ gptImage: GPT_IMAGE_FAMILY.test(parsed.id),
8958
+ codex: api === "openai-codex-responses",
8959
+ xai: parsed.provider === "xai" || parsed.provider === "xai-oauth" || parsed.provider.startsWith("xai-")
8960
+ };
8961
+ }
8962
+ dialectFor(model) {
8963
+ const override = this.config().dialects[`${model.provider}/${model.id}`];
8964
+ return override ?? inferImageDialect(model.api, model.id);
8965
+ }
8966
+ chainSelectors(args) {
8967
+ const preferred = typeof args.model === "string" ? args.model.trim() : "";
8968
+ const configured = this.config().models;
8969
+ const selectors = preferred.length > 0 ? [preferred, ...configured] : [...configured];
8970
+ const seen = /* @__PURE__ */ new Set();
8971
+ return selectors.filter((selector) => {
8972
+ if (seen.has(selector)) return false;
8973
+ seen.add(selector);
8974
+ return true;
8975
+ });
8976
+ }
8977
+ async chain(context) {
8978
+ const registry = this.registry(context);
8979
+ const sessionId = this.sessionId(context);
8980
+ const entries = [];
8981
+ for (const selector of this.config().models) {
8982
+ const model = this.resolveModel(registry, selector);
8983
+ if (!model) {
8984
+ entries.push({ selector, resolved: false, reason: "no matching model in the registry" });
8985
+ continue;
8986
+ }
8987
+ let credentials = false;
8988
+ try {
8989
+ const apiKey = await registry.getApiKey(model, sessionId);
8990
+ credentials = typeof apiKey === "string" && apiKey.length > 0;
8991
+ } catch {
8992
+ credentials = false;
8993
+ }
8994
+ entries.push({
8995
+ selector,
8996
+ resolved: true,
8997
+ provider: model.provider,
8998
+ id: model.id,
8999
+ api: model.api,
9000
+ baseUrl: model.baseUrl,
9001
+ dialect: this.dialectFor(model),
9002
+ credentials
9003
+ });
9004
+ }
9005
+ return {
9006
+ enabled: this.config().enabled,
9007
+ timeoutMs: this.config().timeoutMs,
9008
+ retries: this.config().retries,
9009
+ outputDir: this.config().outputDir || os.tmpdir(),
9010
+ models: entries
9011
+ };
9012
+ }
9013
+ async run(action, args, context) {
9014
+ const config = this.config();
9015
+ if (!config.enabled) {
9016
+ return { ok: false, reason: "image provider is disabled (image.enabled=false)" };
9017
+ }
9018
+ const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
9019
+ if (prompt.length === 0) return { ok: false, reason: "prompt must be a non-empty string" };
9020
+ const selectors = this.chainSelectors(args);
9021
+ if (selectors.length === 0) {
9022
+ return { ok: false, reason: "no image models configured (image.models is empty)" };
9023
+ }
9024
+ const count = typeof args.count === "number" && Number.isFinite(args.count) && args.count > 0 ? Math.min(Math.floor(args.count), 4) : 1;
9025
+ const aspectRatio = typeof args.aspectRatio === "string" ? args.aspectRatio : void 0;
9026
+ const size = typeof args.size === "string" ? args.size : void 0;
9027
+ const saveTo = typeof args.saveTo === "string" && args.saveTo.trim().length > 0 ? args.saveTo.trim() : void 0;
9028
+ const sources = action === "edit" ? Array.isArray(args.images) ? args.images.filter((entry) => typeof entry === "string") : [] : [];
9029
+ if (action === "edit" && sources.length === 0) {
9030
+ return { ok: false, reason: "edit needs at least one image path or data URL" };
9031
+ }
9032
+ let inputImages = [];
9033
+ try {
9034
+ inputImages = await Promise.all(sources.map((source) => readSourceImage(source, context.cwd)));
9035
+ } catch (error) {
9036
+ return { ok: false, reason: describeError(error) };
9037
+ }
9038
+ const registry = this.registry(context);
9039
+ const sessionId = this.sessionId(context);
9040
+ const fetchImpl = this.options.fetchImpl ?? fetch;
9041
+ const now = this.options.now ?? Date.now;
9042
+ const started = now();
9043
+ const failures = [];
9044
+ const attemptsPerModel = Math.max(1, Math.min(config.retries + 1, 5));
9045
+ for (const selector of selectors) {
9046
+ const model = this.resolveModel(registry, selector);
9047
+ if (!model) {
9048
+ failures.push({ model: selector, dialect: "images", error: "no matching model in the registry" });
9049
+ continue;
9050
+ }
9051
+ const dialect = this.dialectFor(model);
9052
+ let apiKey;
9053
+ let extraHeaders = {};
9054
+ try {
9055
+ apiKey = await registry.getApiKey(model, sessionId);
9056
+ extraHeaders = await registry.resolveModelHeaders?.(model, context.signal) ?? {};
9057
+ } catch (error) {
9058
+ failures.push({ model: selector, dialect, error: `credential lookup failed: ${describeError(error)}` });
9059
+ continue;
9060
+ }
9061
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
9062
+ failures.push({ model: selector, dialect, error: "no credential for this model" });
9063
+ continue;
9064
+ }
9065
+ const authHeaders = model.api === "google-generative-ai" ? geminiHeaders(apiKey) : { Authorization: `Bearer ${apiKey}` };
9066
+ for (let attempt = 1; attempt <= attemptsPerModel; attempt += 1) {
9067
+ context.signal?.throwIfAborted();
9068
+ const signals = [AbortSignal.timeout(config.timeoutMs)];
9069
+ if (context.signal) signals.push(context.signal);
9070
+ const env = {
9071
+ baseUrl: model.baseUrl,
9072
+ apiKey,
9073
+ headers: { ...authHeaders, ...extraHeaders },
9074
+ signal: AbortSignal.any(signals),
9075
+ fetchImpl
9076
+ };
9077
+ try {
9078
+ const images = await callDialect(dialect, env, model, {
9079
+ prompt,
9080
+ aspectRatio,
9081
+ size,
9082
+ count,
9083
+ inputImages,
9084
+ signal: env.signal
9085
+ });
9086
+ if (images.length === 0) throw new Error("backend returned no image data");
9087
+ let written;
9088
+ try {
9089
+ written = await this.write(images, saveTo, context.cwd);
9090
+ } catch (error) {
9091
+ return {
9092
+ ok: false,
9093
+ reason: `${model.provider}/${model.id} returned ${images.length} image(s) but the write failed: ${describeError(error)}`,
9094
+ model: `${model.provider}/${model.id}`,
9095
+ dialect,
9096
+ failures,
9097
+ elapsedMs: Math.max(0, now() - started)
9098
+ };
9099
+ }
9100
+ context.attachMedia?.(
9101
+ written.map((entry) => ({ type: "image", data: entry.data, mimeType: entry.mimeType })),
9102
+ `Generated ${written.length} image(s) with ${model.provider}/${model.id} (${dialect}):
9103
+ ${written.map((entry) => ` ${entry.path} (${entry.bytes} bytes)`).join("\n")}`
9104
+ );
9105
+ return {
9106
+ ok: true,
9107
+ model: `${model.provider}/${model.id}`,
9108
+ dialect,
9109
+ images: written.map((entry) => ({ path: entry.path, bytes: entry.bytes, mimeType: entry.mimeType })),
9110
+ attempts: attempt,
9111
+ elapsedMs: Math.max(0, now() - started),
9112
+ failures
9113
+ };
9114
+ } catch (error) {
9115
+ const status = error?.status;
9116
+ failures.push({
9117
+ model: `${model.provider}/${model.id}`,
9118
+ dialect,
9119
+ error: describeError(error),
9120
+ ...typeof status === "number" ? { status } : {}
9121
+ });
9122
+ if (context.signal?.aborted) throw error;
9123
+ if (attempt < attemptsPerModel && transient(error)) continue;
9124
+ break;
9125
+ }
9126
+ }
9127
+ }
9128
+ return {
9129
+ ok: false,
9130
+ reason: `every configured image model failed (${failures.length} attempt${failures.length === 1 ? "" : "s"})`,
9131
+ failures,
9132
+ elapsedMs: Math.max(0, now() - started)
9133
+ };
9134
+ }
9135
+ async write(images, saveTo, cwd) {
9136
+ const configured = this.config().outputDir;
9137
+ const directory = configured.length > 0 ? path10.isAbsolute(configured) ? configured : path10.resolve(cwd, configured) : os.tmpdir();
9138
+ await mkdir(directory, { recursive: true, mode: 448 });
9139
+ const written = [];
9140
+ for (const [index, image] of images.entries()) {
9141
+ const extension = extensionForMime(image.mimeType);
9142
+ const target = saveTo !== void 0 && images.length === 1 ? path10.isAbsolute(saveTo) ? saveTo : path10.resolve(cwd, saveTo) : path10.join(directory, `fabric-image-${Date.now()}-${index}.${extension}`);
9143
+ await mkdir(path10.dirname(target), { recursive: true, mode: 448 });
9144
+ const bytes = Buffer.from(image.data, "base64");
9145
+ await writeFile(target, bytes, { mode: 384 });
9146
+ written.push({ path: target, bytes: bytes.length, mimeType: image.mimeType, data: image.data });
9147
+ }
9148
+ return written;
9149
+ }
9150
+ };
9151
+
8501
9152
  // src/providers/judgment-provider.ts
8502
9153
  import { Type as Type2 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
8503
9154
  import { validateJsonSchemaValue as validateJsonSchemaValue2 } from "@oh-my-pi/pi-ai/utils/schema";
@@ -8510,7 +9161,7 @@ var askSchema = Type2.Object({
8510
9161
  })
8511
9162
  }, { additionalProperties: false });
8512
9163
  var statsSchema = Type2.Object({}, { additionalProperties: false });
8513
- var descriptors2 = [
9164
+ var descriptors3 = [
8514
9165
  {
8515
9166
  name: "ask",
8516
9167
  description: "Answer typed questions about one state with calibrated probabilities: choice (one label plus its distribution), bool (probability of yes), score (weighted position on ordered levels). Questions asked about the same state within the coalesce window travel as one backend request. Returns { ok: false, reason } instead of throwing when no backend is configured, the budget refuses, or the request fails.",
@@ -8524,8 +9175,8 @@ var descriptors2 = [
8524
9175
  risk: "read"
8525
9176
  }
8526
9177
  ];
8527
- var normalizeJudgmentArgs = actionArgNormalizer(() => descriptors2);
8528
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9178
+ var normalizeJudgmentArgs = actionArgNormalizer(() => descriptors3);
9179
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8529
9180
  var instructionsOf = (id, value) => {
8530
9181
  if (typeof value !== "string" || value.length === 0) {
8531
9182
  throw new Error(`judgment.ask question "${id}" needs non-empty string instructions`);
@@ -8534,7 +9185,7 @@ var instructionsOf = (id, value) => {
8534
9185
  };
8535
9186
  var parseChoice = (id, value) => {
8536
9187
  const instructions = instructionsOf(id, value.instructions);
8537
- if (!isRecord3(value.criteria)) {
9188
+ if (!isRecord4(value.criteria)) {
8538
9189
  throw new Error(`judgment.ask choice question "${id}" needs criteria: { label: rubric | null }`);
8539
9190
  }
8540
9191
  const criteria = {};
@@ -8553,7 +9204,7 @@ var parseChoice = (id, value) => {
8553
9204
  var parseBool = (id, value) => {
8554
9205
  const instructions = instructionsOf(id, value.instructions);
8555
9206
  if (value.criteria === void 0) return { type: "noul", instructions };
8556
- if (!isRecord3(value.criteria)) {
9207
+ if (!isRecord4(value.criteria)) {
8557
9208
  throw new Error(`judgment.ask bool question "${id}" criteria must be { true?: string, false?: string }`);
8558
9209
  }
8559
9210
  const criteria = {};
@@ -8580,7 +9231,7 @@ var parseScore = (id, value) => {
8580
9231
  return { type: "score", instructions, criteria: [lowest, second, ...rest] };
8581
9232
  };
8582
9233
  var parseQuestion = (id, value) => {
8583
- if (!isRecord3(value)) throw new Error(`judgment.ask question "${id}" must be an object`);
9234
+ if (!isRecord4(value)) throw new Error(`judgment.ask question "${id}" must be an object`);
8584
9235
  switch (value.type) {
8585
9236
  case "choice":
8586
9237
  return parseChoice(id, value);
@@ -8597,7 +9248,7 @@ var parseState = (value) => {
8597
9248
  if (value.length === 0) throw new Error("judgment.ask state must not be empty");
8598
9249
  return value;
8599
9250
  }
8600
- if (Array.isArray(value) || isRecord3(value)) return value;
9251
+ if (Array.isArray(value) || isRecord4(value)) return value;
8601
9252
  throw new Error("judgment.ask state must be a string, a JSON object, or a JSON array");
8602
9253
  };
8603
9254
  var checked2 = (action, schema, args) => {
@@ -8616,12 +9267,12 @@ var JudgmentProvider = class {
8616
9267
  description = "Typed judgments over one state: calibrated choice, bool, and score answers batched into one backend request";
8617
9268
  async list(request, _context) {
8618
9269
  const query = request.query?.toLowerCase();
8619
- return query ? descriptors2.filter(
9270
+ return query ? descriptors3.filter(
8620
9271
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
8621
- ) : descriptors2;
9272
+ ) : descriptors3;
8622
9273
  }
8623
9274
  async describe(actionName, _context) {
8624
- return descriptors2.find((descriptor) => descriptor.name === actionName);
9275
+ return descriptors3.find((descriptor) => descriptor.name === actionName);
8625
9276
  }
8626
9277
  prepareArguments(actionName, args) {
8627
9278
  return normalizeJudgmentArgs(actionName, args);
@@ -8933,7 +9584,7 @@ var emptySchema = {
8933
9584
  properties: {},
8934
9585
  additionalProperties: false
8935
9586
  };
8936
- var descriptors3 = [
9587
+ var descriptors4 = [
8937
9588
  {
8938
9589
  name: "request",
8939
9590
  description: "Request an advisory compaction of the host session's context at the next safe boundary (agent_end). The host commits it only between turns, never mid-turn. A new request replaces any pending one.",
@@ -8956,7 +9607,7 @@ var descriptors3 = [
8956
9607
  risk: "write"
8957
9608
  }
8958
9609
  ];
8959
- var normalizeCompactArgs = actionArgNormalizer(() => descriptors3);
9610
+ var normalizeCompactArgs = actionArgNormalizer(() => descriptors4);
8960
9611
  var CompactProvider = class {
8961
9612
  constructor(controller) {
8962
9613
  this.controller = controller;
@@ -8965,12 +9616,12 @@ var CompactProvider = class {
8965
9616
  description = "Programmatic, advisory-then-committed context compaction for the host OMP session";
8966
9617
  async list(request, _context) {
8967
9618
  const query = request.query?.toLowerCase();
8968
- return query ? descriptors3.filter(
9619
+ return query ? descriptors4.filter(
8969
9620
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
8970
- ) : descriptors3;
9621
+ ) : descriptors4;
8971
9622
  }
8972
9623
  async describe(actionName, _context) {
8973
- return descriptors3.find((descriptor) => descriptor.name === actionName);
9624
+ return descriptors4.find((descriptor) => descriptor.name === actionName);
8974
9625
  }
8975
9626
  prepareArguments(actionName, args) {
8976
9627
  return normalizeCompactArgs(actionName, args);
@@ -9010,7 +9661,7 @@ var CompactProvider = class {
9010
9661
  };
9011
9662
 
9012
9663
  // src/providers/components-provider.ts
9013
- var descriptors4 = [
9664
+ var descriptors5 = [
9014
9665
  {
9015
9666
  name: "list",
9016
9667
  description: "List registered component definitions and configured component instances.",
@@ -9057,13 +9708,13 @@ var ComponentsProvider = class {
9057
9708
  description = "Supervised component lifecycle, exact capability dependencies, effect cleanup, and reload diagnostics.";
9058
9709
  async list(request, _context) {
9059
9710
  const query = request.query?.normalize("NFKC").trim().toLowerCase();
9060
- const filtered = query ? descriptors4.filter(
9711
+ const filtered = query ? descriptors5.filter(
9061
9712
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
9062
- ) : descriptors4;
9713
+ ) : descriptors5;
9063
9714
  return filtered.slice(0, Math.max(1, Math.min(request.limit ?? 100, 100)));
9064
9715
  }
9065
9716
  async describe(actionName, _context) {
9066
- return descriptors4.find((descriptor) => descriptor.name === actionName);
9717
+ return descriptors5.find((descriptor) => descriptor.name === actionName);
9067
9718
  }
9068
9719
  async invoke(actionName, args, _context) {
9069
9720
  switch (actionName) {
@@ -9097,37 +9748,37 @@ var ComponentsProvider = class {
9097
9748
  // src/providers/mcp-descriptor-cache.ts
9098
9749
  import fs5 from "node:fs/promises";
9099
9750
  import fsSync from "node:fs";
9100
- import os from "node:os";
9101
- import path10 from "node:path";
9751
+ import os2 from "node:os";
9752
+ import path11 from "node:path";
9102
9753
  var MCP_DESCRIPTOR_CACHE_VERSION = 1;
9103
9754
  var homeDir = () => {
9104
9755
  const home = process.env.HOME;
9105
- return home && home.trim().length > 0 ? home : os.homedir();
9756
+ return home && home.trim().length > 0 ? home : os2.homedir();
9106
9757
  };
9107
9758
  var expandHome = (input) => {
9108
9759
  if (!input.startsWith("~")) return input;
9109
9760
  const home = homeDir();
9110
9761
  if (input === "~") return home;
9111
9762
  if (input.startsWith("~/") || input.startsWith("~\\")) {
9112
- return path10.join(home, input.slice(2));
9763
+ return path11.join(home, input.slice(2));
9113
9764
  }
9114
9765
  return input;
9115
9766
  };
9116
- var legacyMcporterDir = () => path10.join(homeDir(), ".mcporter");
9767
+ var legacyMcporterDir = () => path11.join(homeDir(), ".mcporter");
9117
9768
  var mcporterConfigDir = () => {
9118
9769
  const raw = process.env.XDG_CONFIG_HOME;
9119
9770
  if (raw && raw.trim().length > 0) {
9120
9771
  const resolved = expandHome(raw.trim());
9121
- if (path10.isAbsolute(resolved)) return path10.join(resolved, "mcporter");
9772
+ if (path11.isAbsolute(resolved)) return path11.join(resolved, "mcporter");
9122
9773
  }
9123
9774
  return legacyMcporterDir();
9124
9775
  };
9125
9776
  var mcporterConfigCandidates = () => {
9126
9777
  const base = mcporterConfigDir();
9127
- const candidates = [path10.join(base, "mcporter.json"), path10.join(base, "mcporter.jsonc")];
9778
+ const candidates = [path11.join(base, "mcporter.json"), path11.join(base, "mcporter.jsonc")];
9128
9779
  const legacy = legacyMcporterDir();
9129
9780
  if (base !== legacy) {
9130
- candidates.push(path10.join(legacy, "mcporter.json"), path10.join(legacy, "mcporter.jsonc"));
9781
+ candidates.push(path11.join(legacy, "mcporter.json"), path11.join(legacy, "mcporter.jsonc"));
9131
9782
  }
9132
9783
  return candidates;
9133
9784
  };
@@ -9142,12 +9793,12 @@ var pathExists = (filePath) => {
9142
9793
  var mcpConfigLayerPaths = (rootDir, configPath) => {
9143
9794
  const explicitRaw = configPath ?? process.env.MCPORTER_CONFIG;
9144
9795
  if (explicitRaw && explicitRaw.trim().length > 0) {
9145
- return [path10.resolve(expandHome(explicitRaw.trim()))];
9796
+ return [path11.resolve(expandHome(explicitRaw.trim()))];
9146
9797
  }
9147
9798
  const paths = [];
9148
9799
  const home = mcporterConfigCandidates().find(pathExists);
9149
9800
  if (home) paths.push(home);
9150
- const projectPath = path10.resolve(rootDir, "config", "mcporter.json");
9801
+ const projectPath = path11.resolve(rootDir, "config", "mcporter.json");
9151
9802
  if (pathExists(projectPath)) paths.push(projectPath);
9152
9803
  return paths;
9153
9804
  };
@@ -9169,11 +9820,11 @@ var sameConfigLayers = (left, right) => left.length === right.length && left.eve
9169
9820
  return other !== void 0 && layer.path === other.path && layer.size === other.size && Math.abs(layer.mtimeMs - other.mtimeMs) <= 1;
9170
9821
  });
9171
9822
  var hashServerDefinition = (definition) => stableJsonHash(definition);
9172
- var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9823
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9173
9824
  var parseCachedServer = (value) => {
9174
- if (!isRecord4(value)) return void 0;
9825
+ if (!isRecord5(value)) return void 0;
9175
9826
  const tools = Array.isArray(value.tools) ? value.tools.filter(
9176
- (tool) => isRecord4(tool) && typeof tool.name === "string"
9827
+ (tool) => isRecord5(tool) && typeof tool.name === "string"
9177
9828
  ) : void 0;
9178
9829
  if (typeof value.definitionHash !== "string" || tools === void 0) return void 0;
9179
9830
  return {
@@ -9186,10 +9837,10 @@ var parseCachedServer = (value) => {
9186
9837
  };
9187
9838
  };
9188
9839
  var parseCacheFile = (value) => {
9189
- if (!isRecord4(value) || value.version !== MCP_DESCRIPTOR_CACHE_VERSION) return void 0;
9190
- if (!Array.isArray(value.layers) || !isRecord4(value.servers)) return void 0;
9840
+ if (!isRecord5(value) || value.version !== MCP_DESCRIPTOR_CACHE_VERSION) return void 0;
9841
+ if (!Array.isArray(value.layers) || !isRecord5(value.servers)) return void 0;
9191
9842
  const layers = value.layers.filter(
9192
- (layer) => isRecord4(layer) && typeof layer.path === "string" && typeof layer.mtimeMs === "number" && typeof layer.size === "number"
9843
+ (layer) => isRecord5(layer) && typeof layer.path === "string" && typeof layer.mtimeMs === "number" && typeof layer.size === "number"
9193
9844
  );
9194
9845
  return {
9195
9846
  version: MCP_DESCRIPTOR_CACHE_VERSION,
@@ -9198,8 +9849,8 @@ var parseCacheFile = (value) => {
9198
9849
  servers: value.servers
9199
9850
  };
9200
9851
  };
9201
- var projectMcpCachePath = (projectRoot) => path10.join(projectRoot, ".omp", "fabric", "mcp-cache.json");
9202
- var globalMcpCachePath = (agentDir) => path10.join(agentDir, "fabric", "mcp-descriptors.json");
9852
+ var projectMcpCachePath = (projectRoot) => path11.join(projectRoot, ".omp", "fabric", "mcp-cache.json");
9853
+ var globalMcpCachePath = (agentDir) => path11.join(agentDir, "fabric", "mcp-descriptors.json");
9203
9854
  var tempCounter = 0;
9204
9855
  var McpDescriptorCacheStore = class {
9205
9856
  constructor(filePath) {
@@ -9216,9 +9867,9 @@ var McpDescriptorCacheStore = class {
9216
9867
  // Atomic-ish write: temp file then rename, so a concurrent reader either
9217
9868
  // sees the previous cache or the next one, never a torn document.
9218
9869
  async save(file) {
9219
- const directory = path10.dirname(this.filePath);
9870
+ const directory = path11.dirname(this.filePath);
9220
9871
  await fs5.mkdir(directory, { recursive: true });
9221
- const tempPath = path10.join(
9872
+ const tempPath = path11.join(
9222
9873
  directory,
9223
9874
  `.mcp-cache-${process.pid}-${Date.now()}-${tempCounter++}.tmp`
9224
9875
  );
@@ -9233,7 +9884,7 @@ var McpDescriptorCacheStore = class {
9233
9884
  };
9234
9885
 
9235
9886
  // src/providers/mcp-provider.ts
9236
- import path11 from "node:path";
9887
+ import path12 from "node:path";
9237
9888
  var TOOL_METADATA_TTL_MS = 6e4;
9238
9889
  var REVALIDATE_CONCURRENCY = 3;
9239
9890
  var REVALIDATE_SERVER_TIMEOUT_MS = 2e4;
@@ -9394,9 +10045,9 @@ var McpProvider = class {
9394
10045
  this.#kickRevalidation();
9395
10046
  if (!request.namespace) await this.#awaitColdEnumeration(context.signal);
9396
10047
  const query = request.query?.toLowerCase();
9397
- const filterQuery = (descriptors8) => query ? descriptors8.filter(
10048
+ const filterQuery = (descriptors9) => query ? descriptors9.filter(
9398
10049
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
9399
- ) : descriptors8;
10050
+ ) : descriptors9;
9400
10051
  if (request.namespace) {
9401
10052
  const server = await this.#resolveKnownServer(request.namespace);
9402
10053
  if (!server) return [];
@@ -9551,11 +10202,11 @@ var McpProvider = class {
9551
10202
  // Provider-fidelity descriptors for everything currently known, cached or
9552
10203
  // ephemeral.
9553
10204
  sliceDescriptors() {
9554
- const descriptors8 = [];
10205
+ const descriptors9 = [];
9555
10206
  for (const [server, entry] of this.#servers) {
9556
- for (const tool of entry.tools) descriptors8.push(this.#toolDescriptor(server, tool));
10207
+ for (const tool of entry.tools) descriptors9.push(this.#toolDescriptor(server, tool));
9557
10208
  }
9558
- return descriptors8;
10209
+ return descriptors9;
9559
10210
  }
9560
10211
  // Test/ops hook: await hydration and any in-flight background revalidation,
9561
10212
  // then flush pending persistence and notifications.
@@ -9957,7 +10608,7 @@ var McpProvider = class {
9957
10608
  kind: "stdio",
9958
10609
  command: args.command,
9959
10610
  args: commandArgs,
9960
- cwd: path11.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
10611
+ cwd: path12.resolve(this.cwd, typeof args.cwd === "string" ? args.cwd : ".")
9961
10612
  },
9962
10613
  ...env ? { env } : {}
9963
10614
  };
@@ -10022,13 +10673,13 @@ var McpProvider = class {
10022
10673
  return tools.map((tool) => this.#toolDescriptor(server, tool));
10023
10674
  })
10024
10675
  );
10025
- const descriptors8 = settled.flatMap(
10676
+ const descriptors9 = settled.flatMap(
10026
10677
  (entry) => entry.status === "fulfilled" ? entry.value : []
10027
10678
  );
10028
10679
  const query = request.query?.toLowerCase();
10029
- const filtered = query ? descriptors8.filter(
10680
+ const filtered = query ? descriptors9.filter(
10030
10681
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
10031
- ) : descriptors8;
10682
+ ) : descriptors9;
10032
10683
  return request.namespace ? filtered : [...managementDescriptors, ...filtered];
10033
10684
  }
10034
10685
  async #describeLegacy(actionName, _context) {
@@ -10100,7 +10751,7 @@ var assertReadableStateKey = (key) => {
10100
10751
  throw new Error(`Fabric mesh key is private host state: ${key}`);
10101
10752
  }
10102
10753
  };
10103
- var descriptors5 = [
10754
+ var descriptors6 = [
10104
10755
  {
10105
10756
  name: "self",
10106
10757
  description: "Return this Fabric participant's mesh identity",
@@ -10227,7 +10878,7 @@ var descriptors5 = [
10227
10878
  namespace: "coordination"
10228
10879
  }
10229
10880
  ];
10230
- var normalizeMeshArgs = actionArgNormalizer(() => descriptors5);
10881
+ var normalizeMeshArgs = actionArgNormalizer(() => descriptors6);
10231
10882
  var MeshProvider = class {
10232
10883
  constructor(store, identity, participants) {
10233
10884
  this.store = store;
@@ -10238,12 +10889,12 @@ var MeshProvider = class {
10238
10889
  description = "Durable topics and compare-and-swap shared state for emergent agent coordination";
10239
10890
  async list(request, _context) {
10240
10891
  const query = request.query?.toLowerCase();
10241
- return query ? descriptors5.filter(
10892
+ return query ? descriptors6.filter(
10242
10893
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
10243
- ) : descriptors5;
10894
+ ) : descriptors6;
10244
10895
  }
10245
10896
  async describe(actionName, _context) {
10246
- return descriptors5.find((descriptor) => descriptor.name === actionName);
10897
+ return descriptors6.find((descriptor) => descriptor.name === actionName);
10247
10898
  }
10248
10899
  prepareArguments(actionName, args) {
10249
10900
  return normalizeMeshArgs(actionName, args);
@@ -10337,7 +10988,7 @@ var MeshProvider = class {
10337
10988
  // src/providers/omp-tools-provider.ts
10338
10989
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
10339
10990
  import { AsyncLocalStorage } from "node:async_hooks";
10340
- import path14 from "node:path";
10991
+ import path15 from "node:path";
10341
10992
  import { randomUUID as randomUUID3 } from "node:crypto";
10342
10993
  import { BashTool } from "@oh-my-pi/pi-coding-agent/tools/bash";
10343
10994
  import { EditTool } from "@oh-my-pi/pi-coding-agent/edit";
@@ -10356,7 +11007,7 @@ import {
10356
11007
 
10357
11008
  // src/agents/bash-worktree-add.ts
10358
11009
  import fs6 from "node:fs";
10359
- import path12 from "node:path";
11010
+ import path13 from "node:path";
10360
11011
  var BARE_SAFE = /^[A-Za-z0-9_./:@%+=,-]+$/;
10361
11012
  var parseGitWorktreeAdd = (command) => {
10362
11013
  const trimmed = command.trim();
@@ -10419,8 +11070,8 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
10419
11070
  if (typeof command !== "string") return void 0;
10420
11071
  const parsed = parseGitWorktreeAdd(command);
10421
11072
  if (!parsed) return void 0;
10422
- const gitCwd = parsed.gitCwd ? path12.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
10423
- const dest = path12.resolve(gitCwd, parsed.dest);
11073
+ const gitCwd = parsed.gitCwd ? path13.resolve(sessionCwd, parsed.gitCwd) : typeof args.cwd === "string" ? args.cwd : sessionCwd;
11074
+ const dest = path13.resolve(gitCwd, parsed.dest);
10424
11075
  let gitRoot;
10425
11076
  try {
10426
11077
  const root = await executeFile("git", ["rev-parse", "--show-toplevel"], {
@@ -10449,7 +11100,7 @@ var tryExecuteGitWorktreeAdd = async (args, sessionCwd) => {
10449
11100
 
10450
11101
  // src/providers/omp-bash-cwd.ts
10451
11102
  import { accessSync, constants, statSync as statSync3 } from "node:fs";
10452
- import path13 from "node:path";
11103
+ import path14 from "node:path";
10453
11104
  import { createBashToolDefinition } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-pi-coding-agent-shim";
10454
11105
  import { Type as Type4 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
10455
11106
  var OMP_BASH_CWD_KEY = "cwd";
@@ -10459,7 +11110,7 @@ var resolveOmpShellCwd = (toolName, sessionCwd, requested) => {
10459
11110
  `Invalid omp.${toolName} cwd ${JSON.stringify(requested)}: path must be a non-empty string`
10460
11111
  );
10461
11112
  }
10462
- const resolved = path13.isAbsolute(requested) ? path13.resolve(requested) : path13.resolve(sessionCwd, requested);
11113
+ const resolved = path14.isAbsolute(requested) ? path14.resolve(requested) : path14.resolve(sessionCwd, requested);
10463
11114
  try {
10464
11115
  if (!statSync3(resolved).isDirectory()) throw new Error("path is not a directory");
10465
11116
  accessSync(resolved, constants.R_OK | constants.X_OK);
@@ -10481,23 +11132,23 @@ var resolveShellCwdArgument = (toolName, sessionCwd, args) => Object.hasOwn(args
10481
11132
  } : args;
10482
11133
  var CWD_DESCRIPTION = "Execution directory for this command; relative paths resolve from the session cwd.";
10483
11134
  var CWD_PROPERTY = Type4.Optional(Type4.String({ description: CWD_DESCRIPTION }));
10484
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
11135
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10485
11136
  var SHELL_FIELD_DESCRIPTIONS = {
10486
11137
  command: "command to execute",
10487
11138
  [OMP_BASH_CWD_KEY]: CWD_DESCRIPTION,
10488
11139
  pty: "run in pty mode"
10489
11140
  };
10490
11141
  var schemaDocument2 = (schema) => {
10491
- if (isRecord5(schema)) return schema;
11142
+ if (isRecord6(schema)) return schema;
10492
11143
  if (typeof schema !== "function" || !("toJsonSchema" in schema)) return void 0;
10493
11144
  const toJsonSchema = schema.toJsonSchema;
10494
11145
  if (typeof toJsonSchema !== "function") return void 0;
10495
11146
  const document = toJsonSchema.call(schema);
10496
- return isRecord5(document) ? document : void 0;
11147
+ return isRecord6(document) ? document : void 0;
10497
11148
  };
10498
11149
  var withShellCwdSchema = (schema) => {
10499
11150
  const document = schemaDocument2(schema);
10500
- if (!document || !isRecord5(document.properties)) return schema;
11151
+ if (!document || !isRecord6(document.properties)) return schema;
10501
11152
  const properties = { ...document.properties };
10502
11153
  let changed = false;
10503
11154
  const cwdSchema = Object.hasOwn(properties, OMP_BASH_CWD_KEY) ? void 0 : schemaDocument2(CWD_PROPERTY);
@@ -10507,7 +11158,7 @@ var withShellCwdSchema = (schema) => {
10507
11158
  }
10508
11159
  for (const [field, description] of Object.entries(SHELL_FIELD_DESCRIPTIONS)) {
10509
11160
  const property = properties[field];
10510
- if (!isRecord5(property) || typeof property.description === "string") continue;
11161
+ if (!isRecord6(property) || typeof property.description === "string") continue;
10511
11162
  properties[field] = { ...property, description };
10512
11163
  changed = true;
10513
11164
  }
@@ -10544,23 +11195,23 @@ var BashCwdDefinitions = class extends ShellCwdDefinitions {
10544
11195
  // src/providers/write-preview.ts
10545
11196
  import { homedir } from "node:os";
10546
11197
  import { dirname, isAbsolute, resolve, win32 } from "node:path";
10547
- import { mkdir, readFile as readFile2, stat, writeFile } from "node:fs/promises";
11198
+ import { mkdir as mkdir2, readFile as readFile3, stat, writeFile as writeFile2 } from "node:fs/promises";
10548
11199
  import { Type as Type5 } from "@oh-my-pi/pi-coding-agent/extensibility/legacy-typebox";
10549
11200
  import { WriteTool } from "@oh-my-pi/pi-coding-agent/tools/write";
10550
11201
  var mutationQueues = /* @__PURE__ */ new Map();
10551
- var withFileMutationQueue = async (path20, operation) => {
10552
- const previous = mutationQueues.get(path20) ?? Promise.resolve();
11202
+ var withFileMutationQueue = async (path21, operation) => {
11203
+ const previous = mutationQueues.get(path21) ?? Promise.resolve();
10553
11204
  let release;
10554
11205
  const current = new Promise((resolve2) => {
10555
11206
  release = resolve2;
10556
11207
  });
10557
- mutationQueues.set(path20, current);
11208
+ mutationQueues.set(path21, current);
10558
11209
  await previous;
10559
11210
  try {
10560
11211
  return await operation();
10561
11212
  } finally {
10562
11213
  release();
10563
- if (mutationQueues.get(path20) === current) mutationQueues.delete(path20);
11214
+ if (mutationQueues.get(path21) === current) mutationQueues.delete(path21);
10564
11215
  }
10565
11216
  };
10566
11217
  var URI_LIKE_WRITE_TARGET_RE = /^([a-z][a-z0-9+.-]*):\/{1,2}/i;
@@ -10605,7 +11256,7 @@ var readExistingFileForPreview = async (filePath, cwd, nextContent) => {
10605
11256
  return skipped("previous file too large", fileStat.size, true);
10606
11257
  }
10607
11258
  try {
10608
- const content = await readFile2(absolutePath, "utf8");
11259
+ const content = await readFile3(absolutePath, "utf8");
10609
11260
  const bytes = Buffer.byteLength(content, "utf8");
10610
11261
  return bytes > MAX_WRITE_DIFF_BYTES ? skipped("previous file too large", bytes, true) : { kind: "content", content };
10611
11262
  } catch {
@@ -10623,11 +11274,11 @@ var createPreviewWriteToolDefinition = (cwd, session) => {
10623
11274
  return {
10624
11275
  ...original,
10625
11276
  async execute(_toolCallId, params, signal) {
10626
- const { path: path20, content } = params;
10627
- if (isUriLikeWriteTarget(path20)) {
11277
+ const { path: path21, content } = params;
11278
+ if (isUriLikeWriteTarget(path21)) {
10628
11279
  if (session === void 0) {
10629
11280
  throw new Error(
10630
- `Refusing to write '${path20}': URI-like targets need a host session to resolve. Prefix the path with './' to create a literal file by that name.`
11281
+ `Refusing to write '${path21}': URI-like targets need a host session to resolve. Prefix the path with './' to create a literal file by that name.`
10631
11282
  );
10632
11283
  }
10633
11284
  return await new WriteTool(session).execute(
@@ -10639,23 +11290,23 @@ var createPreviewWriteToolDefinition = (cwd, session) => {
10639
11290
  { signal }
10640
11291
  );
10641
11292
  }
10642
- const absolutePath = resolvePreviewPath(path20, cwd);
11293
+ const absolutePath = resolvePreviewPath(path21, cwd);
10643
11294
  return withFileMutationQueue(absolutePath, async () => {
10644
11295
  const throwIfAborted2 = () => {
10645
11296
  if (signal?.aborted) throw new Error("Operation aborted");
10646
11297
  };
10647
11298
  throwIfAborted2();
10648
- const before = await readExistingFileForPreview(path20, cwd, content);
11299
+ const before = await readExistingFileForPreview(path21, cwd, content);
10649
11300
  throwIfAborted2();
10650
- await mkdir(dirname(absolutePath), { recursive: true });
11301
+ await mkdir2(dirname(absolutePath), { recursive: true });
10651
11302
  throwIfAborted2();
10652
- await writeFile(absolutePath, content, "utf8");
11303
+ await writeFile2(absolutePath, content, "utf8");
10653
11304
  throwIfAborted2();
10654
11305
  return {
10655
11306
  content: [
10656
11307
  {
10657
11308
  type: "text",
10658
- text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path20}`
11309
+ text: `Successfully wrote ${Buffer.byteLength(content, "utf8")} bytes to ${path21}`
10659
11310
  }
10660
11311
  ],
10661
11312
  details: { codePreviewBeforeWrite: before }
@@ -10782,7 +11433,7 @@ var createNativeSession = (cwd, {
10782
11433
  try {
10783
11434
  mkdirSync2(artifacts.root, { recursive: true, mode: 448 });
10784
11435
  const id = randomUUID3();
10785
- const artifactPath = path14.join(artifacts.root, `${toolType}-${id}.log`);
11436
+ const artifactPath = path15.join(artifacts.root, `${toolType}-${id}.log`);
10786
11437
  writeFileSync2(artifactPath, "", { mode: 384 });
10787
11438
  artifacts.paths.set(id, artifactPath);
10788
11439
  artifactAllocations.getStore()?.push(artifactPath);
@@ -10950,7 +11601,7 @@ var normalizeEditArguments = (args, replaceAll, cwd) => {
10950
11601
  ...replaceAll || record2.all === true || record2.replace_all === true ? { replace_all: true } : {}
10951
11602
  };
10952
11603
  });
10953
- const resolvedPath = path14.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
11604
+ const resolvedPath = path15.resolve(cwd, filePath.startsWith("@") ? filePath.slice(1) : filePath);
10954
11605
  const current = readFileSync2(resolvedPath, "utf8");
10955
11606
  if (current.length > MAX_REPLACE_ALL_FILE_CHARS) {
10956
11607
  throw new Error(`omp.edit refuses files over ${MAX_REPLACE_ALL_FILE_CHARS} characters`);
@@ -11425,10 +12076,10 @@ var OmpToolsProvider = class _OmpToolsProvider {
11425
12076
  }
11426
12077
  async list(request, _context) {
11427
12078
  const query = request.query?.toLowerCase();
11428
- const descriptors8 = await Promise.all(
12079
+ const descriptors9 = await Promise.all(
11429
12080
  OMP_GUEST_TOOL_NAMES.map((name) => this.describe(name, _context))
11430
12081
  );
11431
- return descriptors8.filter((descriptor) => descriptor !== void 0).filter(
12082
+ return descriptors9.filter((descriptor) => descriptor !== void 0).filter(
11432
12083
  (descriptor) => query ? `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query) : true
11433
12084
  );
11434
12085
  }
@@ -11834,7 +12485,7 @@ var OmpToolsProvider = class _OmpToolsProvider {
11834
12485
  import { spawn } from "node:child_process";
11835
12486
  import { createHash as createHash3, randomBytes, randomUUID as randomUUID4 } from "node:crypto";
11836
12487
  import fs8 from "node:fs";
11837
- import path16 from "node:path";
12488
+ import path17 from "node:path";
11838
12489
 
11839
12490
  // src/schema/types.ts
11840
12491
  var stateBinding = (head) => head ? { transitionId: head.transitionId, version: head.version, to: head.to } : null;
@@ -11843,7 +12494,7 @@ var stateBinding = (head) => head ? { transitionId: head.transitionId, version:
11843
12494
  import { execFileSync } from "node:child_process";
11844
12495
  import { createHash as createHash2 } from "node:crypto";
11845
12496
  import fs7 from "node:fs";
11846
- import path15 from "node:path";
12497
+ import path16 from "node:path";
11847
12498
  var SNAPSHOT_MAX_FILES = 2e4;
11848
12499
  var SNAPSHOT_MAX_BYTES = 512 * 1024 * 1024;
11849
12500
  var FALLBACK_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -11861,10 +12512,10 @@ var git = (cwd, args) => execFileSync("git", ["-C", cwd, ...args], {
11861
12512
  stdio: ["ignore", "pipe", "ignore"]
11862
12513
  });
11863
12514
  var nulPaths = (buffer) => buffer.toString("utf8").split("\0").filter((item) => item.length > 0);
11864
- var normalizeRelative = (value) => value.split(path15.sep).join("/");
12515
+ var normalizeRelative = (value) => value.split(path16.sep).join("/");
11865
12516
  var isInside = (root, candidate) => {
11866
- const relative = path15.relative(root, candidate);
11867
- return relative === "" || !path15.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path15.sep}`);
12517
+ const relative = path16.relative(root, candidate);
12518
+ return relative === "" || !path16.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path16.sep}`);
11868
12519
  };
11869
12520
  var excluded = (absolute, exclusions) => exclusions.some((root) => isInside(root, absolute));
11870
12521
  var entryDigest = (absolute) => {
@@ -11890,12 +12541,12 @@ var buildSnapshot = (cwd, paths, metadata, exclusions) => {
11890
12541
  let bytes = 0;
11891
12542
  const unique = [...new Set(paths)].sort((left, right) => left.localeCompare(right));
11892
12543
  for (const relativeInput of unique) {
11893
- const absolute = path15.resolve(cwd, relativeInput);
12544
+ const absolute = path16.resolve(cwd, relativeInput);
11894
12545
  if (!isInside(cwd, absolute) || excluded(absolute, exclusions)) continue;
11895
12546
  if (Object.keys(entries).length >= SNAPSHOT_MAX_FILES) {
11896
12547
  throw new Error(`Schema workspace snapshot exceeds ${SNAPSHOT_MAX_FILES} files`);
11897
12548
  }
11898
- const relative = normalizeRelative(path15.relative(cwd, absolute));
12549
+ const relative = normalizeRelative(path16.relative(cwd, absolute));
11899
12550
  const entry = entryDigest(absolute);
11900
12551
  bytes += entry.bytes;
11901
12552
  if (bytes > SNAPSHOT_MAX_BYTES) {
@@ -11924,9 +12575,9 @@ var fallbackPaths = (cwd, exclusions) => {
11924
12575
  const entries = fs7.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
11925
12576
  for (const entry of entries) {
11926
12577
  if (FALLBACK_SKIPPED_DIRECTORIES.has(entry.name)) continue;
11927
- const absolute = path15.join(directory, entry.name);
12578
+ const absolute = path16.join(directory, entry.name);
11928
12579
  if (excluded(absolute, exclusions)) continue;
11929
- const relative = normalizeRelative(path15.relative(cwd, absolute));
12580
+ const relative = normalizeRelative(path16.relative(cwd, absolute));
11930
12581
  if (entry.isDirectory()) visit(absolute);
11931
12582
  else paths.push(relative);
11932
12583
  if (paths.length > SNAPSHOT_MAX_FILES) {
@@ -11940,7 +12591,7 @@ var fallbackPaths = (cwd, exclusions) => {
11940
12591
  var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
11941
12592
  const cwd = fs7.realpathSync(cwdInput);
11942
12593
  const exclusions = excludedRoots.map((root) => {
11943
- const absolute = path15.resolve(root);
12594
+ const absolute = path16.resolve(root);
11944
12595
  try {
11945
12596
  return fs7.realpathSync(absolute);
11946
12597
  } catch {
@@ -11978,18 +12629,18 @@ var snapshotWorkspace = (cwdInput, excludedRoots = []) => {
11978
12629
  );
11979
12630
  };
11980
12631
  var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
11981
- if (!requestedPath.trim() || path15.isAbsolute(requestedPath)) {
12632
+ if (!requestedPath.trim() || path16.isAbsolute(requestedPath)) {
11982
12633
  throw new Error(`Schema paths must be non-empty project-relative paths: ${requestedPath}`);
11983
12634
  }
11984
12635
  const cwd = fs7.realpathSync(cwdInput);
11985
- const absolute = path15.resolve(cwd, requestedPath);
12636
+ const absolute = path16.resolve(cwd, requestedPath);
11986
12637
  if (!isInside(cwd, absolute) || absolute === cwd) {
11987
12638
  throw new Error(`Schema path escapes the project workspace: ${requestedPath}`);
11988
12639
  }
11989
- const relativeParts = path15.relative(cwd, absolute).split(path15.sep);
12640
+ const relativeParts = path16.relative(cwd, absolute).split(path16.sep);
11990
12641
  let cursor = cwd;
11991
12642
  for (let index = 0; index < relativeParts.length; index++) {
11992
- cursor = path15.join(cursor, relativeParts[index]);
12643
+ cursor = path16.join(cursor, relativeParts[index]);
11993
12644
  let stat2;
11994
12645
  try {
11995
12646
  stat2 = fs7.lstatSync(cursor);
@@ -11998,9 +12649,9 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
11998
12649
  if (index !== relativeParts.length - 1 || !options.allowAbsent) {
11999
12650
  throw new Error(`Schema path does not exist: ${requestedPath}`);
12000
12651
  }
12001
- const parentReal = fs7.realpathSync(path15.dirname(cursor));
12652
+ const parentReal = fs7.realpathSync(path16.dirname(cursor));
12002
12653
  if (!isInside(cwd, parentReal)) throw new Error(`Schema path parent escapes the workspace: ${requestedPath}`);
12003
- return { absolute, relative: normalizeRelative(path15.relative(cwd, absolute)), exists: false };
12654
+ return { absolute, relative: normalizeRelative(path16.relative(cwd, absolute)), exists: false };
12004
12655
  }
12005
12656
  throw error;
12006
12657
  }
@@ -12012,7 +12663,7 @@ var resolveWorkspaceFile = (cwdInput, requestedPath, options) => {
12012
12663
  throw new Error(`Schema transaction paths must name regular files: ${requestedPath}`);
12013
12664
  }
12014
12665
  }
12015
- return { absolute, relative: normalizeRelative(path15.relative(cwd, absolute)), exists: true };
12666
+ return { absolute, relative: normalizeRelative(path16.relative(cwd, absolute)), exists: true };
12016
12667
  };
12017
12668
  var sha256File = (absolute) => `sha256:${digest(fs7.readFileSync(absolute))}`;
12018
12669
 
@@ -12063,8 +12714,8 @@ var SchemaController = class {
12063
12714
  this.identity = identity;
12064
12715
  this.state = state;
12065
12716
  this.cwd = fs8.realpathSync(cwd);
12066
- this.#journalRoot = path16.join(mesh.root, "schema-transactions");
12067
- this.#lockPath = path16.join(this.#journalRoot, ".commit.lock");
12717
+ this.#journalRoot = path17.join(mesh.root, "schema-transactions");
12718
+ this.#lockPath = path17.join(this.#journalRoot, ".commit.lock");
12068
12719
  this.#recoverJournals();
12069
12720
  }
12070
12721
  #activeHypotheses = /* @__PURE__ */ new Map();
@@ -12263,7 +12914,7 @@ var SchemaController = class {
12263
12914
  this.#assertPayloadBound(input);
12264
12915
  const release = this.#acquireCommitLock();
12265
12916
  const transactionId = randomUUID4();
12266
- const journalPath = path16.join(this.#journalRoot, `${transactionId}.json`);
12917
+ const journalPath = path17.join(this.#journalRoot, `${transactionId}.json`);
12267
12918
  let journal;
12268
12919
  let consumed = false;
12269
12920
  let committed = false;
@@ -12744,7 +13395,7 @@ ${Date.now()}
12744
13395
  }
12745
13396
  for (const name of fs8.readdirSync(this.#journalRoot)) {
12746
13397
  if (!name.endsWith(".json")) continue;
12747
- const filePath = path16.join(this.#journalRoot, name);
13398
+ const filePath = path17.join(this.#journalRoot, name);
12748
13399
  try {
12749
13400
  const journal = JSON.parse(fs8.readFileSync(filePath, "utf8"));
12750
13401
  if (journal.format !== 1 || journal.status !== "applying" || !Array.isArray(journal.before)) continue;
@@ -12864,7 +13515,7 @@ var operationSchema = {
12864
13515
  }
12865
13516
  ]
12866
13517
  };
12867
- var descriptors6 = [
13518
+ var descriptors7 = [
12868
13519
  {
12869
13520
  name: "status",
12870
13521
  description: "Read the fixed session Schema mode, transaction bounds, generation, and invocation hypotheses",
@@ -12934,7 +13585,7 @@ var descriptors6 = [
12934
13585
  namespace: "schema"
12935
13586
  }
12936
13587
  ];
12937
- var normalizeSchemaArgs = actionArgNormalizer(() => descriptors6);
13588
+ var normalizeSchemaArgs = actionArgNormalizer(() => descriptors7);
12938
13589
  var SchemaProvider = class {
12939
13590
  constructor(controller) {
12940
13591
  this.controller = controller;
@@ -12943,12 +13594,12 @@ var SchemaProvider = class {
12943
13594
  description = "Host-owned, opt-in Schema verification and bounded local-file transaction control plane";
12944
13595
  async list(request, _context) {
12945
13596
  const query = request.query?.toLowerCase();
12946
- return query ? descriptors6.filter(
13597
+ return query ? descriptors7.filter(
12947
13598
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
12948
- ) : descriptors6;
13599
+ ) : descriptors7;
12949
13600
  }
12950
13601
  async describe(actionName, _context) {
12951
- return descriptors6.find((descriptor) => descriptor.name === actionName);
13602
+ return descriptors7.find((descriptor) => descriptor.name === actionName);
12952
13603
  }
12953
13604
  prepareArguments(actionName, args) {
12954
13605
  return normalizeSchemaArgs(actionName, args);
@@ -12998,11 +13649,11 @@ var SchemaProvider = class {
12998
13649
 
12999
13650
  // src/state/store.ts
13000
13651
  import { createHash as createHash5 } from "node:crypto";
13001
- import path18 from "node:path";
13652
+ import path19 from "node:path";
13002
13653
 
13003
13654
  // src/state/complexity.ts
13004
13655
  import fs9 from "node:fs";
13005
- import path17 from "node:path";
13656
+ import path18 from "node:path";
13006
13657
  var isWordStart = (character) => character !== void 0 && (character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character === "_" || character === "$");
13007
13658
  var isWordPart = (character) => isWordStart(character) || character !== void 0 && character >= "0" && character <= "9";
13008
13659
  var regularExpressionPrefixWords = /* @__PURE__ */ new Set([
@@ -13234,7 +13885,7 @@ var languageComplexities = [
13234
13885
  typeScriptJavaScriptComplexity
13235
13886
  ];
13236
13887
  var complexityForFile = (file, languages = languageComplexities) => {
13237
- const extension = path17.extname(file).toLowerCase();
13888
+ const extension = path18.extname(file).toLowerCase();
13238
13889
  return languages.find((language) => language.extensions.includes(extension));
13239
13890
  };
13240
13891
  var countFileComplexity = (file) => {
@@ -14125,7 +14776,7 @@ var StateStore = class {
14125
14776
  const files = [];
14126
14777
  let netDelta = 0;
14127
14778
  for (const file of this.normalizeComplexityFiles(requestedFiles, input.cwd)) {
14128
- const measured = countFileComplexity(path18.resolve(input.cwd, file));
14779
+ const measured = countFileComplexity(path19.resolve(input.cwd, file));
14129
14780
  if (!measured) {
14130
14781
  files.push({ file, supported: false });
14131
14782
  continue;
@@ -14152,7 +14803,7 @@ var StateStore = class {
14152
14803
  const updates = [];
14153
14804
  let netDelta = 0;
14154
14805
  for (const file of this.normalizeComplexityFiles(files, cwd)) {
14155
- const measured = countFileComplexity(path18.resolve(cwd, file));
14806
+ const measured = countFileComplexity(path19.resolve(cwd, file));
14156
14807
  if (!measured) {
14157
14808
  deltas.push({ file, supported: false });
14158
14809
  continue;
@@ -14202,11 +14853,11 @@ var StateStore = class {
14202
14853
  const normalized = /* @__PURE__ */ new Set();
14203
14854
  for (const file of files) {
14204
14855
  if (!file.trim()) continue;
14205
- const relative = path18.relative(cwd, path18.resolve(cwd, file));
14206
- if (relative === ".." || relative.startsWith(`..${path18.sep}`) || path18.isAbsolute(relative)) {
14856
+ const relative = path19.relative(cwd, path19.resolve(cwd, file));
14857
+ if (relative === ".." || relative.startsWith(`..${path19.sep}`) || path19.isAbsolute(relative)) {
14207
14858
  throw new Error(`State complexity file must be inside the project cwd: ${file}`);
14208
14859
  }
14209
- normalized.add(relative.split(path18.sep).join("/"));
14860
+ normalized.add(relative.split(path19.sep).join("/"));
14210
14861
  }
14211
14862
  return [...normalized];
14212
14863
  }
@@ -14648,7 +15299,7 @@ var checkGoalSchema = {
14648
15299
  additionalProperties: false
14649
15300
  };
14650
15301
  var emptySchema3 = { type: "object", properties: {}, additionalProperties: false };
14651
- var descriptors7 = [
15302
+ var descriptors8 = [
14652
15303
  {
14653
15304
  name: "transition",
14654
15305
  description: "Append a labeled, validated state transition and compare-and-swap advance the head",
@@ -14699,7 +15350,7 @@ var descriptors7 = [
14699
15350
  namespace: "state"
14700
15351
  }
14701
15352
  ];
14702
- var normalizeStateArgs = actionArgNormalizer(() => descriptors7);
15353
+ var normalizeStateArgs = actionArgNormalizer(() => descriptors8);
14703
15354
  var StateProvider = class {
14704
15355
  name = "state";
14705
15356
  description = "Schema-style labeled transition layer: an append-only timeline of validated transitions with a compare-and-swap head and evidence-based certification over mesh storage";
@@ -14714,12 +15365,12 @@ var StateProvider = class {
14714
15365
  }
14715
15366
  async list(request, _context) {
14716
15367
  const query = request.query?.toLowerCase();
14717
- return query ? descriptors7.filter(
15368
+ return query ? descriptors8.filter(
14718
15369
  (descriptor) => `${descriptor.name} ${descriptor.description}`.toLowerCase().includes(query)
14719
- ) : descriptors7;
15370
+ ) : descriptors8;
14720
15371
  }
14721
15372
  async describe(actionName, _context) {
14722
- return descriptors7.find((descriptor) => descriptor.name === actionName);
15373
+ return descriptors8.find((descriptor) => descriptor.name === actionName);
14723
15374
  }
14724
15375
  prepareArguments(actionName, args) {
14725
15376
  return normalizeStateArgs(actionName, args);
@@ -15227,7 +15878,7 @@ var FabricRuntimeState = class {
15227
15878
  const projectRoot = process.env.OMP_FABRIC_PROJECT_ROOT ?? context.cwd;
15228
15879
  await ensureFabricStateExclude(projectRoot).catch(() => void 0);
15229
15880
  const configuredMeshRoot = this.#config.mesh.root;
15230
- const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path19.resolve(projectRoot, configuredMeshRoot) : path19.join(projectRoot, ".omp", "fabric", "mesh"));
15881
+ const meshRoot = process.env.OMP_FABRIC_MESH_ROOT ?? (configuredMeshRoot ? path20.resolve(projectRoot, configuredMeshRoot) : path20.join(projectRoot, ".omp", "fabric", "mesh"));
15231
15882
  this.#mesh = new MeshStore(
15232
15883
  meshRoot,
15233
15884
  this.#config.mesh.maxEventBytes,
@@ -15323,6 +15974,22 @@ var FabricRuntimeState = class {
15323
15974
  'disabled by configuration (judgment.enabled=false); set "judgment": { "enabled": true } in .omp/fabric.json or the agent fabric.json to enable judgment.* actions'
15324
15975
  );
15325
15976
  }
15977
+ if (this.#config.image.enabled) {
15978
+ const imageConfig = () => this.#config?.image ?? DEFAULT_FABRIC_CONFIG.image;
15979
+ await installBuiltin(createProviderComponent({
15980
+ provider: "image",
15981
+ description: "Image generation and editing across a configured priority chain",
15982
+ create: () => new ImageProvider(imageConfig, {
15983
+ registry: context.modelRegistry,
15984
+ getSessionId: () => context.sessionManager.getSessionId() ?? void 0
15985
+ })
15986
+ }));
15987
+ } else {
15988
+ this.#registry.markUnavailable(
15989
+ "image",
15990
+ 'disabled by configuration (image.enabled=false); set "image": { "enabled": true } in .omp/fabric.json or the agent fabric.json to enable image.* actions'
15991
+ );
15992
+ }
15326
15993
  const agentConfig = enforceSchema ? { ...this.#config.agents, enabled: false } : this.#config.agents;
15327
15994
  const modelsConfig = this.#config.models;
15328
15995
  const visibleOmpModels = () => {
@@ -15421,8 +16088,8 @@ var FabricRuntimeState = class {
15421
16088
  };
15422
16089
  const lineageAlive = (rootId) => this.#participants?.get(rootId) !== void 0;
15423
16090
  const actorRoots = {
15424
- project: path19.join(meshRoot, "actors"),
15425
- session: path19.join(meshRoot, "actors", fabricSessionId)
16091
+ project: path20.join(meshRoot, "actors"),
16092
+ session: path20.join(meshRoot, "actors", fabricSessionId)
15426
16093
  };
15427
16094
  const acquireActorCapabilityView = (requirements, signal) => this.#registry.acquireCapabilityView(requirements, {
15428
16095
  cwd: context.cwd,
@@ -15626,6 +16293,7 @@ ${escapeXmlText(text)}
15626
16293
  "compact",
15627
16294
  ...this.#config.codemap.enabled ? ["codemap"] : [],
15628
16295
  ...this.#config.judgment.enabled ? ["judgment"] : [],
16296
+ ...this.#config.image.enabled ? ["image"] : [],
15629
16297
  "agents",
15630
16298
  ...this.#config.memory.enabled ? ["memory"] : []
15631
16299
  ]);