@adaptiqai/deepseek-harness 0.2.11 → 0.2.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.
package/lib/host.js CHANGED
@@ -17,7 +17,7 @@ var __runInitializers = (array, flags, self, value) => {
17
17
  return value;
18
18
  };
19
19
  var __decorateElement = (array, flags, name2, decorators, target, extra) => {
20
- var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
20
+ var fn, it, done, ctx, access2, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
21
21
  var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
22
22
  var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
23
23
  var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name2]() {
@@ -29,9 +29,9 @@ var __decorateElement = (array, flags, name2, decorators, target, extra) => {
29
29
  for (var i = decorators.length - 1; i >= 0; i--) {
30
30
  ctx = __decoratorContext(k, name2, done = {}, array[3], extraInitializers);
31
31
  if (k) {
32
- ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name2 in x };
33
- if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name2];
34
- if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name2] = y;
32
+ ctx.static = s, ctx.private = p, access2 = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name2 in x };
33
+ if (k ^ 3) access2.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name2];
34
+ if (k > 2) access2.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name2] = y;
35
35
  }
36
36
  it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
37
37
  if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
@@ -54,7 +54,7 @@ import { attributionHeaders as attributionHeaders2 } from "@deepseek-ai/dsh-llm"
54
54
  import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
55
55
 
56
56
  // src/host/adapter.ts
57
- import { attributionHeaders, LlmAdapter, LlmError, ProviderRequestId } from "@deepseek-ai/dsh-llm";
57
+ import { attributionHeaders, IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ProviderRequestId } from "@deepseek-ai/dsh-llm";
58
58
 
59
59
  // ../core/lib/errors.js
60
60
  var CODES = {
@@ -122,21 +122,26 @@ function classifyFailure(status, raw, headers, model2, options = {}) {
122
122
  const library = /\blibrary is full/iu.test(detail.message ?? "");
123
123
  return { ...make(CODES.memoryFull, library ? MEMORY_FULL_LIBRARY : MEMORY_FULL, "memory-full"), memoryFull: library ? "library" : "no-room" };
124
124
  }
125
+ if (detail.code === "plan_usage_exhausted") {
126
+ const renewsAt = envelope.detail?.renews_at;
127
+ const failure = make(CODES.noLanes, detail.message ?? "Your plan's usage is used up for now. Rent extra lanes in the meantime.", "no-lanes");
128
+ return typeof renewsAt === "number" ? { ...failure, planRenewsAt: renewsAt } : failure;
129
+ }
125
130
  if (effective === 402 || detail.code === "rental_paused") {
126
131
  const releaseAt = envelope.detail?.release_at;
127
132
  const minutes = typeof releaseAt === "number" ? Math.max(1, Math.ceil((releaseAt * 1e3 - Date.now()) / 6e4)) : null;
128
133
  const keep = minutes === null ? "to continue" : `within ${minutes} minute${minutes === 1 ? "" : "s"} to keep them`;
129
- const failure = make(CODES.outOfCredit, `Your balance ran out, so your lanes on ${name2} are paused. Add funds ${keep}.`, "out-of-credit");
134
+ const failure = make(CODES.outOfCredit, `Your balance ran out, so your lanes are paused. Add funds ${keep}.`, "out-of-credit");
130
135
  return minutes === null ? failure : { ...failure, minutesLeft: minutes };
131
136
  }
132
137
  if (effective === 401) {
133
138
  return make(CODES.signIn, "Your AdaptiqAI sign-in has expired. Open Settings \u2192 AdaptiqAI and sign in again.", "sign-in");
134
139
  }
135
140
  if (detail.code === "rental_stopped_unused") {
136
- return make(CODES.noLanes, `Your lane on ${name2} stopped because it went unused, so nothing more was charged. Get access to start it again.`, "no-lanes");
141
+ return make(CODES.noLanes, "Your lane stopped because it went unused, so nothing more was charged. Get access to start it again.", "no-lanes");
137
142
  }
138
143
  if (has(detail, "AdaptiqLaneAccessRequired", "inference_entitlement_required", "instance_access_required", "lane_access") || effective === 403 && has(detail, "lane", "entitle", "model access")) {
139
- return make(CODES.noLanes, `Your workspace has no lanes on ${name2} yet. Get access to start.`, "no-lanes");
144
+ return make(CODES.noLanes, "You need to rent a lane or subscribe to AdaptiqAI to continue.", "no-lanes");
140
145
  }
141
146
  if (effective === 403) {
142
147
  return make(CODES.signIn, "This AdaptiqAI connection isn't allowed to do that. Sign in again from Settings \u2192 AdaptiqAI.", "sign-in");
@@ -321,10 +326,10 @@ var REFRESH_MARGIN_MS = 2 * 6e4;
321
326
  var DEVICE_TIMEOUT_MS = 10 * 6e4;
322
327
  var UNREACHABLE = "AdaptiqAI can't be reached. Check your internet connection and try again.";
323
328
  function deviceLabel(value) {
324
- let label = value.replace(/\.local$/iu, "").replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/gu, " ").trim() || "this computer";
325
- while (Buffer.byteLength(label) > 80)
326
- label = label.slice(0, -1);
327
- return label;
329
+ let label2 = value.replace(/\.local$/iu, "").replace(/[\p{Cc}\p{Cf}]/gu, " ").replace(/\s+/gu, " ").trim() || "this computer";
330
+ while (Buffer.byteLength(label2) > 80)
331
+ label2 = label2.slice(0, -1);
332
+ return label2;
328
333
  }
329
334
  var defaultSleep = (ms, signal) => new Promise((resolve2, reject) => {
330
335
  if (signal.aborted) {
@@ -491,13 +496,13 @@ var AuthManager = class {
491
496
  if (!response.ok)
492
497
  throw new Error(`AdaptiqAI could not refresh the sign-in (HTTP ${response.status}).`);
493
498
  const body = await readJson(response);
494
- const access = String(body?.access_token ?? "");
499
+ const access2 = String(body?.access_token ?? "");
495
500
  const refresh = String(body?.refresh_token ?? "");
496
- if (!access.startsWith("ak_") || !refresh.startsWith("akr_"))
501
+ if (!access2.startsWith("ak_") || !refresh.startsWith("akr_"))
497
502
  throw new Error("AdaptiqAI returned an invalid refreshed credential.");
498
503
  return {
499
504
  ...current,
500
- access,
505
+ access: access2,
501
506
  refresh,
502
507
  expiresAt: this.now() + Math.max(60, Number(body?.expires_in ?? 3600)) * 1e3,
503
508
  workspaceLabel: typeof body?.workspace_label === "string" && body.workspace_label !== "" ? body.workspace_label : current.workspaceLabel
@@ -627,9 +632,9 @@ var AuthManager = class {
627
632
  return;
628
633
  }
629
634
  const body = await readJson(response);
630
- const access = String(body?.access_token ?? "");
635
+ const access2 = String(body?.access_token ?? "");
631
636
  const refresh = String(body?.refresh_token ?? "");
632
- if (!access.startsWith("ak_") || !refresh.startsWith("akr_")) {
637
+ if (!access2.startsWith("ak_") || !refresh.startsWith("akr_")) {
633
638
  this.fail(flow, { code: "failed", message: "AdaptiqAI returned an invalid credential." });
634
639
  return;
635
640
  }
@@ -637,7 +642,7 @@ var AuthManager = class {
637
642
  const grant = {
638
643
  schema: "adaptiq-dsh-grant-v1",
639
644
  apiBase: this.options.apiBase,
640
- access,
645
+ access: access2,
641
646
  refresh,
642
647
  clientProof,
643
648
  expiresAt: this.now() + Math.max(60, Number(body?.expires_in ?? 3600)) * 1e3,
@@ -1274,15 +1279,19 @@ var en = {
1274
1279
  n_connected_body: "Your AdaptiqAI models are in the model picker.",
1275
1280
  n_compaction_title: "Nothing to compact",
1276
1281
  n_compaction_body: "AdaptiqAI keeps the whole conversation in memory, so nothing needs shortening.",
1282
+ n_imagesTextOnly_title: "Images were not sent",
1283
+ n_imagesTextOnly_body: "{model} reads text only, so the model got a short note in place of each image. Choose a model that reads images to ask about them.",
1277
1284
  n_signIn_title: "Sign in to keep going",
1278
1285
  n_signIn_body: "Your AdaptiqAI connection needs a fresh sign-in.",
1279
1286
  n_connect_body: "Connect your AdaptiqAI account to use this model.",
1280
- n_noLanes_title: "No lanes on {model}",
1281
- n_noLanes_body: "Your workspace has no lanes on {model} yet.",
1282
- n_laneStopped_body: "Your lane on {model} stopped because it went unused, so nothing more was charged.",
1287
+ n_noLanes_title: "Rent a lane to continue",
1288
+ n_noLanes_body: "You need to rent a lane or subscribe to AdaptiqAI to continue.",
1289
+ n_laneStopped_body: "Your lane stopped because it went unused, so nothing more was charged.",
1290
+ n_planUsedUp_body: "Your plan's usage is used up until {until}. Rent extra lanes in the meantime.",
1291
+ n_planUsedUpNoDate_body: "Your plan's usage is used up for now. Rent extra lanes in the meantime.",
1283
1292
  n_credit_title: "Add funds to continue",
1284
- n_credit_body: "Your balance ran out, so your lanes on {model} are paused.",
1285
- n_creditMinutes_body: "Your balance ran out, so your lanes on {model} are paused. Add funds within {minutes} min to keep them.",
1293
+ n_credit_body: "Your balance ran out, so your lanes are paused.",
1294
+ n_creditMinutes_body: "Your balance ran out, so your lanes are paused. Add funds within {minutes} min to keep them.",
1286
1295
  n_busy_title: "All lanes are busy",
1287
1296
  n_busy_body: "Your request on {model} is waiting for a free lane and will retry by itself.",
1288
1297
  n_preparing_title: "Getting your conversation ready",
@@ -1674,8 +1683,8 @@ var str4 = (value) => typeof value === "string" && value.trim() !== "" ? value.t
1674
1683
  function say(title, body, params = {}) {
1675
1684
  return { title: fill(en[title], params), body: body === null ? "" : fill(en[body], params), text: { title, body, params } };
1676
1685
  }
1677
- function button(id, label, extra = {}) {
1678
- return { id, label: en[label], labelKey: label, ...extra };
1686
+ function button(id, label2, extra = {}) {
1687
+ return { id, label: en[label2], labelKey: label2, ...extra };
1679
1688
  }
1680
1689
  var count2 = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
1681
1690
  var PRIVATE_HELD = "A private conversation is not kept on AdaptiqAI, so it can't use this before it exists there. Turn Remember on for this conversation to add it.";
@@ -1783,9 +1792,9 @@ function isYes(reply) {
1783
1792
  const name2 = reply.slice(at).replace(/^[,,::;;.。]\s*/u, "");
1784
1793
  return YES_PHRASES.has(words(reply.slice(0, at))) && NAMED.test(name2);
1785
1794
  }
1786
- function createRefusal(access) {
1787
- const reason = typeof access?.reason === "string" ? access.reason : null;
1788
- const owners = [].concat(access?.owners ?? access?.owner ?? []).map((owner) => typeof owner === "string" ? owner : typeof owner?.name === "string" ? owner.name : typeof owner?.email === "string" ? owner.email : "").filter((owner) => owner !== "");
1795
+ function createRefusal(access2) {
1796
+ const reason = typeof access2?.reason === "string" ? access2.reason : null;
1797
+ const owners = [].concat(access2?.owners ?? access2?.owner ?? []).map((owner) => typeof owner === "string" ? owner : typeof owner?.name === "string" ? owner.name : typeof owner?.email === "string" ? owner.email : "").filter((owner) => owner !== "");
1789
1798
  const ask = owners.length === 0 ? "ask a workspace owner" : `ask ${owners.join(" or ")}`;
1790
1799
  switch (reason) {
1791
1800
  case "role":
@@ -1842,7 +1851,7 @@ function copyFailure(error, model2) {
1842
1851
  case "model_mismatch":
1843
1852
  return str4(detail.message) ?? `This collection can't be made available for ${model2}.`;
1844
1853
  case "inference_entitlement_required":
1845
- return `Your workspace has no lanes on ${model2}.`;
1854
+ return "You need to rent a lane or subscribe to AdaptiqAI to continue.";
1846
1855
  case "collection_not_found":
1847
1856
  return "This collection no longer exists.";
1848
1857
  case "sources_not_exportable":
@@ -2505,9 +2514,10 @@ var Workspace = class {
2505
2514
  };
2506
2515
  }
2507
2516
  /**
2508
- * Read the newest published version. It is announced only once it is old
2509
- * enough for the harness's package manager to install (pnpm refuses a
2510
- * version younger than a day); the check then comes back when it is.
2517
+ * Read the newest published version. It is announced at once where the
2518
+ * plugin installs it itself and the harness exempts it from its package
2519
+ * manager's wait; otherwise only once it is old enough (pnpm refuses a
2520
+ * version younger than a day), and the check comes back when it is.
2511
2521
  */
2512
2522
  async checkForUpdate() {
2513
2523
  if (!this.deps.updateCheck || this.now() - this.updateCheckedAt < 24 * 36e5)
@@ -2526,7 +2536,7 @@ var Workspace = class {
2526
2536
  const published = Date.parse(str4(obj4(body.time)?.[version]) ?? "");
2527
2537
  if (!Number.isFinite(published))
2528
2538
  return;
2529
- if (this.now() - published < RELEASE_AGE_MS) {
2539
+ if (this.now() - published < RELEASE_AGE_MS && !await this.installsFreshReleases()) {
2530
2540
  this.updateCheckedAt = published + RELEASE_AGE_MS - 24 * 36e5 + 6e4;
2531
2541
  return;
2532
2542
  }
@@ -2536,6 +2546,13 @@ var Workspace = class {
2536
2546
  } catch {
2537
2547
  }
2538
2548
  }
2549
+ /** Whether the one-click update installs a release younger than the package manager's wait. */
2550
+ async installsFreshReleases() {
2551
+ const updater = this.deps.updater;
2552
+ if (updater?.canInstall !== true || updater.installsFreshReleases === void 0)
2553
+ return false;
2554
+ return await updater.installsFreshReleases().catch(() => false);
2555
+ }
2539
2556
  /** The update toast: once a newer version is out and not installed yet. */
2540
2557
  announceUpdate() {
2541
2558
  const plugin = this.pluginState();
@@ -2725,13 +2742,13 @@ var Workspace = class {
2725
2742
  this.publish();
2726
2743
  }
2727
2744
  /** A browser approval completed. */
2728
- signedIn(label) {
2745
+ signedIn(label2) {
2729
2746
  if (this.state.defaultModel === void 0) {
2730
2747
  this.state = { ...this.state, defaultModel: "pending" };
2731
2748
  void this.persist();
2732
2749
  }
2733
2750
  this.clearNotices("sign-in");
2734
- this.raise({ kind: "connected", tone: "success", ...say("connectedTo", "n_connected_body", { workspace: label }), actions: [] });
2751
+ this.raise({ kind: "connected", tone: "success", ...say("connectedTo", "n_connected_body", { workspace: label2 }), actions: [] });
2735
2752
  this.summaryMode = "unknown";
2736
2753
  this.summaryMissingAt = null;
2737
2754
  void this.refresh();
@@ -2742,11 +2759,11 @@ var Workspace = class {
2742
2759
  * Waits while no model is running yet; never fails a refresh.
2743
2760
  */
2744
2761
  async applyFirstDefault() {
2745
- const access = this.deps.defaultModel;
2746
- if (this.state.defaultModel !== "pending" || access === void 0)
2762
+ const access2 = this.deps.defaultModel;
2763
+ if (this.state.defaultModel !== "pending" || access2 === void 0)
2747
2764
  return;
2748
2765
  try {
2749
- const current = access.current();
2766
+ const current = access2.current();
2750
2767
  if (current === void 0)
2751
2768
  return;
2752
2769
  let choice = firstDefaultModel(current, this.deps.route, this.catalog);
@@ -2764,7 +2781,7 @@ var Workspace = class {
2764
2781
  }
2765
2782
  if (choice === null)
2766
2783
  return;
2767
- await access.save(choice);
2784
+ await access2.save(choice);
2768
2785
  this.deps.log?.("info", `adaptiq: default model set to ${choice.model}`);
2769
2786
  } catch (error) {
2770
2787
  this.deps.log?.("warn", `adaptiq: could not set the default model: ${error instanceof Error ? error.message : String(error)}`);
@@ -3317,6 +3334,14 @@ var Workspace = class {
3317
3334
  actions: [button("open-console", "n_openConsole", { url: this.links.console, primary: true })]
3318
3335
  });
3319
3336
  }
3337
+ /**
3338
+ * Say that a message's images were not sent because the model reads text only.
3339
+ * @param sessionId - the conversation, when known.
3340
+ * @param modelName - the model's display name.
3341
+ */
3342
+ imagesNotSent(sessionId2, modelName) {
3343
+ this.raise({ kind: "images", tone: "warning", ...say("n_imagesTextOnly_title", "n_imagesTextOnly_body", { model: modelName }), actions: [], ...sessionId2 === void 0 ? {} : { sessionId: sessionId2 } });
3344
+ }
3320
3345
  failed(sessionId2, model2, failure) {
3321
3346
  if (failure.code === CODES.compaction) {
3322
3347
  this.raise({ kind: "compaction", tone: "info", ...say("n_compaction_title", "n_compaction_body"), actions: [], ...sessionId2 === void 0 ? {} : { sessionId: sessionId2 } });
@@ -3358,11 +3383,13 @@ var Workspace = class {
3358
3383
  break;
3359
3384
  case "no-lanes": {
3360
3385
  const offer = this.offers.find((item) => item.model === model2) ?? this.offers.find((item) => item.name.toLowerCase() === name2.toLowerCase());
3386
+ const body = failure.serverCode === "rental_stopped_unused" ? "n_laneStopped_body" : failure.planRenewsAt !== void 0 ? "n_planUsedUp_body" : failure.serverCode === "plan_usage_exhausted" ? "n_planUsedUpNoDate_body" : "n_noLanes_body";
3387
+ const until = failure.planRenewsAt === void 0 ? "" : `${new Date(failure.planRenewsAt * 1e3).toISOString().slice(0, 16).replace("T", " ")} UTC`;
3361
3388
  this.raise({
3362
3389
  kind: "no-lanes",
3363
3390
  tone: "warning",
3364
3391
  modal: true,
3365
- ...say("n_noLanes_title", failure.serverCode === "rental_stopped_unused" ? "n_laneStopped_body" : "n_noLanes_body", { model: name2 }),
3392
+ ...say("n_noLanes_title", body, { until }),
3366
3393
  actions: [button("get-access", "getAccess", { primary: true, ...offer === void 0 ? {} : { offerId: offer.id } }), button("dismiss", "notNow")]
3367
3394
  });
3368
3395
  break;
@@ -3372,7 +3399,7 @@ var Workspace = class {
3372
3399
  kind: "out-of-credit",
3373
3400
  tone: "error",
3374
3401
  modal: true,
3375
- ...failure.minutesLeft === void 0 ? say("n_credit_title", "n_credit_body", { model: name2 }) : say("n_credit_title", "n_creditMinutes_body", { model: name2, minutes: String(failure.minutesLeft) }),
3402
+ ...failure.minutesLeft === void 0 ? say("n_credit_title", "n_credit_body") : say("n_credit_title", "n_creditMinutes_body", { minutes: String(failure.minutesLeft) }),
3376
3403
  actions: [button("add-funds", "n_addFunds", { url: this.links.models, primary: true }), button("dismiss", "notNow")]
3377
3404
  });
3378
3405
  break;
@@ -3552,7 +3579,7 @@ var Workspace = class {
3552
3579
  if (target === void 0)
3553
3580
  throw new Error("Choose an AdaptiqAI model first.");
3554
3581
  const modelName = this.modelName(target);
3555
- const label = modelName ?? target;
3582
+ const label2 = modelName ?? target;
3556
3583
  let body;
3557
3584
  try {
3558
3585
  body = await this.deps.api.json(`/v1/memories/${encodeURIComponent(id)}/copies`, {
@@ -3568,7 +3595,7 @@ var Workspace = class {
3568
3595
  this.copies.set(copyKey(id, target), { source: id, from, target, copy: null, status: "building" });
3569
3596
  return { status: "building", model: target, modelName };
3570
3597
  }
3571
- throw new Error(copyFailure(error, label));
3598
+ throw new Error(copyFailure(error, label2));
3572
3599
  }
3573
3600
  const copy = obj4(body?.copy);
3574
3601
  const status = copyStatus(str4(copy?.status));
@@ -3815,9 +3842,9 @@ var Workspace = class {
3815
3842
  if (this.memoryModel(session) === void 0)
3816
3843
  await this.ensureModels().catch(() => {
3817
3844
  });
3818
- const access = await this.deps.api.json("/v1/cli-auth/knowledge/status", { allow: [404] }).catch(() => null);
3819
- if (access !== null && access.create_allowed === false)
3820
- throw new Error(createRefusal(access));
3845
+ const access2 = await this.deps.api.json("/v1/cli-auth/knowledge/status", { allow: [404] }).catch(() => null);
3846
+ if (access2 !== null && access2.create_allowed === false)
3847
+ throw new Error(createRefusal(access2));
3821
3848
  const body = await this.deps.api.json("/v1/memories", {
3822
3849
  method: "POST",
3823
3850
  headers: { ...this.memoryHeaders(session), "idempotency-key": idempotencyKey },
@@ -4663,7 +4690,70 @@ async function* translate(events, hooks) {
4663
4690
  };
4664
4691
  }
4665
4692
 
4693
+ // src/host/images.ts
4694
+ var ENGINE_IMAGE_LIMITS = Object.freeze({
4695
+ maxImages: 8,
4696
+ maxImageBytes: 20 * 1024 * 1024,
4697
+ maxPixels: 16e6
4698
+ });
4699
+ var ImageProblem = class extends Error {
4700
+ name = "ImageProblem";
4701
+ };
4702
+ var startsWith = (data, bytes, at = 0) => data.length >= at + bytes.length && bytes.every((byte, index) => data[at + index] === byte);
4703
+ var ascii = (text2) => [...text2].map((char) => char.charCodeAt(0));
4704
+ function sniffImageType(data) {
4705
+ if (startsWith(data, [137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
4706
+ if (startsWith(data, [255, 216, 255])) return "image/jpeg";
4707
+ if (startsWith(data, ascii("GIF87a")) || startsWith(data, ascii("GIF89a"))) return "image/gif";
4708
+ if (startsWith(data, ascii("RIFF")) && startsWith(data, ascii("WEBP"), 8)) return "image/webp";
4709
+ return null;
4710
+ }
4711
+ function fitPixels(width, height, maxPixels) {
4712
+ if (width * height <= maxPixels) return { width, height };
4713
+ const scale = Math.sqrt(maxPixels / (width * height));
4714
+ let w = Math.max(1, Math.floor(width * scale));
4715
+ let h = Math.max(1, Math.floor(height * scale));
4716
+ while (w * h > maxPixels) {
4717
+ if (w >= h) w--;
4718
+ else h--;
4719
+ }
4720
+ return { width: w, height: h };
4721
+ }
4722
+ var carriesImages = (message) => message.role === "user" || message.role === "tool";
4723
+ function sentImages(messages) {
4724
+ const out = [];
4725
+ for (const message of messages) {
4726
+ if (!carriesImages(message)) continue;
4727
+ for (const block of message.content) if (block.type === "image" && block.offloaded !== true) out.push(block);
4728
+ }
4729
+ return out;
4730
+ }
4731
+ function hasImages(messages) {
4732
+ return messages.some((message) => message.content.some((block) => block.type === "image"));
4733
+ }
4734
+ var mib = (bytes) => `${Math.round(bytes / (1024 * 1024) * 10) / 10} MB`;
4735
+ var label = (ref) => ref.name === void 0 ? "An image" : `The image "${ref.name}"`;
4736
+ async function prepareImages(messages, source, limits, signal) {
4737
+ const refs = /* @__PURE__ */ new Map();
4738
+ for (const block of sentImages(messages)) refs.set(String(block.attachment.attachmentId), block.attachment);
4739
+ const entries = await Promise.all([...refs].map(async ([id, ref]) => {
4740
+ const target = { ...fitPixels(ref.width, ref.height, limits.maxPixels), maxBytes: limits.maxImageBytes };
4741
+ const version = await source.readImageRequest(ref, target, signal);
4742
+ const mediaType = sniffImageType(version.data);
4743
+ if (mediaType === null) throw new ImageProblem(`${label(ref)} is not a PNG, JPEG, WebP or GIF file, so AdaptiqAI can't read it. Attach it in one of those formats.`);
4744
+ if (version.data.byteLength > limits.maxImageBytes) {
4745
+ throw new ImageProblem(`${label(ref)} is ${mib(version.data.byteLength)}; AdaptiqAI takes images up to ${mib(limits.maxImageBytes)}. Attach a smaller copy.`);
4746
+ }
4747
+ if (version.width * version.height > limits.maxPixels) {
4748
+ throw new ImageProblem(`${label(ref)} is ${version.width}\xD7${version.height} pixels, more than AdaptiqAI takes. Attach a smaller copy.`);
4749
+ }
4750
+ return [id, { url: `data:${mediaType};base64,${Buffer.from(version.data).toString("base64")}`, width: version.width, height: version.height }];
4751
+ }));
4752
+ return new Map(entries);
4753
+ }
4754
+
4666
4755
  // src/host/wire.ts
4756
+ import { offloadedImageText, requestImageHandleText, textOnlyImageText } from "@deepseek-ai/dsh-llm";
4667
4757
  function sessionReferences(message) {
4668
4758
  if (message.role !== "user") return null;
4669
4759
  const source = message.source;
@@ -4755,16 +4845,62 @@ function userTurns(messages) {
4755
4845
  }
4756
4846
  var text = (blocks) => blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
4757
4847
  var reasoning = (blocks) => blocks.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
4848
+ var Parts = class {
4849
+ parts = [];
4850
+ glue = false;
4851
+ text(value, own = false) {
4852
+ if (value === "") return;
4853
+ const last = this.parts.at(-1);
4854
+ if (last?.type === "text") last.text += (own || this.glue ? "\n" : "") + value;
4855
+ else this.parts.push({ type: "text", text: value });
4856
+ this.glue = own;
4857
+ }
4858
+ image(url) {
4859
+ this.parts.push({ type: "image_url", image_url: { url } });
4860
+ this.glue = false;
4861
+ }
4862
+ get hasImage() {
4863
+ return this.parts.some((part) => part.type === "image_url");
4864
+ }
4865
+ get joined() {
4866
+ return this.parts.map((part) => part.type === "text" ? part.text : "").join("");
4867
+ }
4868
+ };
4869
+ function putImage(block, images, parts, inline) {
4870
+ const access2 = images?.access?.(block.attachment);
4871
+ if (block.offloaded === true) {
4872
+ parts.text(offloadedImageText(block.attachment, access2), true);
4873
+ return null;
4874
+ }
4875
+ const sent = images?.sent?.get(String(block.attachment.attachmentId));
4876
+ if (sent === void 0) {
4877
+ parts.text(textOnlyImageText(block.attachment), true);
4878
+ return null;
4879
+ }
4880
+ parts.text(requestImageHandleText(block.attachment, sent, access2), true);
4881
+ if (!inline) return sent;
4882
+ parts.image(sent.url);
4883
+ return null;
4884
+ }
4758
4885
  function replayField(message) {
4759
4886
  const source = message.source;
4760
4887
  const state2 = source?.replayState;
4761
4888
  const field = state2?.response?.reasoningField;
4762
4889
  return typeof field === "string" && /^[a-z_]{1,32}$/u.test(field) ? field : "reasoning_content";
4763
4890
  }
4764
- function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
4891
+ function wireMessages(options, recalled = /* @__PURE__ */ new Map(), images) {
4765
4892
  const out = [];
4766
4893
  if (options.system !== void 0 && options.system !== "") out.push({ role: "system", content: options.system });
4894
+ let toolImages = [];
4895
+ const flushToolImages = () => {
4896
+ if (toolImages.length === 0) return;
4897
+ const parts = [{ type: "text", text: toolImages.length === 1 ? "The image from the tool result above:" : "The images from the tool results above, in order:" }];
4898
+ for (const image of toolImages) parts.push({ type: "image_url", image_url: { url: image.url } });
4899
+ out.push({ role: "user", content: parts });
4900
+ toolImages = [];
4901
+ };
4767
4902
  options.messages.forEach((message, index) => {
4903
+ if (message.role !== "tool") flushToolImages();
4768
4904
  switch (message.role) {
4769
4905
  case "system": {
4770
4906
  const content = text(message.content);
@@ -4776,6 +4912,15 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
4776
4912
  return;
4777
4913
  }
4778
4914
  case "user": {
4915
+ if (message.content.some((block) => block.type === "image")) {
4916
+ const parts = new Parts();
4917
+ for (const block of message.content) {
4918
+ if (block.type === "text") parts.text(block.text);
4919
+ else if (block.type === "image") putImage(block, images, parts, true);
4920
+ }
4921
+ out.push({ role: "user", content: parts.hasImage ? parts.parts : parts.joined });
4922
+ return;
4923
+ }
4779
4924
  const references = recalled.size === 0 ? null : sessionReferences(message);
4780
4925
  const content = text(message.content);
4781
4926
  out.push({ role: "user", content: references === null ? content : recallSnapshot(content, references, recalled) });
@@ -4797,7 +4942,15 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
4797
4942
  return;
4798
4943
  }
4799
4944
  case "tool": {
4800
- const content = text(message.content);
4945
+ const parts = new Parts();
4946
+ for (const block of message.content) {
4947
+ if (block.type === "text") parts.text(block.text);
4948
+ else if (block.type === "image") {
4949
+ const deferred = putImage(block, images, parts, false);
4950
+ if (deferred !== null) toolImages.push(deferred);
4951
+ }
4952
+ }
4953
+ const content = parts.joined;
4801
4954
  out.push({ role: "tool", tool_call_id: message.toolCallId, content: content === "" ? "(no output)" : content });
4802
4955
  return;
4803
4956
  }
@@ -4805,12 +4958,13 @@ function wireMessages(options, recalled = /* @__PURE__ */ new Map()) {
4805
4958
  return;
4806
4959
  }
4807
4960
  });
4961
+ flushToolImages();
4808
4962
  return out;
4809
4963
  }
4810
4964
  function buildChatBody(options, model2, extras) {
4811
4965
  const body = {
4812
4966
  model: options.model,
4813
- messages: wireMessages(options, extras.recalled),
4967
+ messages: wireMessages(options, extras.recalled, extras.images),
4814
4968
  stream: true,
4815
4969
  stream_options: { include_usage: extras.includeUsage ?? true }
4816
4970
  };
@@ -4962,6 +5116,41 @@ var AdaptiqAdapter = class extends LlmAdapter {
4962
5116
  stream(options) {
4963
5117
  return this.generate(options);
4964
5118
  }
5119
+ /**
5120
+ * The request's images, read from the harness's stored copies, when the
5121
+ * model's card says it reads images. Otherwise each image goes as a short
5122
+ * note and the person is told.
5123
+ */
5124
+ async images(options, model2, sessionId2, fail2, signal) {
5125
+ if (!hasImages(options.messages)) return void 0;
5126
+ const access2 = this.deps.imageAccess === void 0 ? void 0 : (ref) => {
5127
+ try {
5128
+ return this.deps.imageAccess?.(ref);
5129
+ } catch {
5130
+ return void 0;
5131
+ }
5132
+ };
5133
+ const withAccess = access2 === void 0 ? {} : { access: access2 };
5134
+ const problem = (message) => fail2({ code: CODES.invalidRequest, message, status: null, requestId: null, retryAfterMs: null, serverType: null, serverCode: null, notice: null });
5135
+ if (model2?.input.includes("image") !== true) {
5136
+ if (sentImages(options.messages).length > 0) this.deps.events.imagesNotSent?.(sessionId2, model2?.name ?? options.model);
5137
+ return { sent: null, ...withAccess };
5138
+ }
5139
+ const limits = this.deps.imageLimits ?? ENGINE_IMAGE_LIMITS;
5140
+ const excess = sentImages(options.messages).length - limits.maxImages;
5141
+ if (excess > 0) {
5142
+ throw new LlmError(`AdaptiqAI takes up to ${limits.maxImages} images per request; the ${excess} oldest ${excess === 1 ? "one goes" : "go"} as a note naming ${excess === 1 ? "it" : "them"}.`, IMAGE_OFFLOAD_REQUIRED_CODE, { offloadImages: excess });
5143
+ }
5144
+ const source = this.deps.attachments?.();
5145
+ if (source === void 0) throw problem("This DeepSeek Harness has no attachment service, so images can't be sent. Update DeepSeek Harness and try again.");
5146
+ try {
5147
+ return { sent: await prepareImages(options.messages, source, limits, signal), ...withAccess };
5148
+ } catch (error) {
5149
+ if (error instanceof ImageProblem) throw problem(error.message);
5150
+ if (signal.aborted) throw new LlmError("Request stopped.", CODES.aborted, { cause: error });
5151
+ throw problem(`An attached image could not be read: ${error instanceof Error ? error.message : String(error)}. Attach it again.`);
5152
+ }
5153
+ }
4965
5154
  async *generate(options) {
4966
5155
  if (options.purpose === "compaction") {
4967
5156
  const message = "AdaptiqAI keeps the whole conversation in native memory, so it never needs compacting. Nothing was removed.";
@@ -5036,7 +5225,8 @@ var AdaptiqAdapter = class extends LlmAdapter {
5036
5225
  if (references.length > 0) recalled = await this.deps.recall(sessionId2, options.model, references, signal).catch(() => NOTHING_RECALLED);
5037
5226
  }
5038
5227
  const parent = options.sessionId === void 0 ? void 0 : this.deps.parentSession?.(String(options.sessionId));
5039
- let body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled }));
5228
+ const images = await this.images(options, model2, sessionId2, fail2, signal);
5229
+ let body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled, images }));
5040
5230
  this.deps.debug?.(`adaptiq: request ${sessionId2 ?? "-"}: ${options.messages.length} messages, ${body.length} bytes, ${recalled.size} read from memory, transcripts sent: ${body.includes("<referenced-sessions>") ? "yes" : "no"}`);
5041
5231
  let response;
5042
5232
  let refreshed = false;
@@ -5077,7 +5267,7 @@ var AdaptiqAdapter = class extends LlmAdapter {
5077
5267
  await response.body?.cancel().catch(() => {
5078
5268
  });
5079
5269
  stored = false;
5080
- body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled }));
5270
+ body = JSON.stringify(buildChatBody(options, model2, { remember, stored, recalled, images }));
5081
5271
  continue;
5082
5272
  }
5083
5273
  break;
@@ -5881,6 +6071,150 @@ ${hash}`).digest("hex").slice(0, 48);
5881
6071
  })), "adaptiq: adaptiq_create_knowledge tool");
5882
6072
  }
5883
6073
 
6074
+ // src/host/release-age.ts
6075
+ import { constants } from "node:fs";
6076
+ import { access, readFile as readFile3, rename as rename2, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
6077
+ import { isAbsolute as isAbsolute2, join } from "node:path";
6078
+ import { randomBytes } from "node:crypto";
6079
+ var KEY = "minimumReleaseAgeExclude";
6080
+ var FILE = "pnpm-workspace.yaml";
6081
+ var PACKAGE_NAME = /^(?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$/u;
6082
+ var TOP_KEY = /^(?:([A-Za-z_][\w-]*)|'([^']*)'|"([^"\\]*)") *:(?: +(.*))?$/u;
6083
+ var ITEM = /^( *)- +(\S.*)$/u;
6084
+ var ENTRY = /^(?:'([^']*)'|"([^"\\]*)"|([\w.][\w@./~^<>=*|+ -]*?)) *(?: #.*)?$/u;
6085
+ var COMMENT = /^ *(?:#.*)?$/u;
6086
+ function singleLineValue(value) {
6087
+ if (value.startsWith('"')) return /^"(?:[^"\\]|\\.)*" *(?:#.*)?$/u.test(value);
6088
+ if (value.startsWith("'")) return /^'(?:[^']|'')*' *(?:#.*)?$/u.test(value);
6089
+ if (value.startsWith("{")) return /^\{[^{}[\]]*\} *(?:#.*)?$/u.test(value);
6090
+ if (value.startsWith("[")) return /^\[[^{}[\]]*\] *(?:#.*)?$/u.test(value);
6091
+ return !/^[|>]/u.test(value) || /^[|>][-+]?\d? *(?:#.*)?$/u.test(value);
6092
+ }
6093
+ function readExcludeList(text2) {
6094
+ if (/[\r\t\uFEFF]/u.test(text2)) return null;
6095
+ const lines = text2.split("\n");
6096
+ if (lines.at(-1) === "") lines.pop();
6097
+ let keyLine = -1;
6098
+ let blockKey = null;
6099
+ let listOpen = false;
6100
+ let blockValue = false;
6101
+ const entries = [];
6102
+ let indent = null;
6103
+ for (const [index, line] of lines.entries()) {
6104
+ if (COMMENT.test(line)) continue;
6105
+ if (line.startsWith(" ")) {
6106
+ if (!listOpen) continue;
6107
+ const item = ITEM.exec(line);
6108
+ if (item === null || indent !== null && item[1] !== indent) return null;
6109
+ const entry = ENTRY.exec(item[2]);
6110
+ if (entry === null) return null;
6111
+ indent = item[1];
6112
+ entries.push({ line: index, value: (entry[1] ?? entry[2] ?? entry[3]).trim() });
6113
+ continue;
6114
+ }
6115
+ if (line.startsWith("- ")) {
6116
+ if (!blockValue) return null;
6117
+ if (listOpen) {
6118
+ if (indent !== null && indent !== "") return null;
6119
+ const entry = ENTRY.exec(line.slice(2).trimStart());
6120
+ if (entry === null) return null;
6121
+ indent = "";
6122
+ entries.push({ line: index, value: (entry[1] ?? entry[2] ?? entry[3]).trim() });
6123
+ }
6124
+ continue;
6125
+ }
6126
+ const key = TOP_KEY.exec(line);
6127
+ if (key === null) return null;
6128
+ const name2 = key[1] ?? key[2] ?? key[3];
6129
+ const value = key[4] ?? "";
6130
+ listOpen = false;
6131
+ blockValue = COMMENT.test(value);
6132
+ if (name2 !== KEY) {
6133
+ if (!blockValue && !singleLineValue(value)) return null;
6134
+ continue;
6135
+ }
6136
+ if (keyLine !== -1) return null;
6137
+ keyLine = index;
6138
+ const flow = /^\[\] *(#.*)?$/u.exec(value);
6139
+ if (flow !== null) blockKey = `${line.slice(0, line.indexOf(":"))}:${flow[1] === void 0 ? "" : ` ${flow[1]}`}`;
6140
+ else if (!blockValue) return null;
6141
+ listOpen = blockValue;
6142
+ }
6143
+ return { lines, keyLine, blockKey, entries, indent: indent ?? " " };
6144
+ }
6145
+ function excludeRelease(text2, name2, version) {
6146
+ if (!PACKAGE_NAME.test(name2) || !RELEASE_VERSION.test(version)) return null;
6147
+ const list = readExcludeList(text2);
6148
+ if (list === null) return null;
6149
+ const spec = `${name2}@${version}`;
6150
+ const ours = list.entries.filter((entry) => entry.value.startsWith(`${name2}@`));
6151
+ if (ours.length === 1 && ours[0].value === spec) return text2;
6152
+ const item = `${list.indent}- '${spec}'`;
6153
+ const drop = new Set(ours.map((entry) => entry.line));
6154
+ const out = [];
6155
+ if (list.keyLine === -1) {
6156
+ out.push(...list.lines, `${KEY}:`, ` - '${spec}'`);
6157
+ } else {
6158
+ const after = list.entries.length > 0 ? list.entries.at(-1).line : list.keyLine;
6159
+ for (const [index, line] of list.lines.entries()) {
6160
+ if (index === list.keyLine && list.blockKey !== null) out.push(list.blockKey);
6161
+ else if (!drop.has(index)) out.push(line);
6162
+ if (index === after) out.push(item);
6163
+ }
6164
+ }
6165
+ return `${out.join("\n")}
6166
+ `;
6167
+ }
6168
+ function profileDir(ctx) {
6169
+ const profile = ctx.get("profileContext");
6170
+ return typeof profile?.dir === "string" && isAbsolute2(profile.dir) ? profile.dir : null;
6171
+ }
6172
+ async function canExcludeRelease(dir, name2) {
6173
+ try {
6174
+ const file = join(dir, FILE);
6175
+ if (excludeRelease(await readFile3(file, "utf8"), name2, "0.0.0") === null) return false;
6176
+ await access(file, constants.W_OK);
6177
+ await access(dir, constants.W_OK);
6178
+ return true;
6179
+ } catch {
6180
+ return false;
6181
+ }
6182
+ }
6183
+ async function writeReleaseExclusion(dir, name2, version, waitMs = 5e3) {
6184
+ const file = join(dir, FILE);
6185
+ const lock = join(dir, "package.json.lock");
6186
+ const deadline = Date.now() + waitMs;
6187
+ for (; ; ) {
6188
+ try {
6189
+ await writeFile2(lock, `${process.pid}
6190
+ `, { mode: 384, flag: "wx" });
6191
+ break;
6192
+ } catch (error) {
6193
+ if (error.code !== "EEXIST" || Date.now() >= deadline) return false;
6194
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
6195
+ }
6196
+ }
6197
+ try {
6198
+ const before = await readFile3(file, "utf8");
6199
+ const after = excludeRelease(before, name2, version);
6200
+ if (after === null) return false;
6201
+ if (after === before) return true;
6202
+ const temp = `${file}.${randomBytes(6).toString("hex")}.tmp`;
6203
+ try {
6204
+ await writeFile2(temp, after, { mode: (await stat2(file)).mode & 511, flag: "wx" });
6205
+ await rename2(temp, file);
6206
+ } catch (error) {
6207
+ await rm(temp, { force: true });
6208
+ throw error;
6209
+ }
6210
+ return true;
6211
+ } catch {
6212
+ return false;
6213
+ } finally {
6214
+ await rm(lock, { force: true });
6215
+ }
6216
+ }
6217
+
5884
6218
  // src/host/updater.ts
5885
6219
  function pluginManager(ctx) {
5886
6220
  const service = ctx.get("pluginManager");
@@ -5905,6 +6239,10 @@ function harnessUpdater(ctx, name2, runtime) {
5905
6239
  get canInstall() {
5906
6240
  return pluginManager(ctx) !== void 0;
5907
6241
  },
6242
+ async installsFreshReleases() {
6243
+ const dir = profileDir(ctx);
6244
+ return pluginManager(ctx) !== void 0 && dir !== null && await canExcludeRelease(dir, name2);
6245
+ },
5908
6246
  async installed() {
5909
6247
  const local = runtime?.installedVersion() ?? null;
5910
6248
  if (local !== null) return local;
@@ -5915,6 +6253,8 @@ function harnessUpdater(ctx, name2, runtime) {
5915
6253
  if (!RELEASE_VERSION.test(version)) throw new UpdateFailure("failed", `${version} is not a release version`);
5916
6254
  const manager = pluginManager(ctx);
5917
6255
  if (manager === void 0) throw new UpdateFailure("failed", "this harness has no plugin manager");
6256
+ const dir = profileDir(ctx);
6257
+ if (dir !== null) await writeReleaseExclusion(dir, name2, version);
5918
6258
  const result = await manager.installBundle(`${name2}@${version}`, { enabled: true });
5919
6259
  if (result.application === "failed" || result.application === "cancelled" || result.error !== void 0) throw installFailure(result);
5920
6260
  },
@@ -5933,7 +6273,7 @@ Open **Settings \u2192 AdaptiqAI** to connect, or run \`/adaptiq signin\`.
5933
6273
  var ENTRY_PROTOCOL = 1;
5934
6274
  var RELOAD_SIGNATURE = true ? "ada3dfffa1b3d7b1" : "source";
5935
6275
  function pluginVersion() {
5936
- if (true) return "0.2.11";
6276
+ if (true) return "0.2.13";
5937
6277
  try {
5938
6278
  return createRequire(import.meta.url)("../package.json").version;
5939
6279
  } catch {
@@ -5956,14 +6296,25 @@ async function harnessSessions(ctx) {
5956
6296
  return new Map(records.map((record) => {
5957
6297
  let title = null;
5958
6298
  try {
5959
- const label = typeof labels?.projectedLabels === "function" ? labels.projectedLabels(record).label : void 0;
5960
- if (typeof label === "string" && label.trim() !== "" && label !== record.header.id) title = label.trim();
6299
+ const label2 = typeof labels?.projectedLabels === "function" ? labels.projectedLabels(record).label : void 0;
6300
+ if (typeof label2 === "string" && label2.trim() !== "" && label2 !== record.header.id) title = label2.trim();
5961
6301
  } catch {
5962
6302
  }
5963
6303
  const subagent = record.header.origin === "subagent" || (record.header.delegationDepth ?? 0) > 0;
5964
6304
  return [record.header.id, { title, subagent }];
5965
6305
  }));
5966
6306
  }
6307
+ function attachmentService(ctx) {
6308
+ const service = ctx.get("attachments");
6309
+ return typeof service?.readImageRequest === "function" ? service : void 0;
6310
+ }
6311
+ function imageAccess(ctx, ref) {
6312
+ const host = attachmentService(ctx)?.imageHostPath?.(ref);
6313
+ if (host === void 0) return void 0;
6314
+ const fs = ctx.get("fs");
6315
+ const readonlyPath = fs?.processPathFromHostPath?.(host);
6316
+ return readonlyPath === void 0 ? void 0 : { readonlyPath };
6317
+ }
5967
6318
  var VERSION = pluginVersion();
5968
6319
  async function apply(ctx, config) {
5969
6320
  await applyWith(ctx, config, void 0);
@@ -5988,8 +6339,8 @@ async function applyWith(ctx, raw, runtime) {
5988
6339
  onChange: () => {
5989
6340
  workspace?.authChanged();
5990
6341
  },
5991
- onSignedIn: (label) => {
5992
- workspace?.signedIn(label);
6342
+ onSignedIn: (label2) => {
6343
+ workspace?.signedIn(label2);
5993
6344
  }
5994
6345
  });
5995
6346
  const api = new AdaptiqApi(apiBase, auth, () => ({
@@ -6071,6 +6422,8 @@ async function applyWith(ctx, raw, runtime) {
6071
6422
  attachPending: (sessionId2) => owned.attachPending(sessionId2),
6072
6423
  events: owned,
6073
6424
  autoSleep: (model2) => owned.autoSleep(model2),
6425
+ attachments: () => attachmentService(ctx),
6426
+ imageAccess: (ref) => imageAccess(ctx, ref),
6074
6427
  ...process.env.DSH_ADAPTIQ_DEBUG_LOG === void 0 || process.env.DSH_ADAPTIQ_DEBUG_LOG === "" ? {} : { debug: (message) => {
6075
6428
  log("info", message);
6076
6429
  } }