@moxt-ai/mobius 0.0.27 → 0.0.28

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 (36) hide show
  1. package/dist/acp/activity.d.ts +1 -0
  2. package/dist/acp/activity.d.ts.map +1 -1
  3. package/dist/acp/activity.js +14 -1
  4. package/dist/acp/activity.js.map +1 -1
  5. package/dist/acp/session-history.d.ts.map +1 -1
  6. package/dist/acp/session-history.js +10 -16
  7. package/dist/acp/session-history.js.map +1 -1
  8. package/dist/acp/session-manager.d.ts +7 -0
  9. package/dist/acp/session-manager.d.ts.map +1 -1
  10. package/dist/acp/session-manager.js +65 -11
  11. package/dist/acp/session-manager.js.map +1 -1
  12. package/dist/artifacts/runtime-artifact-uploader.d.ts +12 -0
  13. package/dist/artifacts/runtime-artifact-uploader.d.ts.map +1 -0
  14. package/dist/artifacts/runtime-artifact-uploader.js +157 -0
  15. package/dist/artifacts/runtime-artifact-uploader.js.map +1 -0
  16. package/dist/authorization/local-raster.d.ts +10 -0
  17. package/dist/authorization/local-raster.d.ts.map +1 -0
  18. package/dist/authorization/local-raster.js +134 -0
  19. package/dist/authorization/local-raster.js.map +1 -0
  20. package/dist/cli.js +826 -113
  21. package/dist/cli.js.map +4 -4
  22. package/dist/index.js +849 -137
  23. package/dist/index.js.map +4 -4
  24. package/dist/native-sessions/codex-history.d.ts.map +1 -1
  25. package/dist/native-sessions/codex-history.js +11 -11
  26. package/dist/native-sessions/codex-history.js.map +1 -1
  27. package/dist/native-sessions/history-images.d.ts +3 -0
  28. package/dist/native-sessions/history-images.d.ts.map +1 -0
  29. package/dist/native-sessions/history-images.js +74 -0
  30. package/dist/native-sessions/history-images.js.map +1 -0
  31. package/dist/run-daemon.d.ts.map +1 -1
  32. package/dist/run-daemon.js +30 -7
  33. package/dist/run-daemon.js.map +1 -1
  34. package/dist/runtime.js +845 -133
  35. package/dist/runtime.js.map +4 -4
  36. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import { mkdir as mkdir5 } from "node:fs/promises";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@moxt-ai/mobius",
14
- version: "0.0.27",
14
+ version: "0.0.28",
15
15
  description: "Mobius CLI for pairing and running local AI agent services.",
16
16
  type: "module",
17
17
  bin: {
@@ -55,6 +55,7 @@ var package_default = {
55
55
  },
56
56
  devDependencies: {
57
57
  "@moxt-ai/mobius-protocol": "workspace:*",
58
+ "@moxt-ai/mobius-sdk": "workspace:*",
58
59
  "@types/node": "^24.13.3",
59
60
  esbuild: "0.28.2"
60
61
  },
@@ -18841,8 +18842,8 @@ function rewriteKeyNames(ctx) {
18841
18842
  bySchema.set(entry.schema, entry);
18842
18843
  }
18843
18844
  const rewrites = /* @__PURE__ */ new Map();
18844
- for (const record2 of pendingRecords.get(ctx) ?? []) {
18845
- const seen = ctx.seen.get(record2);
18845
+ for (const record3 of pendingRecords.get(ctx) ?? []) {
18846
+ const seen = ctx.seen.get(record3);
18846
18847
  const names = (seen?.def ?? seen?.schema)?.propertyNames;
18847
18848
  if (!names || names === true || rewrites.has(names))
18848
18849
  continue;
@@ -21799,6 +21800,7 @@ var elicitationFieldSchema = external_exports.strictObject({
21799
21800
  type: external_exports.enum(["boolean", "integer", "multiselect", "number", "select", "string"])
21800
21801
  }).readonly();
21801
21802
  var displayContentSchema = external_exports.strictObject({
21803
+ artifactId: external_exports.string().max(128).default(""),
21802
21804
  data: external_exports.string().max(4e6),
21803
21805
  mimeType: external_exports.string().max(255),
21804
21806
  name: external_exports.string().max(500),
@@ -21811,6 +21813,7 @@ var displayContentSchema = external_exports.strictObject({
21811
21813
  uri: external_exports.string().max(4096)
21812
21814
  }).readonly();
21813
21815
  var recordedDisplayContentSchema = external_exports.strictObject({
21816
+ artifactId: external_exports.string().max(128).default(""),
21814
21817
  data: external_exports.string().max(4e6),
21815
21818
  mimeType: external_exports.string().max(255),
21816
21819
  name: external_exports.string().max(500),
@@ -23681,7 +23684,7 @@ var AuditStore = class {
23681
23684
  this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
23682
23685
  }
23683
23686
  }
23684
- record(record2) {
23687
+ record(record3) {
23685
23688
  this.database.prepare(
23686
23689
  `
23687
23690
  INSERT INTO audit_events (
@@ -23694,10 +23697,10 @@ var AuditStore = class {
23694
23697
  `
23695
23698
  ).run(
23696
23699
  (/* @__PURE__ */ new Date()).toISOString(),
23697
- record2.event,
23698
- record2.commandId,
23699
- record2.sessionId,
23700
- JSON.stringify(record2.details ?? {})
23700
+ record3.event,
23701
+ record3.commandId,
23702
+ record3.sessionId,
23703
+ JSON.stringify(record3.details ?? {})
23701
23704
  );
23702
23705
  }
23703
23706
  close() {
@@ -23707,9 +23710,9 @@ var AuditStore = class {
23707
23710
 
23708
23711
  // src/run-daemon.ts
23709
23712
  import { randomUUID as randomUUID6 } from "node:crypto";
23710
- import { basename as basename4, join as join16 } from "node:path";
23713
+ import { basename as basename5, join as join16 } from "node:path";
23711
23714
  import { styleText } from "node:util";
23712
- import { fetch as fetch6, WebSocket as WebSocket2 } from "undici";
23715
+ import { fetch as fetch7, WebSocket as WebSocket2 } from "undici";
23713
23716
 
23714
23717
  // src/acp/session-manager.ts
23715
23718
  import { spawn as spawn3 } from "node:child_process";
@@ -23720,6 +23723,212 @@ import {
23720
23723
  RequestError as RequestError2
23721
23724
  } from "@agentclientprotocol/sdk";
23722
23725
 
23726
+ // src/authorization/local-raster.ts
23727
+ import { readFile, realpath as realpath7, stat as stat6 } from "node:fs/promises";
23728
+ import { basename as basename2, isAbsolute as isAbsolute7, normalize as normalize2, relative as relative4, sep as sep4 } from "node:path";
23729
+ import { fileURLToPath } from "node:url";
23730
+ var MAX_LOCAL_RASTER_BYTES = 3e6;
23731
+ var PNG_SIGNATURE = Uint8Array.of(137, 80, 78, 71);
23732
+ var JPEG_SIGNATURE = Uint8Array.of(255, 216, 255);
23733
+ var GIF87_SIGNATURE = new TextEncoder().encode("GIF87a");
23734
+ var GIF89_SIGNATURE = new TextEncoder().encode("GIF89a");
23735
+ var RIFF_SIGNATURE = new TextEncoder().encode("RIFF");
23736
+ var WEBP_SIGNATURE = new TextEncoder().encode("WEBP");
23737
+ var LocalRasterImage = class {
23738
+ bytes;
23739
+ mimeType;
23740
+ name;
23741
+ constructor(name, mimeType, bytes) {
23742
+ this.bytes = bytes;
23743
+ this.mimeType = mimeType;
23744
+ this.name = name;
23745
+ }
23746
+ base64 = () => Buffer.from(this.bytes).toString("base64");
23747
+ };
23748
+ function startsWith(bytes, signature, offset = 0) {
23749
+ if (bytes.byteLength < offset + signature.byteLength) {
23750
+ return false;
23751
+ }
23752
+ return signature.every((value, index) => bytes[offset + index] === value);
23753
+ }
23754
+ function sniffRasterMime(bytes) {
23755
+ if (startsWith(bytes, PNG_SIGNATURE)) {
23756
+ return "image/png";
23757
+ }
23758
+ if (startsWith(bytes, JPEG_SIGNATURE)) {
23759
+ return "image/jpeg";
23760
+ }
23761
+ if (startsWith(bytes, GIF87_SIGNATURE) || startsWith(bytes, GIF89_SIGNATURE)) {
23762
+ return "image/gif";
23763
+ }
23764
+ if (startsWith(bytes, RIFF_SIGNATURE) && startsWith(bytes, WEBP_SIGNATURE, 8)) {
23765
+ return "image/webp";
23766
+ }
23767
+ return null;
23768
+ }
23769
+ function isInsideRoot(root, target) {
23770
+ const relation = relative4(root, target);
23771
+ return relation !== ".." && !relation.startsWith(`..${sep4}`) && !isAbsolute7(relation);
23772
+ }
23773
+ async function resolveCanonicalPath(requested, signal) {
23774
+ signal.throwIfAborted();
23775
+ if (!isAbsolute7(requested) || requested.includes("\0") || normalize2(requested) !== requested) {
23776
+ return null;
23777
+ }
23778
+ try {
23779
+ const target = await realpath7(requested);
23780
+ signal.throwIfAborted();
23781
+ if (!isAbsolute7(target) || normalize2(target) !== target) {
23782
+ return null;
23783
+ }
23784
+ return target;
23785
+ } catch (cause) {
23786
+ signal.throwIfAborted();
23787
+ if (cause instanceof Error && cause.name === "AbortError") {
23788
+ throw cause;
23789
+ }
23790
+ return null;
23791
+ }
23792
+ }
23793
+ function parseLocalImageUri(uri) {
23794
+ const trimmed = uri.trim();
23795
+ if (trimmed.length === 0 || trimmed.length > 2048 || trimmed.includes("\0")) {
23796
+ return null;
23797
+ }
23798
+ if (trimmed.startsWith("data:")) {
23799
+ return null;
23800
+ }
23801
+ if (trimmed.startsWith("file:")) {
23802
+ try {
23803
+ const url2 = new URL(trimmed);
23804
+ if (url2.protocol !== "file:" || url2.host.length > 0) {
23805
+ return null;
23806
+ }
23807
+ return fileURLToPath(url2);
23808
+ } catch {
23809
+ return null;
23810
+ }
23811
+ }
23812
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
23813
+ return null;
23814
+ }
23815
+ return trimmed;
23816
+ }
23817
+ async function readAuthorizedRaster(uri, roots, signal) {
23818
+ signal.throwIfAborted();
23819
+ const requested = parseLocalImageUri(uri);
23820
+ if (requested === null) {
23821
+ return null;
23822
+ }
23823
+ const target = await resolveCanonicalPath(requested, signal);
23824
+ if (target === null) {
23825
+ return null;
23826
+ }
23827
+ const targetMetadata = await stat6(target);
23828
+ signal.throwIfAborted();
23829
+ if (!targetMetadata.isFile() || targetMetadata.size < 1 || targetMetadata.size > MAX_LOCAL_RASTER_BYTES) {
23830
+ return null;
23831
+ }
23832
+ const authorizedRoots = [];
23833
+ for (const root of roots) {
23834
+ const directory = await resolveCanonicalPath(root, signal);
23835
+ if (directory === null) {
23836
+ continue;
23837
+ }
23838
+ const metadata = await stat6(directory);
23839
+ signal.throwIfAborted();
23840
+ if (metadata.isDirectory()) {
23841
+ authorizedRoots.push(directory);
23842
+ }
23843
+ }
23844
+ if (!authorizedRoots.some((root) => isInsideRoot(root, target))) {
23845
+ return null;
23846
+ }
23847
+ const bytes = new Uint8Array(await readFile(target));
23848
+ signal.throwIfAborted();
23849
+ if (bytes.byteLength < 1 || bytes.byteLength > MAX_LOCAL_RASTER_BYTES) {
23850
+ return null;
23851
+ }
23852
+ const mimeType = sniffRasterMime(bytes);
23853
+ if (mimeType === null) {
23854
+ return null;
23855
+ }
23856
+ return new LocalRasterImage(basename2(target) || "Image", mimeType, bytes);
23857
+ }
23858
+
23859
+ // src/native-sessions/history-images.ts
23860
+ var IMAGE_MARKER = /\[@image\]\(([^)\r\n]*)(?:\)|$)/g;
23861
+ async function embedLocalHistoryImages(history, roots, signal) {
23862
+ if (history.status !== "available") {
23863
+ return history;
23864
+ }
23865
+ const messages = [];
23866
+ let imageCharacters = 0;
23867
+ for (const message of history.messages) {
23868
+ signal.throwIfAborted();
23869
+ const resolved = await resolveMessageImages(message, roots, imageCharacters, signal);
23870
+ imageCharacters += (resolved.images ?? []).reduce((size, image) => size + image.data.length, 0);
23871
+ messages.push(resolved);
23872
+ }
23873
+ return {
23874
+ ...history,
23875
+ messages
23876
+ };
23877
+ }
23878
+ async function resolveMessageImages(message, roots, usedCharacters, signal) {
23879
+ const images = [...message.images ?? []];
23880
+ let imageCharacters = images.reduce((size, image) => size + image.data.length, 0);
23881
+ const text = await replaceImageMarkers(message.text, async (uri) => {
23882
+ if (images.length >= 8 || usedCharacters + imageCharacters >= SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
23883
+ return "[Image unavailable]";
23884
+ }
23885
+ const embedded = parseNativeHistoryText(`[@image](${uri})`).images[0];
23886
+ if (embedded !== void 0) {
23887
+ if (usedCharacters + imageCharacters + embedded.data.length > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
23888
+ return "[Image unavailable]";
23889
+ }
23890
+ images.push(embedded);
23891
+ imageCharacters += embedded.data.length;
23892
+ return "";
23893
+ }
23894
+ const local = await readAuthorizedRaster(uri, roots, signal);
23895
+ if (local === null) {
23896
+ return "[Image unavailable]";
23897
+ }
23898
+ const data = local.base64();
23899
+ if (usedCharacters + imageCharacters + data.length > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
23900
+ return "[Image unavailable]";
23901
+ }
23902
+ images.push({
23903
+ data,
23904
+ mimeType: local.mimeType,
23905
+ name: local.name
23906
+ });
23907
+ imageCharacters += data.length;
23908
+ return "";
23909
+ });
23910
+ return {
23911
+ ...message,
23912
+ text,
23913
+ ...images.length > 0 ? { images } : {}
23914
+ };
23915
+ }
23916
+ async function replaceImageMarkers(text, replace) {
23917
+ const matches = [...text.matchAll(IMAGE_MARKER)];
23918
+ if (matches.length === 0) {
23919
+ return text;
23920
+ }
23921
+ let cursor = 0;
23922
+ let rewritten = "";
23923
+ for (const match of matches) {
23924
+ const index = match.index ?? 0;
23925
+ rewritten += text.slice(cursor, index);
23926
+ rewritten += await replace(match[1] ?? "");
23927
+ cursor = index + match[0].length;
23928
+ }
23929
+ return `${rewritten}${text.slice(cursor)}`;
23930
+ }
23931
+
23723
23932
  // src/acp/activity.ts
23724
23933
  var TRUNCATION_MARKER = "\n\u2026 output truncated by Mobius";
23725
23934
  function isRecord8(value) {
@@ -23817,6 +24026,7 @@ function readContentOutputDetail(content) {
23817
24026
  function summarizeDisplayContent(content) {
23818
24027
  return {
23819
24028
  ...content,
24029
+ artifactId: truncate(content.artifactId, 128),
23820
24030
  data: truncate(content.data, AGENT_ACTIVITY_DETAIL_LIMIT),
23821
24031
  mimeType: truncate(content.mimeType, 255),
23822
24032
  name: truncate(content.name, 500),
@@ -23829,7 +24039,7 @@ function summarizeDisplayContent(content) {
23829
24039
  };
23830
24040
  }
23831
24041
  function contentFitsRecordingEnvelope(content) {
23832
- return content.data.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.mimeType.length <= 255 && content.name.length <= 500 && content.newText.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.oldText.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.path.length <= 2048 && content.terminalId.length <= 255 && content.text.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.uri.length <= 4096;
24042
+ return content.artifactId.length <= 128 && content.data.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.mimeType.length <= 255 && content.name.length <= 500 && content.newText.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.oldText.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.path.length <= 2048 && content.terminalId.length <= 255 && content.text.length <= AGENT_ACTIVITY_RECORDED_FIELD_LIMIT && content.uri.length <= 4096;
23833
24043
  }
23834
24044
  function encodedByteLength(value) {
23835
24045
  return new TextEncoder().encode(JSON.stringify(value)).byteLength;
@@ -23855,6 +24065,7 @@ function recordedDetail(activity) {
23855
24065
  }
23856
24066
  function emptyDisplayContent(type) {
23857
24067
  return {
24068
+ artifactId: "",
23858
24069
  data: "",
23859
24070
  mimeType: "",
23860
24071
  name: "",
@@ -23867,6 +24078,16 @@ function emptyDisplayContent(type) {
23867
24078
  uri: ""
23868
24079
  };
23869
24080
  }
24081
+ function imageDisplayContent(mimeType, name, data, artifactId = "", uri = "") {
24082
+ return {
24083
+ ...emptyDisplayContent("image"),
24084
+ artifactId,
24085
+ data,
24086
+ mimeType,
24087
+ name,
24088
+ uri
24089
+ };
24090
+ }
23870
24091
  function normalizeContentBlock(content) {
23871
24092
  if (content.type === "text") {
23872
24093
  return { ...emptyDisplayContent("text"), text: content.text };
@@ -24658,7 +24879,6 @@ var AcpHistoryReplay = class {
24658
24879
  #messages = [];
24659
24880
  #lastMessageId = null;
24660
24881
  #characters = 0;
24661
- #imageCharacters = 0;
24662
24882
  #truncated = false;
24663
24883
  record = (update) => {
24664
24884
  if (update.sessionUpdate !== "user_message_chunk" && update.sessionUpdate !== "agent_message_chunk") {
@@ -24669,40 +24889,30 @@ var AcpHistoryReplay = class {
24669
24889
  }
24670
24890
  const role = update.sessionUpdate === "user_message_chunk" ? "user" : "assistant";
24671
24891
  const content = update.content;
24672
- const parsed = parseNativeHistoryText(
24673
- content.type === "text" ? content.text : content.type === "image" ? `[@image](data:${content.mimeType};base64,${content.data})` : `[${content.type} attachment]`
24674
- );
24675
- const text = parsed.text;
24676
- if (text.length === 0 && parsed.images.length === 0) {
24892
+ const text = content.type === "text" ? content.text : content.type === "image" ? content.data.length > 0 ? `[@image](data:${content.mimeType};base64,${content.data})` : content.uri != null && content.uri.length > 0 ? `[@image](${content.uri})` : "[image attachment]" : `[${content.type} attachment]`;
24893
+ if (text.length === 0) {
24677
24894
  return;
24678
24895
  }
24679
24896
  const previous = this.#messages.at(-1);
24680
24897
  const id = update.messageId ?? null;
24681
24898
  const continuation = previous?.role === role && id === this.#lastMessageId;
24682
24899
  const combined = continuation ? `${previous.text}${text}` : text;
24683
- const allImages = continuation ? [...previous.images ?? [], ...parsed.images] : parsed.images;
24684
- const images = allImages.slice(0, 8);
24685
- this.#truncated ||= images.length < allImages.length;
24686
24900
  if (continuation) {
24687
24901
  this.#messages.pop();
24688
24902
  this.#characters -= previous.text.length;
24689
- this.#imageCharacters -= (previous.images ?? []).reduce((size, image) => size + image.data.length, 0);
24690
24903
  }
24691
24904
  const bounded = combined.slice(-SESSION_HISTORY_CHARACTER_LIMIT);
24692
24905
  this.#truncated ||= bounded.length < combined.length;
24693
24906
  this.#messages.push({
24694
24907
  role,
24695
- text: bounded,
24696
- ...images.length > 0 ? { images } : {}
24908
+ text: bounded
24697
24909
  });
24698
24910
  this.#characters += bounded.length;
24699
- this.#imageCharacters += images.reduce((size, image) => size + image.data.length, 0);
24700
24911
  this.#lastMessageId = id;
24701
- while (this.#messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || this.#characters > SESSION_HISTORY_CHARACTER_LIMIT || this.#imageCharacters > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
24912
+ while (this.#messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || this.#characters > SESSION_HISTORY_CHARACTER_LIMIT) {
24702
24913
  const removed = this.#messages.shift();
24703
24914
  if (removed !== void 0) {
24704
24915
  this.#characters -= removed.text.length;
24705
- this.#imageCharacters -= (removed.images ?? []).reduce((size, image) => size + image.data.length, 0);
24706
24916
  }
24707
24917
  this.#truncated = true;
24708
24918
  }
@@ -24925,6 +25135,10 @@ var MAX_PRE_SESSION_UPDATES = 100;
24925
25135
  var MAX_PRE_SESSION_UPDATE_BYTES = 1024 * 1024;
24926
25136
  var SESSION_CONTROL_COMMAND_ID = "session-control";
24927
25137
  var NullAcpSessionObserver = class {
25138
+ importLocalImage = (_sessionId, _image, signal) => {
25139
+ signal.throwIfAborted();
25140
+ return Promise.resolve("");
25141
+ };
24928
25142
  onCommands = () => Promise.resolve();
24929
25143
  onConfig = () => Promise.resolve();
24930
25144
  onContent = () => Promise.resolve();
@@ -25547,11 +25761,75 @@ var AcpProcessSession = class _AcpProcessSession {
25547
25761
  );
25548
25762
  await this.#updateTail;
25549
25763
  signal.throwIfAborted();
25550
- return history.snapshot();
25764
+ return await embedLocalHistoryImages(
25765
+ history.snapshot(),
25766
+ [this.#cwd, ...this.#additionalDirectories],
25767
+ signal
25768
+ );
25551
25769
  } finally {
25552
25770
  this.#replay = { kind: "idle" };
25553
25771
  }
25554
25772
  };
25773
+ imageRoots = () => [this.#cwd, ...this.#additionalDirectories];
25774
+ emitAgentText = async (text, messageId) => {
25775
+ const signal = AbortSignal.timeout(2e4);
25776
+ const parts = text.split(/(\[@image\]\([^)\r\n]*(?:\)|$))/);
25777
+ let leftover = "";
25778
+ for (const part of parts) {
25779
+ const marker = /^\[@image\]\(([^)\r\n]*)(?:\)|$)/.exec(part);
25780
+ if (marker === null) {
25781
+ leftover += part;
25782
+ continue;
25783
+ }
25784
+ if (leftover.length > 0) {
25785
+ await this.#turn.onText(leftover, messageId);
25786
+ leftover = "";
25787
+ }
25788
+ const raster = await readAuthorizedRaster(marker[1] ?? "", this.imageRoots(), signal);
25789
+ if (raster === null) {
25790
+ leftover += "[Image unavailable]";
25791
+ continue;
25792
+ }
25793
+ await this.emitRaster(raster, messageId, signal);
25794
+ }
25795
+ if (leftover.length > 0) {
25796
+ await this.#turn.onText(leftover, messageId);
25797
+ }
25798
+ };
25799
+ emitAgentContent = async (content, messageId) => {
25800
+ if (content.type === "image" && content.data.length === 0 && content.uri.length > 0) {
25801
+ const signal = AbortSignal.timeout(2e4);
25802
+ const raster = await readAuthorizedRaster(content.uri, this.imageRoots(), signal);
25803
+ if (raster !== null) {
25804
+ await this.emitRaster(raster, messageId, signal);
25805
+ return;
25806
+ }
25807
+ }
25808
+ await this.#getObserver().onContent({
25809
+ commandId: this.#turn.commandId,
25810
+ content,
25811
+ contentId: `content-${crypto.randomUUID()}`,
25812
+ kind: AGENT_CONTENT_KIND,
25813
+ messageId,
25814
+ sessionId: this.#mobiusSessionId
25815
+ });
25816
+ };
25817
+ emitRaster = async (raster, messageId, signal) => {
25818
+ const artifactId = await this.#getObserver().importLocalImage(this.#mobiusSessionId, raster, signal);
25819
+ await this.#getObserver().onContent({
25820
+ commandId: this.#turn.commandId,
25821
+ content: imageDisplayContent(
25822
+ raster.mimeType,
25823
+ raster.name,
25824
+ artifactId.length > 0 ? "" : raster.base64(),
25825
+ artifactId
25826
+ ),
25827
+ contentId: `content-${crypto.randomUUID()}`,
25828
+ kind: AGENT_CONTENT_KIND,
25829
+ messageId,
25830
+ sessionId: this.#mobiusSessionId
25831
+ });
25832
+ };
25555
25833
  async #initializeAgent(signal) {
25556
25834
  const initialized = await this.race(
25557
25835
  this.#connection.agent.request(
@@ -26254,18 +26532,14 @@ var AcpProcessSession = class _AcpProcessSession {
26254
26532
  );
26255
26533
  } else {
26256
26534
  await this.#turn.activities.finishNarrative();
26257
- await this.#turn.onText(update.content.text, truncate3(update.messageId ?? "", 255));
26535
+ await this.emitAgentText(update.content.text, truncate3(update.messageId ?? "", 255));
26258
26536
  }
26259
26537
  } else {
26260
26538
  await this.#turn.activities.finishNarrative();
26261
- await this.#getObserver().onContent({
26262
- commandId: this.#turn.commandId,
26263
- content: normalizeContentBlock(update.content),
26264
- contentId: `content-${crypto.randomUUID()}`,
26265
- kind: AGENT_CONTENT_KIND,
26266
- messageId: truncate3(update.messageId ?? "", 255),
26267
- sessionId: this.#mobiusSessionId
26268
- });
26539
+ await this.emitAgentContent(
26540
+ normalizeContentBlock(update.content),
26541
+ truncate3(update.messageId ?? "", 255)
26542
+ );
26269
26543
  }
26270
26544
  return;
26271
26545
  }
@@ -27626,6 +27900,402 @@ var FixedInstallationExecutor = class {
27626
27900
  };
27627
27901
  };
27628
27902
 
27903
+ // src/artifacts/runtime-artifact-uploader.ts
27904
+ import { createHash as createHash2 } from "node:crypto";
27905
+
27906
+ // ../mobius-sdk/src/errors.ts
27907
+ var MobiusError = class extends Error {
27908
+ code;
27909
+ constructor(code, message, options = {}) {
27910
+ super(message, options);
27911
+ this.code = code;
27912
+ this.name = "MobiusError";
27913
+ }
27914
+ };
27915
+ var MobiusProtocolError = class extends MobiusError {
27916
+ constructor(message, options = {}) {
27917
+ super("invalid_service_response", message, options);
27918
+ this.name = "MobiusProtocolError";
27919
+ }
27920
+ };
27921
+ var MobiusValidationError = class extends MobiusError {
27922
+ constructor(message, options = {}) {
27923
+ super("invalid_request", message, options);
27924
+ this.name = "MobiusValidationError";
27925
+ }
27926
+ };
27927
+
27928
+ // ../mobius-sdk/src/resources.ts
27929
+ var IDENTIFIER_PATTERN6 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
27930
+ function invalidResponse(message) {
27931
+ return new MobiusProtocolError(message);
27932
+ }
27933
+ function isRecord11(value) {
27934
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27935
+ }
27936
+ function record2(value, name) {
27937
+ if (!isRecord11(value)) {
27938
+ throw invalidResponse(`The service returned an invalid ${name}`);
27939
+ }
27940
+ return value;
27941
+ }
27942
+ function stringField(value, field, maximumLength = 85e5) {
27943
+ const candidate = value[field];
27944
+ if (typeof candidate !== "string" || candidate.length > maximumLength) {
27945
+ throw invalidResponse(`The service returned an invalid ${field}`);
27946
+ }
27947
+ return candidate;
27948
+ }
27949
+ function identifierField(value, field) {
27950
+ const candidate = stringField(value, field, 128);
27951
+ if (!IDENTIFIER_PATTERN6.test(candidate)) {
27952
+ throw invalidResponse(`The service returned an invalid ${field}`);
27953
+ }
27954
+ return candidate;
27955
+ }
27956
+ function integerField(value, field) {
27957
+ const candidate = value[field];
27958
+ if (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 0) {
27959
+ throw invalidResponse(`The service returned an invalid ${field}`);
27960
+ }
27961
+ return candidate;
27962
+ }
27963
+ function validateTimestamp(value, field) {
27964
+ if (Number.isNaN(Date.parse(value))) {
27965
+ throw invalidResponse(`The service returned an invalid ${field}`);
27966
+ }
27967
+ return value;
27968
+ }
27969
+ var MobiusArtifactUploadRequest = class {
27970
+ byteLength;
27971
+ mediaType;
27972
+ name;
27973
+ sha256;
27974
+ constructor(name, mediaType, byteLength, sha256) {
27975
+ if (name.length < 1 || name.length > 500 || name.includes("\0") || mediaType.length < 1 || mediaType.length > 255 || mediaType.includes("\r") || mediaType.includes("\n") || !Number.isSafeInteger(byteLength) || byteLength < 1 || !/^[a-f0-9]{64}$/.test(sha256)) {
27976
+ throw new MobiusValidationError("The artifact upload request is invalid");
27977
+ }
27978
+ this.byteLength = byteLength;
27979
+ this.mediaType = mediaType;
27980
+ this.name = name;
27981
+ this.sha256 = sha256;
27982
+ }
27983
+ };
27984
+ var MobiusArtifact = class {
27985
+ artifactId;
27986
+ byteLength;
27987
+ createdAt;
27988
+ mediaType;
27989
+ name;
27990
+ sessionId;
27991
+ sha256;
27992
+ constructor(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt) {
27993
+ this.artifactId = artifactId;
27994
+ this.byteLength = byteLength;
27995
+ this.createdAt = createdAt;
27996
+ this.mediaType = mediaType;
27997
+ this.name = name;
27998
+ this.sessionId = sessionId;
27999
+ this.sha256 = sha256;
28000
+ }
28001
+ };
28002
+ var MobiusUploadingArtifact = class extends MobiusArtifact {
28003
+ expiresAt;
28004
+ state = "uploading";
28005
+ constructor(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt, expiresAt) {
28006
+ super(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt);
28007
+ this.expiresAt = expiresAt;
28008
+ }
28009
+ };
28010
+ var MobiusArtifactUpload = class extends MobiusUploadingArtifact {
28011
+ uploadToken;
28012
+ constructor(artifact, uploadToken) {
28013
+ super(
28014
+ artifact.sessionId,
28015
+ artifact.artifactId,
28016
+ artifact.name,
28017
+ artifact.mediaType,
28018
+ artifact.byteLength,
28019
+ artifact.sha256,
28020
+ artifact.createdAt,
28021
+ artifact.expiresAt
28022
+ );
28023
+ this.uploadToken = uploadToken;
28024
+ }
28025
+ };
28026
+ var MobiusAvailableArtifact = class extends MobiusArtifact {
28027
+ availableAt;
28028
+ state = "available";
28029
+ constructor(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt, availableAt) {
28030
+ super(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt);
28031
+ this.availableAt = availableAt;
28032
+ }
28033
+ };
28034
+ var MobiusExpiredArtifact = class extends MobiusArtifact {
28035
+ expiredAt;
28036
+ state = "expired";
28037
+ constructor(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt, expiredAt) {
28038
+ super(sessionId, artifactId, name, mediaType, byteLength, sha256, createdAt);
28039
+ this.expiredAt = expiredAt;
28040
+ }
28041
+ };
28042
+ function artifactFields(value) {
28043
+ const sha256 = stringField(value, "sha256", 64);
28044
+ if (!/^[a-f0-9]{64}$/.test(sha256)) {
28045
+ throw invalidResponse("The service returned an invalid artifact digest");
28046
+ }
28047
+ return {
28048
+ artifactId: identifierField(value, "artifactId"),
28049
+ byteLength: integerField(value, "byteLength"),
28050
+ createdAt: validateTimestamp(stringField(value, "createdAt", 128), "createdAt"),
28051
+ mediaType: stringField(value, "mediaType", 255),
28052
+ name: stringField(value, "name", 500),
28053
+ sessionId: identifierField(value, "sessionId"),
28054
+ sha256
28055
+ };
28056
+ }
28057
+ function uploadingArtifact(value) {
28058
+ const fields = artifactFields(value);
28059
+ return new MobiusUploadingArtifact(
28060
+ fields.sessionId,
28061
+ fields.artifactId,
28062
+ fields.name,
28063
+ fields.mediaType,
28064
+ fields.byteLength,
28065
+ fields.sha256,
28066
+ fields.createdAt,
28067
+ validateTimestamp(stringField(value, "expiresAt", 128), "expiresAt")
28068
+ );
28069
+ }
28070
+ function decodeMobiusArtifactUpload(value) {
28071
+ const artifact = record2(value, "artifact upload authorization");
28072
+ return new MobiusArtifactUpload(uploadingArtifact(artifact), stringField(artifact, "uploadToken", 256));
28073
+ }
28074
+ function decodeMobiusArtifact(value) {
28075
+ const artifact = record2(value, "artifact");
28076
+ const state = stringField(artifact, "state", 32);
28077
+ if (state === "uploading") {
28078
+ return uploadingArtifact(artifact);
28079
+ }
28080
+ const fields = artifactFields(artifact);
28081
+ if (state === "available") {
28082
+ return new MobiusAvailableArtifact(
28083
+ fields.sessionId,
28084
+ fields.artifactId,
28085
+ fields.name,
28086
+ fields.mediaType,
28087
+ fields.byteLength,
28088
+ fields.sha256,
28089
+ fields.createdAt,
28090
+ validateTimestamp(stringField(artifact, "availableAt", 128), "availableAt")
28091
+ );
28092
+ }
28093
+ if (state === "expired") {
28094
+ return new MobiusExpiredArtifact(
28095
+ fields.sessionId,
28096
+ fields.artifactId,
28097
+ fields.name,
28098
+ fields.mediaType,
28099
+ fields.byteLength,
28100
+ fields.sha256,
28101
+ fields.createdAt,
28102
+ validateTimestamp(stringField(artifact, "expiredAt", 128), "expiredAt")
28103
+ );
28104
+ }
28105
+ throw invalidResponse("The service returned an invalid artifact state");
28106
+ }
28107
+ function decodeMobiusAvailableArtifact(value) {
28108
+ const artifact = decodeMobiusArtifact(value);
28109
+ if (!(artifact instanceof MobiusAvailableArtifact)) {
28110
+ throw invalidResponse("The service did not make the artifact available");
28111
+ }
28112
+ return artifact;
28113
+ }
28114
+ var MobiusRuntimePairingLifecycle = Object.freeze({
28115
+ Expired: "expired",
28116
+ Paired: "paired",
28117
+ Pending: "pending",
28118
+ Revoked: "revoked"
28119
+ });
28120
+
28121
+ // src/artifacts/runtime-artifact-uploader.ts
28122
+ import { fetch as fetch4 } from "undici";
28123
+ var UPLOAD_TIMEOUT_MILLISECONDS = 2e4;
28124
+ var RuntimeArtifactUploadError = class extends Error {
28125
+ code;
28126
+ constructor(code, message, options = {}) {
28127
+ super(message, options);
28128
+ this.code = code;
28129
+ this.name = "RuntimeArtifactUploadError";
28130
+ }
28131
+ };
28132
+ var UploadedRuntimeArtifact = class {
28133
+ artifactId;
28134
+ byteLength;
28135
+ mimeType;
28136
+ name;
28137
+ sha256;
28138
+ constructor(artifactId, name, mimeType, byteLength, sha256) {
28139
+ this.artifactId = artifactId;
28140
+ this.byteLength = byteLength;
28141
+ this.mimeType = mimeType;
28142
+ this.name = name;
28143
+ this.sha256 = sha256;
28144
+ }
28145
+ };
28146
+ function daemonToken(config2) {
28147
+ const token = config2.relayUrl.searchParams.get("token");
28148
+ if (token === null || token.length < 32) {
28149
+ throw new RuntimeArtifactUploadError("artifact_endpoint_invalid", "The Relay daemon token is missing");
28150
+ }
28151
+ return token;
28152
+ }
28153
+ function channelOrigin(config2) {
28154
+ const url2 = new URL(config2.relayUrl);
28155
+ url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
28156
+ let channelPath = "";
28157
+ if (url2.pathname.endsWith("/daemon")) {
28158
+ channelPath = url2.pathname.slice(0, -"/daemon".length);
28159
+ } else {
28160
+ const runtimePath = `${MOBIUS_API_PREFIX}/v1/runtimes/connect`;
28161
+ if (!url2.pathname.endsWith("/v1/runtimes/connect") && !url2.pathname.endsWith(runtimePath)) {
28162
+ throw new RuntimeArtifactUploadError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
28163
+ }
28164
+ channelPath = `${url2.pathname.replace(/\/v1\/runtimes\/connect$/, "")}/channels/${encodeURIComponent(config2.channelId)}`;
28165
+ }
28166
+ url2.pathname = channelPath;
28167
+ url2.search = "";
28168
+ url2.hash = "";
28169
+ return url2;
28170
+ }
28171
+ function uploadError(code, message, cause) {
28172
+ return new RuntimeArtifactUploadError(code, message, cause === void 0 ? {} : { cause });
28173
+ }
28174
+ function translateSdkError(cause) {
28175
+ if (cause instanceof MobiusValidationError) {
28176
+ throw uploadError("artifact_upload_failed", cause.message, cause);
28177
+ }
28178
+ if (cause instanceof MobiusProtocolError) {
28179
+ throw uploadError("artifact_upload_failed", "The Relay returned an invalid upload response", cause);
28180
+ }
28181
+ throw cause;
28182
+ }
28183
+ async function readJson(response) {
28184
+ try {
28185
+ return await response.json();
28186
+ } catch (cause) {
28187
+ throw uploadError("artifact_upload_failed", "The Relay returned an invalid upload response", cause);
28188
+ }
28189
+ }
28190
+ async function readAuthorization(response) {
28191
+ try {
28192
+ return decodeMobiusArtifactUpload(await readJson(response));
28193
+ } catch (cause) {
28194
+ translateSdkError(cause);
28195
+ }
28196
+ }
28197
+ function sessionArtifactUrl(origin, sessionId, resourcePath, token) {
28198
+ const url2 = new URL(`${origin.pathname}/sessions/${encodeURIComponent(sessionId)}/${resourcePath}`, origin);
28199
+ url2.searchParams.set("token", token);
28200
+ return url2;
28201
+ }
28202
+ async function postLease(url2, request, signal) {
28203
+ return await fetch4(url2, {
28204
+ dispatcher: environmentProxyDispatcher,
28205
+ headers: { "content-type": "application/json" },
28206
+ method: "POST",
28207
+ body: JSON.stringify(request),
28208
+ signal
28209
+ });
28210
+ }
28211
+ async function putContent(url2, authorization, bytes, signal) {
28212
+ return await fetch4(url2, {
28213
+ dispatcher: environmentProxyDispatcher,
28214
+ headers: {
28215
+ "content-type": authorization.mediaType,
28216
+ "x-mobius-upload-token": authorization.uploadToken
28217
+ },
28218
+ method: "PUT",
28219
+ body: bytes,
28220
+ signal
28221
+ });
28222
+ }
28223
+ async function uploadRuntimeArtifact(config2, sessionId, image, signal) {
28224
+ signal.throwIfAborted();
28225
+ let request;
28226
+ try {
28227
+ request = new MobiusArtifactUploadRequest(
28228
+ image.name,
28229
+ image.mimeType,
28230
+ image.bytes.byteLength,
28231
+ createHash2("sha256").update(image.bytes).digest("hex")
28232
+ );
28233
+ } catch (cause) {
28234
+ translateSdkError(cause);
28235
+ }
28236
+ const timeoutSignal = AbortSignal.timeout(UPLOAD_TIMEOUT_MILLISECONDS);
28237
+ const requestSignal = AbortSignal.any([signal, timeoutSignal]);
28238
+ const origin = channelOrigin(config2);
28239
+ const token = daemonToken(config2);
28240
+ let created;
28241
+ try {
28242
+ created = await postLease(
28243
+ sessionArtifactUrl(origin, sessionId, "artifacts/uploads", token),
28244
+ request,
28245
+ requestSignal
28246
+ );
28247
+ } catch (cause) {
28248
+ signal.throwIfAborted();
28249
+ throw uploadError(
28250
+ timeoutSignal.aborted ? "artifact_upload_timeout" : "artifact_upload_failed",
28251
+ timeoutSignal.aborted ? "The artifact upload authorization timed out" : "The artifact upload could not start",
28252
+ cause
28253
+ );
28254
+ }
28255
+ if (!created.ok) {
28256
+ await created.body?.cancel();
28257
+ throw uploadError("artifact_upload_rejected", "The Relay rejected the artifact upload");
28258
+ }
28259
+ const authorization = await readAuthorization(created);
28260
+ let uploaded;
28261
+ try {
28262
+ uploaded = await putContent(
28263
+ sessionArtifactUrl(
28264
+ origin,
28265
+ sessionId,
28266
+ `artifacts/${encodeURIComponent(authorization.artifactId)}/content`,
28267
+ token
28268
+ ),
28269
+ authorization,
28270
+ image.bytes,
28271
+ requestSignal
28272
+ );
28273
+ } catch (cause) {
28274
+ signal.throwIfAborted();
28275
+ throw uploadError(
28276
+ timeoutSignal.aborted ? "artifact_upload_timeout" : "artifact_upload_failed",
28277
+ timeoutSignal.aborted ? "The artifact upload timed out" : "The artifact content could not be uploaded",
28278
+ cause
28279
+ );
28280
+ }
28281
+ if (!uploaded.ok) {
28282
+ await uploaded.body?.cancel();
28283
+ throw uploadError("artifact_upload_rejected", "The Relay rejected the artifact content");
28284
+ }
28285
+ try {
28286
+ const available = decodeMobiusAvailableArtifact(await readJson(uploaded));
28287
+ return new UploadedRuntimeArtifact(
28288
+ available.artifactId,
28289
+ available.name,
28290
+ available.mediaType,
28291
+ available.byteLength,
28292
+ available.sha256
28293
+ );
28294
+ } catch (cause) {
28295
+ translateSdkError(cause);
28296
+ }
28297
+ }
28298
+
27629
28299
  // src/authorization/local-authorization-error.ts
27630
28300
  var LocalAuthorizationError = class extends Error {
27631
28301
  code = "local_authorization_failed";
@@ -27636,9 +28306,9 @@ var LocalAuthorizationError = class extends Error {
27636
28306
  };
27637
28307
 
27638
28308
  // src/authorization/workspace-directory.ts
27639
- import { readdir, realpath as realpath7, stat as stat6 } from "node:fs/promises";
28309
+ import { readdir, realpath as realpath8, stat as stat7 } from "node:fs/promises";
27640
28310
  import { homedir as homedir2 } from "node:os";
27641
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute7, join as join15, normalize as normalize2, relative as relative4 } from "node:path";
28311
+ import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute8, join as join15, normalize as normalize3, relative as relative5 } from "node:path";
27642
28312
  var WorkspaceDirectoryListing = class {
27643
28313
  directoryId;
27644
28314
  directoryLabel;
@@ -27664,7 +28334,7 @@ function decodePath(value, prefix) {
27664
28334
  throw new LocalAuthorizationError("The directory identifier is invalid");
27665
28335
  }
27666
28336
  const decoded = Buffer.from(encoded, "base64url").toString("utf8");
27667
- if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize2(decoded) !== decoded) {
28337
+ if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize3(decoded) !== decoded) {
27668
28338
  throw new LocalAuthorizationError("The directory identifier is invalid");
27669
28339
  }
27670
28340
  return decoded;
@@ -27690,7 +28360,7 @@ var WorkspaceDirectoryService = class {
27690
28360
  }
27691
28361
  if (id.startsWith("path-")) {
27692
28362
  const path2 = decodePath(id, "path-");
27693
- if (isAbsolute7(path2)) {
28363
+ if (isAbsolute8(path2)) {
27694
28364
  return path2;
27695
28365
  }
27696
28366
  }
@@ -27698,15 +28368,15 @@ var WorkspaceDirectoryService = class {
27698
28368
  };
27699
28369
  resolvePath = async (requested, signal) => {
27700
28370
  signal.throwIfAborted();
27701
- if (!isAbsolute7(requested) || requested.includes("\0") || normalize2(requested) !== requested) {
28371
+ if (!isAbsolute8(requested) || requested.includes("\0") || normalize3(requested) !== requested) {
27702
28372
  throw new LocalAuthorizationError("The local directory path is invalid");
27703
28373
  }
27704
28374
  try {
27705
- const target = await realpath7(requested);
28375
+ const target = await realpath8(requested);
27706
28376
  signal.throwIfAborted();
27707
- const metadata = await stat6(target);
28377
+ const metadata = await stat7(target);
27708
28378
  signal.throwIfAborted();
27709
- if (!isAbsolute7(target) || normalize2(target) !== target || !metadata.isDirectory()) {
28379
+ if (!isAbsolute8(target) || normalize3(target) !== target || !metadata.isDirectory()) {
27710
28380
  throw new LocalAuthorizationError("The selected location is not a directory");
27711
28381
  }
27712
28382
  return target;
@@ -27721,8 +28391,8 @@ var WorkspaceDirectoryService = class {
27721
28391
  }
27722
28392
  };
27723
28393
  label = (path2) => {
27724
- const suffix = relative4(homedir2(), path2);
27725
- return suffix === "" ? "~" : !isAbsolute7(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
28394
+ const suffix = relative5(homedir2(), path2);
28395
+ return suffix === "" ? "~" : !isAbsolute8(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
27726
28396
  };
27727
28397
  identify = async (bindingId, requestedPath, signal) => {
27728
28398
  this.authorizeMachine(bindingId);
@@ -27730,7 +28400,7 @@ var WorkspaceDirectoryService = class {
27730
28400
  return {
27731
28401
  path: path2,
27732
28402
  directoryId: encodePath(path2),
27733
- name: basename2(path2) || path2,
28403
+ name: basename3(path2) || path2,
27734
28404
  label: this.label(path2)
27735
28405
  };
27736
28406
  };
@@ -27787,8 +28457,8 @@ var WorkspaceDirectoryService = class {
27787
28457
  };
27788
28458
 
27789
28459
  // src/bots/local-bot-grant-store.ts
27790
- import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
27791
- import { stat as stat7 } from "node:fs/promises";
28460
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
28461
+ import { stat as stat8 } from "node:fs/promises";
27792
28462
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
27793
28463
  var LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS = 90 * 24 * 60 * 60 * 1e3;
27794
28464
  var LocalBotAuthorizationError = class extends Error {
@@ -27832,7 +28502,7 @@ var LocalBotGrant = class {
27832
28502
  }
27833
28503
  };
27834
28504
  function digest(value) {
27835
- return createHash2("sha256").update(value, "utf8").digest("hex");
28505
+ return createHash3("sha256").update(value, "utf8").digest("hex");
27836
28506
  }
27837
28507
  function agentDigest(agent) {
27838
28508
  return digest(
@@ -27846,7 +28516,7 @@ function agentDigest(agent) {
27846
28516
  );
27847
28517
  }
27848
28518
  async function workspaceDigest(path2) {
27849
- const metadata = await stat7(path2);
28519
+ const metadata = await stat8(path2);
27850
28520
  if (!metadata.isDirectory()) {
27851
28521
  throw new LocalBotAuthorizationError("binding_incompatible", "The Bot workspace is not a directory");
27852
28522
  }
@@ -28263,12 +28933,12 @@ var BotExecutionContextRegistry = class {
28263
28933
  // src/bots/bot-outbound-ipc.ts
28264
28934
  import { chmod as chmod2, mkdir as mkdir4, unlink } from "node:fs/promises";
28265
28935
  import { createServer } from "node:net";
28266
- import { dirname as dirname6, isAbsolute as isAbsolute8 } from "node:path";
28267
- import { fetch as fetch4 } from "undici";
28936
+ import { dirname as dirname6, isAbsolute as isAbsolute9 } from "node:path";
28937
+ import { fetch as fetch5 } from "undici";
28268
28938
  var BOT_IPC_REQUEST_LIMIT = 32 * 1024;
28269
28939
  var BOT_IPC_TIMEOUT_MILLISECONDS2 = 2e4;
28270
28940
  var BOT_RELAY_TIMEOUT_MILLISECONDS = 2e4;
28271
- var IDENTIFIER_PATTERN6 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
28941
+ var IDENTIFIER_PATTERN7 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
28272
28942
  var WINDOWS_NAMED_PIPE_PREFIX = "\\\\.\\pipe\\";
28273
28943
  var BotOutboundIpcRequest = class {
28274
28944
  contextHandle;
@@ -28282,7 +28952,7 @@ var BotOutboundIpcRequest = class {
28282
28952
  this.text = text;
28283
28953
  }
28284
28954
  };
28285
- function isRecord11(value) {
28955
+ function isRecord12(value) {
28286
28956
  return typeof value === "object" && value !== null && !Array.isArray(value);
28287
28957
  }
28288
28958
  function decodeRequest(encoded) {
@@ -28292,7 +28962,7 @@ function decodeRequest(encoded) {
28292
28962
  } catch (cause) {
28293
28963
  throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid", { cause });
28294
28964
  }
28295
- if (!isRecord11(value)) {
28965
+ if (!isRecord12(value)) {
28296
28966
  throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid");
28297
28967
  }
28298
28968
  const keys = Object.keys(value);
@@ -28303,7 +28973,7 @@ function decodeRequest(encoded) {
28303
28973
  const idempotencyKey = value["idempotencyKey"];
28304
28974
  const target = value["target"];
28305
28975
  const text = value["text"];
28306
- if (typeof contextHandle !== "string" || !/^ctx_[a-zA-Z0-9_-]{43}$/.test(contextHandle) || typeof idempotencyKey !== "string" || !IDENTIFIER_PATTERN6.test(idempotencyKey) || typeof target !== "string" || !IDENTIFIER_PATTERN6.test(target) || typeof text !== "string" || text.trim().length === 0 || text.length > 1e4) {
28976
+ if (typeof contextHandle !== "string" || !/^ctx_[a-zA-Z0-9_-]{43}$/.test(contextHandle) || typeof idempotencyKey !== "string" || !IDENTIFIER_PATTERN7.test(idempotencyKey) || typeof target !== "string" || !IDENTIFIER_PATTERN7.test(target) || typeof text !== "string" || text.trim().length === 0 || text.length > 1e4) {
28307
28977
  throw new LocalBotAuthorizationError("invalid_request", "The local Bot request is invalid");
28308
28978
  }
28309
28979
  return new BotOutboundIpcRequest(contextHandle, idempotencyKey, target, text);
@@ -28325,7 +28995,7 @@ function relayEndpoint(relayUrl, context, target) {
28325
28995
  endpoint.pathname = `${MOBIUS_API_PREFIX}/v1/bot-executions/${execution}/delivery-targets/${encodeURIComponent(deliveryTargetId)}/messages`;
28326
28996
  return endpoint;
28327
28997
  }
28328
- function daemonToken(relayUrl) {
28998
+ function daemonToken2(relayUrl) {
28329
28999
  const token = relayUrl.searchParams.get("token") ?? "";
28330
29000
  if (!/^[a-zA-Z0-9_-]{32,256}$/.test(token)) {
28331
29001
  throw new LocalBotAuthorizationError("bot_service_unavailable", "The Runtime credential is unavailable");
@@ -28342,10 +29012,10 @@ async function decodeRelayResponse(response) {
28342
29012
  cause
28343
29013
  });
28344
29014
  }
28345
- if (response.status === 202 && isRecord11(value) && typeof value["outboundMessageId"] === "string") {
29015
+ if (response.status === 202 && isRecord12(value) && typeof value["outboundMessageId"] === "string") {
28346
29016
  return value["outboundMessageId"];
28347
29017
  }
28348
- if (isRecord11(value) && isRecord11(value["error"])) {
29018
+ if (isRecord12(value) && isRecord12(value["error"])) {
28349
29019
  const error61 = value["error"];
28350
29020
  if (typeof error61["code"] === "string" && typeof error61["message"] === "string") {
28351
29021
  throw new LocalBotAuthorizationError(error61["code"], error61["message"]);
@@ -28361,7 +29031,7 @@ var BotOutboundIpcServer = class {
28361
29031
  #usesFilesystemSocket;
28362
29032
  constructor(socketPath, relayUrl, registry2) {
28363
29033
  const usesFilesystemSocket = !socketPath.startsWith(WINDOWS_NAMED_PIPE_PREFIX);
28364
- if (usesFilesystemSocket && !isAbsolute8(socketPath)) {
29034
+ if (usesFilesystemSocket && !isAbsolute9(socketPath)) {
28365
29035
  throw new Error("The Bot IPC path must be absolute");
28366
29036
  }
28367
29037
  this.#registry = registry2;
@@ -28378,7 +29048,7 @@ var BotOutboundIpcServer = class {
28378
29048
  if (this.#usesFilesystemSocket) {
28379
29049
  await mkdir4(dirname6(this.#socketPath), { mode: 448, recursive: true });
28380
29050
  await unlink(this.#socketPath).catch((error61) => {
28381
- if (!isRecord11(error61) || error61["code"] !== "ENOENT") {
29051
+ if (!isRecord12(error61) || error61["code"] !== "ENOENT") {
28382
29052
  throw error61;
28383
29053
  }
28384
29054
  });
@@ -28413,7 +29083,7 @@ var BotOutboundIpcServer = class {
28413
29083
  }
28414
29084
  if (this.#usesFilesystemSocket) {
28415
29085
  await unlink(this.#socketPath).catch((error61) => {
28416
- if (!isRecord11(error61) || error61["code"] !== "ENOENT") {
29086
+ if (!isRecord12(error61) || error61["code"] !== "ENOENT") {
28417
29087
  throw error61;
28418
29088
  }
28419
29089
  });
@@ -28477,10 +29147,10 @@ var BotOutboundIpcServer = class {
28477
29147
  const requestSignal = AbortSignal.any([signal, timeout]);
28478
29148
  let response;
28479
29149
  try {
28480
- response = await fetch4(relayEndpoint(this.#relayUrl, context, request.target), {
29150
+ response = await fetch5(relayEndpoint(this.#relayUrl, context, request.target), {
28481
29151
  body: JSON.stringify({ idempotencyKey: request.idempotencyKey, text: request.text }),
28482
29152
  headers: {
28483
- authorization: `Bearer ${daemonToken(this.#relayUrl)}`,
29153
+ authorization: `Bearer ${daemonToken2(this.#relayUrl)}`,
28484
29154
  "content-type": "application/json",
28485
29155
  "x-mobius-bot-execution-capability": context.command.executionCapabilitySecret
28486
29156
  },
@@ -28501,7 +29171,7 @@ var BotOutboundIpcServer = class {
28501
29171
 
28502
29172
  // src/native-sessions/service.ts
28503
29173
  import { randomUUID as randomUUID4 } from "node:crypto";
28504
- import { basename as basename3 } from "node:path";
29174
+ import { basename as basename4 } from "node:path";
28505
29175
 
28506
29176
  // src/native-sessions/codex-history.ts
28507
29177
  import { spawn as spawn4 } from "node:child_process";
@@ -28534,7 +29204,6 @@ function decodeHistory(thread, value) {
28534
29204
  const page = turnsSchema.parse(value);
28535
29205
  const messages = [];
28536
29206
  let characters = 0;
28537
- let imageCharacters = 0;
28538
29207
  let truncated = page.nextCursor != null;
28539
29208
  for (const turn of page.data.toReversed()) {
28540
29209
  for (const item of turn.items) {
@@ -28542,22 +29211,19 @@ function decodeHistory(thread, value) {
28542
29211
  let message;
28543
29212
  if (type === "userMessage") {
28544
29213
  const { content } = userMessageSchema.parse(item);
28545
- const parsed = parseNativeHistoryText(
28546
- content.map((block) => {
28547
- const { type: type2 } = itemTypeSchema.parse(block);
28548
- if (type2 === "text") {
28549
- return textSchema.parse(block).text;
28550
- }
28551
- if (type2 === "image") {
28552
- return `[@image](${imageSchema.parse(block).url})`;
28553
- }
28554
- return "[Attachment]";
28555
- }).join("")
28556
- );
29214
+ const text2 = content.map((block) => {
29215
+ const { type: type2 } = itemTypeSchema.parse(block);
29216
+ if (type2 === "text") {
29217
+ return textSchema.parse(block).text;
29218
+ }
29219
+ if (type2 === "image") {
29220
+ return `[@image](${imageSchema.parse(block).url})`;
29221
+ }
29222
+ return "[Attachment]";
29223
+ }).join("");
28557
29224
  message = {
28558
29225
  role: "user",
28559
- text: parsed.text,
28560
- ...parsed.images.length > 0 ? { images: parsed.images } : {}
29226
+ text: text2
28561
29227
  };
28562
29228
  } else if (type === "agentMessage") {
28563
29229
  const agent = agentMessageSchema.parse(item);
@@ -28575,12 +29241,10 @@ function decodeHistory(thread, value) {
28575
29241
  truncated ||= text.length < message.text.length;
28576
29242
  messages.push({ ...message, text });
28577
29243
  characters += text.length;
28578
- imageCharacters += (message.images ?? []).reduce((size, image) => size + image.data.length, 0);
28579
- while (messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || characters > SESSION_HISTORY_CHARACTER_LIMIT || imageCharacters > SESSION_HISTORY_IMAGE_CHARACTER_LIMIT) {
29244
+ while (messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || characters > SESSION_HISTORY_CHARACTER_LIMIT) {
28580
29245
  const removed = messages.shift();
28581
29246
  if (removed !== void 0) {
28582
29247
  characters -= removed.text.length;
28583
- imageCharacters -= (removed.images ?? []).reduce((size, image) => size + image.data.length, 0);
28584
29248
  }
28585
29249
  truncated = true;
28586
29250
  }
@@ -28738,7 +29402,11 @@ async function readCodexHistory(agent, cwd, nativeSessionId, signal) {
28738
29402
  params: { clientInfo: { name: "mobius_history", version: "1.0.0" } }
28739
29403
  });
28740
29404
  }
28741
- return await response.promise;
29405
+ const snapshot = await response.promise;
29406
+ return {
29407
+ ...snapshot,
29408
+ history: await embedLocalHistoryImages(snapshot.history, [cwd, snapshot.cwd], signal)
29409
+ };
28742
29410
  } finally {
28743
29411
  clearTimeout(timeout);
28744
29412
  signal.removeEventListener("abort", abort);
@@ -28992,7 +29660,7 @@ var NativeSessionService = class {
28992
29660
  }
28993
29661
  resolvedPaths.add(path2);
28994
29662
  additionalDirectories.push(path2);
28995
- additionalEntries.push({ directoryId: directoryId2, name: basename3(path2) });
29663
+ additionalEntries.push({ directoryId: directoryId2, name: basename4(path2) });
28996
29664
  }
28997
29665
  const listing = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
28998
29666
  const opened = await this.#sessions.forkSessionAtTurn(
@@ -29131,13 +29799,13 @@ var NativeSessionService = class {
29131
29799
  };
29132
29800
 
29133
29801
  // src/prompt-artifact-resolver.ts
29134
- import { createHash as createHash3 } from "node:crypto";
29135
- import { fetch as fetch5 } from "undici";
29802
+ import { createHash as createHash4 } from "node:crypto";
29803
+ import { fetch as fetch6 } from "undici";
29136
29804
  var ARTIFACT_SCHEME = "mobius-artifact:";
29137
29805
  var ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS = 2e4;
29138
29806
  var MAX_AGENT_IMAGE_BASE64_LENGTH = 4e6;
29139
29807
  var MAX_AGENT_RESOURCE_BYTE_LENGTH = 1e6;
29140
- var IDENTIFIER_PATTERN7 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
29808
+ var IDENTIFIER_PATTERN8 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
29141
29809
  var PromptArtifactError = class extends Error {
29142
29810
  code;
29143
29811
  constructor(code, message, options = {}) {
@@ -29180,7 +29848,7 @@ function parseReference(resource) {
29180
29848
  const encodedByteLength2 = url2.searchParams.get("byteLength") ?? "";
29181
29849
  const sha256 = url2.searchParams.get("sha256") ?? "";
29182
29850
  const byteLength = Number(encodedByteLength2);
29183
- if (url2.protocol !== ARTIFACT_SCHEME || url2.hash.length > 0 || resource.type !== "link" || resource.text.length > 0 || !IDENTIFIER_PATTERN7.test(artifactId) || keys.length !== 2 || keys[0] !== "byteLength" || keys[1] !== "sha256" || !/^[1-9][0-9]*$/.test(encodedByteLength2) || !Number.isSafeInteger(byteLength) || !/^[a-f0-9]{64}$/.test(sha256)) {
29851
+ if (url2.protocol !== ARTIFACT_SCHEME || url2.hash.length > 0 || resource.type !== "link" || resource.text.length > 0 || !IDENTIFIER_PATTERN8.test(artifactId) || keys.length !== 2 || keys[0] !== "byteLength" || keys[1] !== "sha256" || !/^[1-9][0-9]*$/.test(encodedByteLength2) || !Number.isSafeInteger(byteLength) || !/^[a-f0-9]{64}$/.test(sha256)) {
29184
29852
  throw new PromptArtifactError("artifact_reference_invalid", "The prompt artifact reference is invalid");
29185
29853
  }
29186
29854
  return new PromptArtifactReference(artifactId, byteLength, sha256, resource);
@@ -29246,7 +29914,7 @@ async function download(config2, command, reference, signal) {
29246
29914
  const requestSignal = AbortSignal.any([signal, timeoutSignal]);
29247
29915
  let response;
29248
29916
  try {
29249
- response = await fetch5(downloadUrl(config2, command, reference), {
29917
+ response = await fetch6(downloadUrl(config2, command, reference), {
29250
29918
  dispatcher: environmentProxyDispatcher,
29251
29919
  headers: {
29252
29920
  accept: reference.resource.mimeType,
@@ -29270,7 +29938,7 @@ async function download(config2, command, reference, signal) {
29270
29938
  );
29271
29939
  }
29272
29940
  const bytes = await responseBytes(response, reference.byteLength, requestSignal);
29273
- if (createHash3("sha256").update(bytes).digest("hex") !== reference.sha256) {
29941
+ if (createHash4("sha256").update(bytes).digest("hex") !== reference.sha256) {
29274
29942
  throw new PromptArtifactError(
29275
29943
  "artifact_integrity_failed",
29276
29944
  "The prompt artifact failed integrity validation"
@@ -30306,10 +30974,23 @@ function botIpcSocketPath() {
30306
30974
  return join16("/tmp", `mobius-bot-${randomUUID6()}.sock`);
30307
30975
  }
30308
30976
  var DaemonAcpObserver = class {
30977
+ #config;
30309
30978
  #record;
30310
- constructor(record2) {
30311
- this.#record = record2;
30979
+ constructor(config2, record3) {
30980
+ this.#config = config2;
30981
+ this.#record = record3;
30312
30982
  }
30983
+ importLocalImage = async (sessionId, image, signal) => {
30984
+ try {
30985
+ return (await uploadRuntimeArtifact(this.#config, sessionId, image, signal)).artifactId;
30986
+ } catch (cause) {
30987
+ signal.throwIfAborted();
30988
+ if (cause instanceof Error && cause.name === "AbortError") {
30989
+ throw cause;
30990
+ }
30991
+ return "";
30992
+ }
30993
+ };
30313
30994
  onCommands = (event) => {
30314
30995
  this.#record(event.sessionId, encodeSessionCommandsUpdatedEvent(event));
30315
30996
  return Promise.resolve();
@@ -30519,7 +31200,7 @@ async function reconcileActiveTurn(config2, turn, signal) {
30519
31200
  const requestSignal = AbortSignal.any([signal, timeoutSignal]);
30520
31201
  let response;
30521
31202
  try {
30522
- response = await fetch6(activeTurnReconciliationEndpoint(config2, turn), {
31203
+ response = await fetch7(activeTurnReconciliationEndpoint(config2, turn), {
30523
31204
  body: JSON.stringify({ outcome: { kind: "running" } }),
30524
31205
  dispatcher: environmentProxyDispatcher,
30525
31206
  headers: { ...runtimeAuthorization(config2.relayUrl), "content-type": "application/json" },
@@ -30835,7 +31516,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
30835
31516
  additionalDirectories.push(directory);
30836
31517
  additionalDirectoryEntries.push({
30837
31518
  directoryId: additionalDirectoryId,
30838
- name: basename4(directory)
31519
+ name: basename5(directory)
30839
31520
  });
30840
31521
  }
30841
31522
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -31346,8 +32027,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
31346
32027
  rejectConnection(failure);
31347
32028
  };
31348
32029
  const processingDeliveryIds = /* @__PURE__ */ new Set();
31349
- const processDeliveredPrompt = async (delivery) => {
31350
- const command = decodeSessionPromptCommand(delivery.payload);
32030
+ const processDeliveredPrompt = async (delivery, command, trackActiveAttempt, execute) => {
31351
32031
  if (command.sessionId !== delivery.sessionId) {
31352
32032
  throw new Error("Runtime delivery Session identity does not match its payload");
31353
32033
  }
@@ -31375,22 +32055,11 @@ async function runRelayConnection(config2, directories, audit, sessions, install
31375
32055
  }
31376
32056
  const turn = new ActiveDaemonTurn(command, receipt.attemptId);
31377
32057
  activeTurns.set(command.commandId, turn);
31378
- if (command.disposition !== SessionPromptDisposition.Steer) {
32058
+ if (trackActiveAttempt) {
31379
32059
  activeAttemptIds.set(command.sessionId, receipt.attemptId);
31380
32060
  }
31381
32061
  try {
31382
- await executePrompt(
31383
- outbox,
31384
- publisher,
31385
- command,
31386
- config2,
31387
- directories,
31388
- audit,
31389
- sessions,
31390
- receipt.attemptId,
31391
- timer,
31392
- turn.signal
31393
- );
32062
+ await execute(receipt.attemptId, turn.signal);
31394
32063
  } finally {
31395
32064
  activeTurns.delete(command.commandId);
31396
32065
  if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
@@ -31469,8 +32138,50 @@ async function runRelayConnection(config2, directories, audit, sessions, install
31469
32138
  }
31470
32139
  return;
31471
32140
  }
32141
+ if (kind === BOT_SESSION_PROMPT_KIND) {
32142
+ const command = decodeBotSessionPromptCommand(delivery.payload);
32143
+ await processDeliveredPrompt(
32144
+ delivery,
32145
+ command,
32146
+ true,
32147
+ (attemptId, executionSignal) => executeBotPrompt(
32148
+ outbox,
32149
+ publisher,
32150
+ command,
32151
+ config2,
32152
+ directories,
32153
+ audit,
32154
+ sessions,
32155
+ grants,
32156
+ contexts,
32157
+ applicator,
32158
+ ipc,
32159
+ attemptId,
32160
+ timer,
32161
+ executionSignal
32162
+ )
32163
+ );
32164
+ return;
32165
+ }
31472
32166
  if (kind === SESSION_PROMPT_KIND) {
31473
- await processDeliveredPrompt(delivery);
32167
+ const command = decodeSessionPromptCommand(delivery.payload);
32168
+ await processDeliveredPrompt(
32169
+ delivery,
32170
+ command,
32171
+ command.disposition !== SessionPromptDisposition.Steer,
32172
+ (attemptId, executionSignal) => executePrompt(
32173
+ outbox,
32174
+ publisher,
32175
+ command,
32176
+ config2,
32177
+ directories,
32178
+ audit,
32179
+ sessions,
32180
+ attemptId,
32181
+ timer,
32182
+ executionSignal
32183
+ )
32184
+ );
31474
32185
  return;
31475
32186
  }
31476
32187
  throw new Error("Runtime delivery contains an unsupported protocol message");
@@ -31508,6 +32219,8 @@ async function runRelayConnection(config2, directories, audit, sessions, install
31508
32219
  payloadSessionId = decodeSessionElicitationRespondCommand(delivery.payload).sessionId;
31509
32220
  } else if (payloadKind === SESSION_PROMPT_KIND) {
31510
32221
  payloadSessionId = decodeSessionPromptCommand(delivery.payload).sessionId;
32222
+ } else if (payloadKind === BOT_SESSION_PROMPT_KIND) {
32223
+ payloadSessionId = decodeBotSessionPromptCommand(delivery.payload).sessionId;
31511
32224
  } else {
31512
32225
  throw new Error("Runtime delivery contains an unsupported protocol message");
31513
32226
  }
@@ -32105,7 +32818,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
32105
32818
  }
32106
32819
  publisher.recordAttempt(sessionId, payload, attemptId);
32107
32820
  };
32108
- const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
32821
+ const sessions = new AcpSessionManager(new DaemonAcpObserver(config2, recordSessionEvent), sessionStore);
32109
32822
  const directories = new WorkspaceDirectoryService(config2.bindingId, config2.defaultWorkspace);
32110
32823
  const installationObserver = new DaemonInstallationObserver();
32111
32824
  const diagnostics = new RelayConnectionDiagnostics(audit, config2.channelId, config2.relayUrl);