@moxt-ai/mobius 0.0.323 → 0.0.324

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 (88) hide show
  1. package/dist/cli.js +431 -280
  2. package/dist/cli.js.map +4 -4
  3. package/dist/runtime/command/dispatcher.d.ts +1 -0
  4. package/dist/runtime/command/dispatcher.d.ts.map +1 -1
  5. package/dist/runtime/command/dispatcher.js.map +1 -1
  6. package/dist/runtime/command/registry/content-write/index.d.ts +10 -0
  7. package/dist/runtime/command/registry/content-write/index.d.ts.map +1 -0
  8. package/dist/runtime/command/registry/content-write/index.js +125 -0
  9. package/dist/runtime/command/registry/content-write/index.js.map +1 -0
  10. package/dist/runtime/command/registry/file-read/index.d.ts.map +1 -1
  11. package/dist/runtime/command/registry/file-read/index.js +2 -2
  12. package/dist/runtime/command/registry/file-read/index.js.map +1 -1
  13. package/dist/runtime/command/registry/index.d.ts +2 -1
  14. package/dist/runtime/command/registry/index.d.ts.map +1 -1
  15. package/dist/runtime/command/registry/index.js +9 -1
  16. package/dist/runtime/command/registry/index.js.map +1 -1
  17. package/dist/runtime/command/registry/prompt.d.ts.map +1 -1
  18. package/dist/runtime/command/registry/prompt.js +7 -2
  19. package/dist/runtime/command/registry/prompt.js.map +1 -1
  20. package/dist/runtime/command/registry/session.d.ts.map +1 -1
  21. package/dist/runtime/command/registry/session.js +10 -5
  22. package/dist/runtime/command/registry/session.js.map +1 -1
  23. package/dist/runtime/command/transport.d.ts +1 -0
  24. package/dist/runtime/command/transport.d.ts.map +1 -1
  25. package/dist/runtime/command/transport.js +7 -1
  26. package/dist/runtime/command/transport.js.map +1 -1
  27. package/dist/runtime/relay/connect.d.ts.map +1 -1
  28. package/dist/runtime/relay/connect.js +1 -0
  29. package/dist/runtime/relay/connect.js.map +1 -1
  30. package/dist/runtime/relay/run-relay-connection.d.ts.map +1 -1
  31. package/dist/runtime/relay/run-relay-connection.js +6 -1
  32. package/dist/runtime/relay/run-relay-connection.js.map +1 -1
  33. package/dist/runtime.js +418 -266
  34. package/dist/runtime.js.map +4 -4
  35. package/dist/session/api/session-api.d.ts +11 -4
  36. package/dist/session/api/session-api.d.ts.map +1 -1
  37. package/dist/session/api/session-api.js +3 -1
  38. package/dist/session/api/session-api.js.map +1 -1
  39. package/dist/session/content/external-content-store.d.ts +2 -0
  40. package/dist/session/content/external-content-store.d.ts.map +1 -1
  41. package/dist/session/content/external-content-store.js +5 -0
  42. package/dist/session/content/external-content-store.js.map +1 -1
  43. package/dist/session/content/session-files.d.ts +1 -1
  44. package/dist/session/content/session-files.d.ts.map +1 -1
  45. package/dist/session/content/session-files.js +4 -11
  46. package/dist/session/content/session-files.js.map +1 -1
  47. package/dist/session/live/agent-session.d.ts +2 -2
  48. package/dist/session/live/agent-session.d.ts.map +1 -1
  49. package/dist/session/live/agent-session.js +45 -13
  50. package/dist/session/live/agent-session.js.map +1 -1
  51. package/dist/session/manager/session-manager.d.ts +3 -3
  52. package/dist/session/manager/session-manager.d.ts.map +1 -1
  53. package/dist/session/manager/session-manager.js +2 -2
  54. package/dist/session/manager/session-manager.js.map +1 -1
  55. package/dist/session/prompt/apply-session-configuration.d.ts.map +1 -1
  56. package/dist/session/prompt/apply-session-configuration.js +2 -2
  57. package/dist/session/prompt/apply-session-configuration.js.map +1 -1
  58. package/dist/session/prompt/execute-prompt.d.ts +3 -3
  59. package/dist/session/prompt/execute-prompt.d.ts.map +1 -1
  60. package/dist/session/prompt/execute-prompt.js +2 -2
  61. package/dist/session/prompt/execute-prompt.js.map +1 -1
  62. package/dist/session/prompt/prompt-artifacts.d.ts +7 -22
  63. package/dist/session/prompt/prompt-artifacts.d.ts.map +1 -1
  64. package/dist/session/prompt/prompt-artifacts.js +22 -76
  65. package/dist/session/prompt/prompt-artifacts.js.map +1 -1
  66. package/dist/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  67. package/dist/session/prompt/prompt-lifecycle.js +15 -12
  68. package/dist/session/prompt/prompt-lifecycle.js.map +1 -1
  69. package/dist/session/turn/session-turns.d.ts +5 -9
  70. package/dist/session/turn/session-turns.d.ts.map +1 -1
  71. package/dist/session/turn/session-turns.js +1 -2
  72. package/dist/session/turn/session-turns.js.map +1 -1
  73. package/dist/storage/files/scratch-directory.d.ts +2 -0
  74. package/dist/storage/files/scratch-directory.d.ts.map +1 -1
  75. package/dist/storage/files/scratch-directory.js +7 -1
  76. package/dist/storage/files/scratch-directory.js.map +1 -1
  77. package/dist/util/filesystem/private-path.js +2 -3
  78. package/dist/util/filesystem/private-path.js.map +2 -2
  79. package/dist/util/platform/windows.d.ts.map +1 -1
  80. package/dist/util/platform/windows.js +3 -5
  81. package/dist/util/platform/windows.js.map +1 -1
  82. package/dist/util/process/executable.js +2 -3
  83. package/dist/util/process/executable.js.map +2 -2
  84. package/package.json +2 -2
  85. package/dist/runtime/relay/artifact-download.d.ts +0 -5
  86. package/dist/runtime/relay/artifact-download.d.ts.map +0 -1
  87. package/dist/runtime/relay/artifact-download.js +0 -105
  88. package/dist/runtime/relay/artifact-download.js.map +0 -1
package/dist/cli.js CHANGED
@@ -2086,7 +2086,7 @@ var init_package = __esm({
2086
2086
  "package.json"() {
2087
2087
  package_default = {
2088
2088
  name: "@moxt-ai/mobius",
2089
- version: "0.0.323",
2089
+ version: "0.0.324",
2090
2090
  description: "Mobius CLI for pairing and running local AI agent services.",
2091
2091
  type: "module",
2092
2092
  bin: {
@@ -70855,11 +70855,10 @@ var init_windows = __esm({
70855
70855
  windowsPermissions = `
70856
70856
  $ErrorActionPreference = 'Stop'
70857
70857
  $ProgressPreference = 'SilentlyContinue'
70858
- Import-Module (Join-Path $PSHOME 'Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1')
70859
70858
  $item = Get-Item -LiteralPath $env:MOBIUS_PERMISSION_PATH -Force
70860
70859
  if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'Unsafe path' }
70861
70860
  $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
70862
- $acl = Get-Acl -LiteralPath $item.FullName
70861
+ $acl = $item.GetAccessControl()
70863
70862
  $owner = $acl.GetOwner([Security.Principal.SecurityIdentifier])
70864
70863
  if ($owner -ne $sid -and -not ($owner.Value -eq 'S-1-5-32-544' -and ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { throw 'Path is not user-owned' }
70865
70864
  if ($env:MOBIUS_PERMISSION_PRIVATE -eq '1') {
@@ -70873,7 +70872,7 @@ if ($env:MOBIUS_PERMISSION_PRIVATE -eq '1') {
70873
70872
  $private.SetOwner($sid)
70874
70873
  $private.SetAccessRuleProtection($true, $false)
70875
70874
  $private.AddAccessRule($rule)
70876
- Set-Acl -LiteralPath $item.FullName -AclObject $private
70875
+ $item.SetAccessControl($private)
70877
70876
  }
70878
70877
  `;
70879
70878
  windowsPermissionsTimeoutMs = 3e4;
@@ -74107,6 +74106,8 @@ var init_configuration = __esm({
74107
74106
  ...sessionIdentityFields,
74108
74107
  booleanValue: external_exports.boolean(),
74109
74108
  configId: external_exports.string().min(1).max(128),
74109
+ // Omitted by legacy clients: explicit edits retain last-write-wins behavior.
74110
+ expectedConfigurationRevision: identifierStringSchema.optional(),
74110
74111
  kind: external_exports.literal(SESSION_CONFIGURE_KIND),
74111
74112
  requestId: identifierStringSchema,
74112
74113
  value: external_exports.string().max(500),
@@ -74114,6 +74115,8 @@ var init_configuration = __esm({
74114
74115
  }).readonly();
74115
74116
  sessionConfigUpdatedEventSchema = external_exports.object({
74116
74117
  configOptions: sessionConfigOptionsSchema,
74118
+ // An opaque Runtime generation token; absent on older Runtimes.
74119
+ configurationRevision: identifierStringSchema.optional(),
74117
74120
  kind: external_exports.literal(SESSION_CONFIG_UPDATED_KIND),
74118
74121
  requestId: identifierStringSchema,
74119
74122
  sessionId: identifierStringSchema
@@ -74132,6 +74135,8 @@ var init_configuration = __esm({
74132
74135
  }).readonly();
74133
74136
  sessionConfigReadResultSchema = external_exports.object({
74134
74137
  configOptions: sessionConfigOptionsSchema,
74138
+ // An opaque Runtime generation token; absent on older Runtimes.
74139
+ configurationRevision: identifierStringSchema.optional(),
74135
74140
  kind: external_exports.literal(SESSION_CONFIG_READ_RESULT_KIND),
74136
74141
  requestId: identifierStringSchema,
74137
74142
  sessionId: identifierStringSchema
@@ -74174,7 +74179,60 @@ var init_title = __esm({
74174
74179
  });
74175
74180
 
74176
74181
  // ../mobius-sdk/src/protocol/session/files/index.ts
74177
- var FILE_READ_KIND, FILE_BEGIN_KIND, FILE_CHUNK_KIND, FILE_END_KIND, FILE_FAILED_KIND, fileReadCommandSchema, fileBeginEventSchema, fileChunkEventSchema, fileEndEventSchema, fileFailedEventSchema, decodeFileReadCommand, encodeFileReadCommand, decodeFileBeginEvent, encodeFileBeginEvent, decodeFileChunkEvent, encodeFileChunkEvent, decodeFileEndEvent, encodeFileEndEvent, decodeFileFailedEvent, encodeFileFailedEvent;
74182
+ function binaryBytes(value) {
74183
+ if (value instanceof ArrayBuffer) {
74184
+ return new Uint8Array(value);
74185
+ }
74186
+ if (ArrayBuffer.isView(value)) {
74187
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
74188
+ }
74189
+ throw new Error("Content chunk must be a binary WebSocket frame");
74190
+ }
74191
+ function encodeContentChunkFrame(frame) {
74192
+ if (!isIdentifier(frame.requestId)) {
74193
+ throw new Error("Content chunk request identifier is invalid");
74194
+ }
74195
+ if (!Number.isSafeInteger(frame.sequence) || frame.sequence < 0 || frame.sequence > 4294967295) {
74196
+ throw new Error("Content chunk sequence is invalid");
74197
+ }
74198
+ if (frame.bytes.byteLength < 1 || frame.bytes.byteLength > CONTENT_CHUNK_MAX_BYTES) {
74199
+ throw new Error("Content chunk size is invalid");
74200
+ }
74201
+ const requestId = new TextEncoder().encode(frame.requestId);
74202
+ if (requestId.byteLength > 255) {
74203
+ throw new Error("Content chunk request identifier is too long");
74204
+ }
74205
+ const encoded = new Uint8Array(CONTENT_CHUNK_HEADER_BYTES + requestId.byteLength + frame.bytes.byteLength);
74206
+ encoded.set(CONTENT_CHUNK_MAGIC, 0);
74207
+ encoded[CONTENT_CHUNK_MAGIC.byteLength] = requestId.byteLength;
74208
+ new DataView(encoded.buffer).setUint32(CONTENT_CHUNK_MAGIC.byteLength + 1, frame.sequence);
74209
+ encoded.set(requestId, CONTENT_CHUNK_HEADER_BYTES);
74210
+ encoded.set(frame.bytes, CONTENT_CHUNK_HEADER_BYTES + requestId.byteLength);
74211
+ return encoded;
74212
+ }
74213
+ function decodeContentChunkFrame(value) {
74214
+ const encoded = binaryBytes(value);
74215
+ if (encoded.byteLength <= CONTENT_CHUNK_HEADER_BYTES || !CONTENT_CHUNK_MAGIC.every((byte, index2) => encoded[index2] === byte)) {
74216
+ throw new Error("Content chunk frame header is invalid");
74217
+ }
74218
+ const requestIdLength = encoded[CONTENT_CHUNK_MAGIC.byteLength] ?? 0;
74219
+ const payloadOffset = CONTENT_CHUNK_HEADER_BYTES + requestIdLength;
74220
+ if (requestIdLength < 1 || payloadOffset >= encoded.byteLength) {
74221
+ throw new Error("Content chunk frame length is invalid");
74222
+ }
74223
+ const requestId = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
74224
+ encoded.subarray(CONTENT_CHUNK_HEADER_BYTES, payloadOffset)
74225
+ );
74226
+ const bytes = encoded.slice(payloadOffset);
74227
+ const sequence = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength).getUint32(
74228
+ CONTENT_CHUNK_MAGIC.byteLength + 1
74229
+ );
74230
+ if (!isIdentifier(requestId) || bytes.byteLength > CONTENT_CHUNK_MAX_BYTES) {
74231
+ throw new Error("Content chunk frame is invalid");
74232
+ }
74233
+ return { bytes, requestId, sequence };
74234
+ }
74235
+ var FILE_READ_KIND, FILE_BEGIN_KIND, FILE_END_KIND, FILE_FAILED_KIND, CONTENT_WRITE_KIND, CONTENT_COMMIT_KIND, CONTENT_READY_KIND, CONTENT_AVAILABLE_KIND, CONTENT_CHUNK_MAX_BYTES, CONTENT_WRITE_MAX_BYTES, PROMPT_IMAGE_MAX_BYTES, CONTENT_CHUNK_MAGIC, CONTENT_CHUNK_HEADER_BYTES, fileReadCommandSchema, fileBeginEventSchema, contentWriteCommandSchema, contentCommitCommandSchema, contentReadyEventSchema, contentAvailableEventSchema, fileEndEventSchema, fileFailedEventSchema, decodeFileReadCommand, encodeFileReadCommand, decodeFileBeginEvent, encodeFileBeginEvent, decodeFileEndEvent, encodeFileEndEvent, decodeFileFailedEvent, encodeFileFailedEvent, decodeContentWriteCommand, encodeContentWriteCommand, decodeContentCommitCommand, encodeContentCommitCommand, decodeContentReadyEvent, encodeContentReadyEvent, decodeContentAvailableEvent, encodeContentAvailableEvent;
74178
74236
  var init_files = __esm({
74179
74237
  "../mobius-sdk/src/protocol/session/files/index.ts"() {
74180
74238
  "use strict";
@@ -74183,9 +74241,17 @@ var init_files = __esm({
74183
74241
  init_message_codec();
74184
74242
  FILE_READ_KIND = "file.read";
74185
74243
  FILE_BEGIN_KIND = "file.begin";
74186
- FILE_CHUNK_KIND = "file.chunk";
74187
74244
  FILE_END_KIND = "file.end";
74188
74245
  FILE_FAILED_KIND = "file.failed";
74246
+ CONTENT_WRITE_KIND = "content.write";
74247
+ CONTENT_COMMIT_KIND = "content.commit";
74248
+ CONTENT_READY_KIND = "content.ready";
74249
+ CONTENT_AVAILABLE_KIND = "content.available";
74250
+ CONTENT_CHUNK_MAX_BYTES = 192 * 1024;
74251
+ CONTENT_WRITE_MAX_BYTES = 32 * 1024 * 1024;
74252
+ PROMPT_IMAGE_MAX_BYTES = 5e6;
74253
+ CONTENT_CHUNK_MAGIC = new Uint8Array([77, 66, 67, 49]);
74254
+ CONTENT_CHUNK_HEADER_BYTES = CONTENT_CHUNK_MAGIC.byteLength + 1 + 4;
74189
74255
  fileReadCommandSchema = external_exports.object({
74190
74256
  kind: external_exports.literal(FILE_READ_KIND),
74191
74257
  maxBytes: external_exports.number().int().positive().max(128 * 1024 * 1024),
@@ -74202,11 +74268,31 @@ var init_files = __esm({
74202
74268
  requestId: identifierStringSchema,
74203
74269
  sessionId: identifierStringSchema
74204
74270
  }).readonly();
74205
- fileChunkEventSchema = external_exports.object({
74206
- data: external_exports.string().min(1).max(4e5),
74207
- kind: external_exports.literal(FILE_CHUNK_KIND),
74271
+ contentWriteCommandSchema = external_exports.object({
74272
+ byteLength: external_exports.number().int().positive().max(CONTENT_WRITE_MAX_BYTES),
74273
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
74274
+ contentKind: external_exports.enum(["image", "resource"]),
74275
+ kind: external_exports.literal(CONTENT_WRITE_KIND),
74276
+ mediaType: external_exports.string().min(1).max(255),
74277
+ name: external_exports.string().min(1).max(500),
74278
+ requestId: identifierStringSchema,
74279
+ sessionId: identifierStringSchema
74280
+ }).readonly();
74281
+ contentCommitCommandSchema = external_exports.object({
74282
+ kind: external_exports.literal(CONTENT_COMMIT_KIND),
74283
+ requestId: identifierStringSchema,
74284
+ sessionId: identifierStringSchema
74285
+ }).readonly();
74286
+ contentReadyEventSchema = external_exports.object({
74287
+ kind: external_exports.literal(CONTENT_READY_KIND),
74288
+ requestId: identifierStringSchema,
74289
+ sessionId: identifierStringSchema
74290
+ }).readonly();
74291
+ contentAvailableEventSchema = external_exports.object({
74292
+ byteLength: external_exports.number().int().positive().max(CONTENT_WRITE_MAX_BYTES),
74293
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
74294
+ kind: external_exports.literal(CONTENT_AVAILABLE_KIND),
74208
74295
  requestId: identifierStringSchema,
74209
- sequence: external_exports.number().int().nonnegative().max(1024),
74210
74296
  sessionId: identifierStringSchema
74211
74297
  }).readonly();
74212
74298
  fileEndEventSchema = external_exports.object({
@@ -74225,12 +74311,18 @@ var init_files = __esm({
74225
74311
  encodeFileReadCommand = (message) => encodeZodMessage(message, fileReadCommandSchema);
74226
74312
  decodeFileBeginEvent = (encoded) => decodeZodMessage(encoded, fileBeginEventSchema);
74227
74313
  encodeFileBeginEvent = (message) => encodeZodMessage(message, fileBeginEventSchema);
74228
- decodeFileChunkEvent = (encoded) => decodeZodMessage(encoded, fileChunkEventSchema);
74229
- encodeFileChunkEvent = (message) => encodeZodMessage(message, fileChunkEventSchema);
74230
74314
  decodeFileEndEvent = (encoded) => decodeZodMessage(encoded, fileEndEventSchema);
74231
74315
  encodeFileEndEvent = (message) => encodeZodMessage(message, fileEndEventSchema);
74232
74316
  decodeFileFailedEvent = (encoded) => decodeZodMessage(encoded, fileFailedEventSchema);
74233
74317
  encodeFileFailedEvent = (message) => encodeZodMessage(message, fileFailedEventSchema);
74318
+ decodeContentWriteCommand = (encoded) => decodeZodMessage(encoded, contentWriteCommandSchema);
74319
+ encodeContentWriteCommand = (message) => encodeZodMessage(message, contentWriteCommandSchema);
74320
+ decodeContentCommitCommand = (encoded) => decodeZodMessage(encoded, contentCommitCommandSchema);
74321
+ encodeContentCommitCommand = (message) => encodeZodMessage(message, contentCommitCommandSchema);
74322
+ decodeContentReadyEvent = (encoded) => decodeZodMessage(encoded, contentReadyEventSchema);
74323
+ encodeContentReadyEvent = (message) => encodeZodMessage(message, contentReadyEventSchema);
74324
+ decodeContentAvailableEvent = (encoded) => decodeZodMessage(encoded, contentAvailableEventSchema);
74325
+ encodeContentAvailableEvent = (message) => encodeZodMessage(message, contentAvailableEventSchema);
74234
74326
  }
74235
74327
  });
74236
74328
 
@@ -74584,6 +74676,7 @@ var init_lifecycle = __esm({
74584
74676
  capabilities: sessionCapabilitiesSchema,
74585
74677
  commands: external_exports.array(sessionCommandSchema).max(500).readonly(),
74586
74678
  configOptions: sessionConfigOptionsSchema,
74679
+ configurationRevision: identifierStringSchema.optional(),
74587
74680
  directoryId: DIRECTORY_IDENTIFIER,
74588
74681
  directoryLabel: external_exports.string().min(1).max(MAX_WORKSPACE_PATH_LENGTH),
74589
74682
  kind: external_exports.literal(SESSION_READY_KIND),
@@ -74678,13 +74771,12 @@ function decodeSessionPromptCommand(encoded) {
74678
74771
  const hasSessionConfigurations = Object.hasOwn(value, "sessionConfigurations");
74679
74772
  assertKind(value, SESSION_PROMPT_KIND);
74680
74773
  const prompt = readText(value, "prompt", MAX_PROMPT_LENGTH);
74681
- const images = decodePromptImagesValue(value["images"]);
74682
- const resources = decodePromptResourcesValue(value["resources"]);
74683
- const attachmentLength = images.reduce((total, image) => total + image.data.length, 0) + resources.reduce((total, resource) => total + resource.text.length, 0);
74684
- if (attachmentLength > MAX_PROMPT_ATTACHMENT_LENGTH) {
74774
+ const attachments = !Object.hasOwn(value, "attachments") && Array.isArray(value["images"]) && Array.isArray(value["resources"]) ? [] : decodePromptAttachmentsValue(value["attachments"]);
74775
+ const attachmentBytes = attachments.reduce((total, attachment) => total + attachment.byteLength, 0);
74776
+ if (attachmentBytes > MAX_PROMPT_ATTACHMENT_BYTES) {
74685
74777
  throw new ProtocolMessageError("Prompt attachments are too large");
74686
74778
  }
74687
- if (prompt.trim().length === 0 && images.length === 0 && resources.length === 0) {
74779
+ if (prompt.trim().length === 0 && attachments.length === 0) {
74688
74780
  throw new ProtocolMessageError("Prompt must contain content");
74689
74781
  }
74690
74782
  const disposition = readPromptDisposition(value);
@@ -74704,13 +74796,12 @@ function decodeSessionPromptCommand(encoded) {
74704
74796
  commandId: readIdentifier(value, "commandId"),
74705
74797
  directoryId: readDirectoryIdentifier(value, "directoryId"),
74706
74798
  disposition,
74707
- images,
74799
+ attachments,
74708
74800
  ...value["historyPolicy"] === void 0 ? {} : {
74709
74801
  historyPolicy: readHistoryPolicy(value)
74710
74802
  },
74711
74803
  kind: SESSION_PROMPT_KIND,
74712
74804
  prompt,
74713
- resources,
74714
74805
  ...hasSessionConfigurations ? { sessionConfigurations } : {},
74715
74806
  sessionId: readIdentifier(value, "sessionId"),
74716
74807
  targetCommandId
@@ -74746,7 +74837,15 @@ function decodeSessionPromptDispositionCommand(encoded) {
74746
74837
  };
74747
74838
  }
74748
74839
  function encodeSessionPromptCommand(message) {
74749
- return JSON.stringify(decodeSessionPromptCommand(JSON.stringify(message)));
74840
+ const command = decodeSessionPromptCommand(JSON.stringify(message));
74841
+ return JSON.stringify({
74842
+ ...command,
74843
+ // Keep text prompts compatible with the previous Runtime release while
74844
+ // attachments move to the content-key protocol. Older Runtimes ignore
74845
+ // `attachments` and require both legacy arrays to be present.
74846
+ images: [],
74847
+ resources: []
74848
+ });
74750
74849
  }
74751
74850
  function encodeSessionCancelCommand(message) {
74752
74851
  return JSON.stringify(decodeSessionCancelCommand(JSON.stringify(message)));
@@ -74754,7 +74853,7 @@ function encodeSessionCancelCommand(message) {
74754
74853
  function encodeSessionPromptDispositionCommand(message) {
74755
74854
  return JSON.stringify(decodeSessionPromptDispositionCommand(JSON.stringify(message)));
74756
74855
  }
74757
- var SESSION_PROMPT_KIND, SESSION_PROMPT_DISPOSITION_KIND, SESSION_CANCEL_KIND, SessionPromptDisposition, SessionHistoryPolicy, MAX_PROMPT_LENGTH, MAX_PROMPT_ATTACHMENT_LENGTH, promptImageSchema, promptResourceSchema, decodePromptImagesValue, decodePromptResourcesValue;
74856
+ var SESSION_PROMPT_KIND, SESSION_PROMPT_DISPOSITION_KIND, SESSION_CANCEL_KIND, SessionPromptDisposition, SessionHistoryPolicy, MAX_PROMPT_LENGTH, MAX_PROMPT_ATTACHMENT_BYTES, promptAttachmentSchema, decodePromptAttachmentsValue;
74758
74857
  var init_commands = __esm({
74759
74858
  "../mobius-sdk/src/protocol/session/prompt/commands.ts"() {
74760
74859
  "use strict";
@@ -74775,21 +74874,16 @@ var init_commands = __esm({
74775
74874
  ExecutionOnly: "execution-only"
74776
74875
  });
74777
74876
  MAX_PROMPT_LENGTH = 1e5;
74778
- MAX_PROMPT_ATTACHMENT_LENGTH = 6e6;
74779
- promptImageSchema = external_exports.object({
74780
- data: external_exports.string().min(1).max(4e6),
74877
+ MAX_PROMPT_ATTACHMENT_BYTES = 32 * 1024 * 1024;
74878
+ promptAttachmentSchema = external_exports.object({
74879
+ byteLength: external_exports.number().int().positive().max(MAX_PROMPT_ATTACHMENT_BYTES),
74880
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
74881
+ kind: external_exports.enum(["image", "resource"]),
74781
74882
  mimeType: external_exports.string().min(1).max(255),
74782
- name: external_exports.string().min(1).max(500)
74783
- }).readonly();
74784
- promptResourceSchema = external_exports.object({
74785
- mimeType: external_exports.string().max(255),
74786
74883
  name: external_exports.string().min(1).max(500),
74787
- text: external_exports.string().max(1e6),
74788
- type: external_exports.enum(["embedded", "link"]),
74789
74884
  uri: external_exports.string().min(1).max(4096)
74790
74885
  }).readonly();
74791
- decodePromptImagesValue = (value) => decodeZodValue(value, external_exports.array(promptImageSchema).max(8).readonly());
74792
- decodePromptResourcesValue = (value) => decodeZodValue(value, external_exports.array(promptResourceSchema).max(16).readonly());
74886
+ decodePromptAttachmentsValue = (value) => decodeZodValue(value, external_exports.array(promptAttachmentSchema).max(16).readonly());
74793
74887
  }
74794
74888
  });
74795
74889
 
@@ -74889,9 +74983,7 @@ var init_native = __esm({
74889
74983
  external_exports.object({
74890
74984
  commandId: identifierStringSchema,
74891
74985
  prompt: external_exports.string().max(1e5),
74892
- images: external_exports.array(promptImageSchema).max(8).readonly(),
74893
- // Absent from daemons that predate it; a client then cannot restore the prompt's files.
74894
- resources: external_exports.array(promptResourceSchema).max(16).readonly().optional()
74986
+ attachments: external_exports.array(promptAttachmentSchema).max(16).readonly()
74895
74987
  }).readonly()
74896
74988
  ).readonly(),
74897
74989
  turn: activeTurnSnapshotSchema.nullable()
@@ -75896,6 +75988,16 @@ var init_daemon_transport = __esm({
75896
75988
  decodeAgentCatalogDiscoverCommand
75897
75989
  ),
75898
75990
  [FILE_READ_KIND]: defineMessageSignature(FILE_READ_KIND, encodeFileReadCommand, decodeFileReadCommand),
75991
+ [CONTENT_WRITE_KIND]: defineMessageSignature(
75992
+ CONTENT_WRITE_KIND,
75993
+ encodeContentWriteCommand,
75994
+ decodeContentWriteCommand
75995
+ ),
75996
+ [CONTENT_COMMIT_KIND]: defineMessageSignature(
75997
+ CONTENT_COMMIT_KIND,
75998
+ encodeContentCommitCommand,
75999
+ decodeContentCommitCommand
76000
+ ),
75899
76001
  [SESSION_LIST_KIND]: defineMessageSignature(SESSION_LIST_KIND, encodeSessionListCommand, decodeSessionListCommand),
75900
76002
  [SESSION_QUERY_KIND]: defineMessageSignature(
75901
76003
  SESSION_QUERY_KIND,
@@ -76047,9 +76149,14 @@ var init_daemon_transport = __esm({
76047
76149
  decodeAgentCatalogDiscoveryFailedEvent
76048
76150
  ),
76049
76151
  [FILE_BEGIN_KIND]: defineMessageSignature(FILE_BEGIN_KIND, encodeFileBeginEvent, decodeFileBeginEvent),
76050
- [FILE_CHUNK_KIND]: defineMessageSignature(FILE_CHUNK_KIND, encodeFileChunkEvent, decodeFileChunkEvent),
76051
76152
  [FILE_END_KIND]: defineMessageSignature(FILE_END_KIND, encodeFileEndEvent, decodeFileEndEvent),
76052
76153
  [FILE_FAILED_KIND]: defineMessageSignature(FILE_FAILED_KIND, encodeFileFailedEvent, decodeFileFailedEvent),
76154
+ [CONTENT_READY_KIND]: defineMessageSignature(CONTENT_READY_KIND, encodeContentReadyEvent, decodeContentReadyEvent),
76155
+ [CONTENT_AVAILABLE_KIND]: defineMessageSignature(
76156
+ CONTENT_AVAILABLE_KIND,
76157
+ encodeContentAvailableEvent,
76158
+ decodeContentAvailableEvent
76159
+ ),
76053
76160
  [SESSION_LISTED_KIND]: defineMessageSignature(
76054
76161
  SESSION_LISTED_KIND,
76055
76162
  encodeSessionsListedEvent,
@@ -93501,7 +93608,8 @@ var init_session_api = __esm({
93501
93608
  configOptions;
93502
93609
  prompted;
93503
93610
  restored;
93504
- constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
93611
+ configurationRevision;
93612
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored, configurationRevision) {
93505
93613
  this.agentName = agentName;
93506
93614
  this.agentVersion = agentVersion;
93507
93615
  this.capabilities = capabilities;
@@ -93509,6 +93617,7 @@ var init_session_api = __esm({
93509
93617
  this.configOptions = configOptions;
93510
93618
  this.prompted = prompted;
93511
93619
  this.restored = restored;
93620
+ this.configurationRevision = configurationRevision;
93512
93621
  }
93513
93622
  };
93514
93623
  AgentSettingsSnapshot = class {
@@ -94573,6 +94682,7 @@ var init_agent_session = __esm({
94573
94682
  init_prompt_content();
94574
94683
  init_session_capabilities();
94575
94684
  init_session_config_protocol();
94685
+ init_serial_task_queue();
94576
94686
  init_value_utils();
94577
94687
  init_session_api();
94578
94688
  init_session_history();
@@ -94632,7 +94742,8 @@ var init_agent_session = __esm({
94632
94742
  #agentVersion = "";
94633
94743
  #capabilities = unsupportedSessionCapabilities();
94634
94744
  #commands = [];
94635
- #configRevision = 0;
94745
+ #configRevision = crypto.randomUUID();
94746
+ #configurationChanges = new SerialTaskQueue();
94636
94747
  #configOptions = [];
94637
94748
  #sessionConfigurations = new SessionConfigurations();
94638
94749
  #nativeHistoryOwnership = { kind: "unbound" };
@@ -94801,7 +94912,8 @@ var init_agent_session = __esm({
94801
94912
  this.#commands,
94802
94913
  this.#configOptions,
94803
94914
  this.#prompted,
94804
- this.#restored
94915
+ this.#restored,
94916
+ this.#configRevision
94805
94917
  );
94806
94918
  ensureHistoryLoaded = async (signal) => {
94807
94919
  if (!this.#capabilities.loadSession) {
@@ -94931,12 +95043,23 @@ var init_agent_session = __esm({
94931
95043
  const configured = await this.#adapter.configureSession(this.#target(), selection, signal);
94932
95044
  if (this.#configRevision === configRevision) {
94933
95045
  this.#configOptions = configured;
95046
+ this.#configRevision = crypto.randomUUID();
95047
+ await this.#publishConfiguration();
94934
95048
  }
94935
95049
  };
94936
- configure = async (options) => {
95050
+ // The revision check and Agent mutation share one queue, including legacy callers.
95051
+ // A lost response may have committed: reread after a conflict, never replay an edit blindly.
95052
+ configure = async (options) => this.#configurationChanges.run(async (signal) => {
95053
+ if (options.expectedConfigurationRevision !== void 0 && options.expectedConfigurationRevision !== this.#configRevision) {
95054
+ throw new AcpExecutionError(
95055
+ "Session settings changed. Review the current settings before applying your selection again.",
95056
+ {},
95057
+ "session_configuration_conflict"
95058
+ );
95059
+ }
94937
95060
  await this.#applyConfiguration(
94938
95061
  options.valueType === "boolean" ? { type: "boolean", configId: options.configId, booleanValue: options.booleanValue } : { type: "select", configId: options.configId, value: options.value },
94939
- options.signal
95062
+ signal
94940
95063
  );
94941
95064
  const applied = this.#configOptions.find((option) => option.id === options.configId);
94942
95065
  if (applied === void 0) {
@@ -94944,7 +95067,24 @@ var init_agent_session = __esm({
94944
95067
  }
94945
95068
  this.#sessionConfigurations.select(applied);
94946
95069
  this.writeStoredSession((/* @__PURE__ */ new Date()).toISOString());
94947
- return this.#configOptions;
95070
+ return { configOptions: this.#configOptions, configurationRevision: this.#configRevision };
95071
+ }, options.signal);
95072
+ #publishConfiguration = async () => {
95073
+ const event = this.#store.events.append(this.#mobiusSessionId, {
95074
+ type: "acp",
95075
+ payload: {
95076
+ sessionUpdate: "config_option_update",
95077
+ configOptions: this.#configOptions.map((option) => ({
95078
+ category: option.category,
95079
+ description: option.description,
95080
+ id: option.id,
95081
+ name: option.name,
95082
+ ...option.type === "boolean" ? { type: "boolean", currentValue: option.booleanValue } : { type: "select", currentValue: option.currentValue, options: option.values }
95083
+ }))
95084
+ }
95085
+ });
95086
+ await this.#getObserver().onSessionEvent?.(event);
95087
+ await this.emitConfigUpdate();
94948
95088
  };
94949
95089
  #target = () => ({
94950
95090
  nativeSessionId: this.#updates.sessionId(),
@@ -95122,6 +95262,13 @@ var init_agent_session = __esm({
95122
95262
  this.#nativeHistoryOwnership = { kind: "agent-owned" };
95123
95263
  };
95124
95264
  #processEvent = async (event) => {
95265
+ if (event.kind === "configuration" || event.kind === "mode") {
95266
+ this.#configOptions = event.kind === "configuration" ? event.configOptions : this.#configOptions.map(
95267
+ (option) => option.category === "mode" ? { ...option, currentValue: event.modeId } : option
95268
+ );
95269
+ this.#configRevision = crypto.randomUUID();
95270
+ this.#captureSelectedConfigurationValues();
95271
+ }
95125
95272
  if (this.#sessionLoad.kind === "capturing") {
95126
95273
  await this.#sessionLoad.history.record(event.payload);
95127
95274
  } else if (this.#sessionLoad.kind === "loaded" || this.#sessionLoad.kind === "execution-only") {
@@ -95142,17 +95289,7 @@ var init_agent_session = __esm({
95142
95289
  });
95143
95290
  return;
95144
95291
  case "configuration":
95145
- this.#configOptions = event.configOptions;
95146
- this.#configRevision += 1;
95147
- this.#captureSelectedConfigurationValues();
95148
- await this.emitConfigUpdate();
95149
- return;
95150
95292
  case "mode":
95151
- this.#configOptions = this.#configOptions.map(
95152
- (option) => option.category === "mode" ? { ...option, currentValue: event.modeId } : option
95153
- );
95154
- this.#configRevision += 1;
95155
- this.#captureSelectedConfigurationValues();
95156
95293
  await this.emitConfigUpdate();
95157
95294
  return;
95158
95295
  case "info":
@@ -95215,6 +95352,7 @@ var init_agent_session = __esm({
95215
95352
  }
95216
95353
  setConfigOptions(options) {
95217
95354
  this.#configOptions = options;
95355
+ this.#configRevision = crypto.randomUUID();
95218
95356
  }
95219
95357
  setNativeHistoryOwnership(ownership) {
95220
95358
  this.#nativeHistoryOwnership = ownership;
@@ -95236,6 +95374,7 @@ var init_agent_session = __esm({
95236
95374
  emitConfigUpdate = async () => {
95237
95375
  await this.#getObserver().onConfig({
95238
95376
  configOptions: this.#configOptions,
95377
+ configurationRevision: this.#configRevision,
95239
95378
  kind: SESSION_CONFIG_UPDATED_KIND,
95240
95379
  requestId: `agent-update-${crypto.randomUUID()}`,
95241
95380
  sessionId: this.#mobiusSessionId
@@ -95663,7 +95802,7 @@ var init_session_manager = __esm({
95663
95802
  (option) => option.id === "model" || option.category === "model"
95664
95803
  );
95665
95804
  const modelIsFixed = modelOption2?.type === "select" && modelOption2.values.length < 2 && modelOption2.currentValue === model;
95666
- const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : await runtime.configure({
95805
+ const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : (await runtime.configure({
95667
95806
  ...options2,
95668
95807
  booleanValue: false,
95669
95808
  configId: modelOption2?.id ?? "model",
@@ -95671,7 +95810,7 @@ var init_session_manager = __esm({
95671
95810
  sessionId: `agent-settings-${crypto.randomUUID()}`,
95672
95811
  value: model,
95673
95812
  valueType: "select"
95674
- });
95813
+ })).configOptions;
95675
95814
  return new AgentSettingsSnapshot(
95676
95815
  prepared.agentName,
95677
95816
  prepared.agentVersion,
@@ -96525,19 +96664,22 @@ function promptContent(command) {
96525
96664
  if (command.prompt.length > 0) {
96526
96665
  content.push({ text: command.prompt, type: "text" });
96527
96666
  }
96528
- for (const image of command.images) {
96529
- content.push({
96530
- data: image.data,
96531
- mimeType: image.mimeType,
96532
- type: "image"
96533
- });
96534
- }
96535
- for (const resource of command.resources) {
96667
+ for (const attachment of command.attachments) {
96536
96668
  content.push(
96537
- resource.type === "embedded" ? {
96538
- resource: { mimeType: resource.mimeType, text: resource.text, uri: resource.uri },
96539
- type: "resource"
96540
- } : { mimeType: resource.mimeType, name: resource.name, type: "resource_link", uri: resource.uri }
96669
+ attachment.kind === "image" ? {
96670
+ byteLength: attachment.byteLength,
96671
+ imageKey: attachment.contentKey,
96672
+ mimeType: attachment.mimeType,
96673
+ type: "external_image",
96674
+ uri: attachment.uri
96675
+ } : {
96676
+ byteLength: attachment.byteLength,
96677
+ encoding: "text",
96678
+ mimeType: attachment.mimeType,
96679
+ resourceKey: attachment.contentKey,
96680
+ type: "external_resource",
96681
+ uri: attachment.uri
96682
+ }
96541
96683
  );
96542
96684
  }
96543
96685
  return content;
@@ -97258,8 +97400,7 @@ var init_session_turns = __esm({
97258
97400
  queued.push({
97259
97401
  commandId: candidate.command.commandId,
97260
97402
  prompt: candidate.command.prompt,
97261
- images: candidate.command.images,
97262
- resources: candidate.command.resources
97403
+ attachments: candidate.command.attachments
97263
97404
  });
97264
97405
  }
97265
97406
  return { turn: turnSnapshot ?? null, queued };
@@ -97584,7 +97725,7 @@ var init_session_layer = __esm({
97584
97725
 
97585
97726
  // src/storage/files/scratch-directory.ts
97586
97727
  import { randomUUID as randomUUID16 } from "node:crypto";
97587
- import { lstat as lstat6, mkdir as mkdir5, readdir as readdir7, realpath as realpath9, rename as rename5, rm as rm4, stat as stat8, utimes, writeFile as writeFile5 } from "node:fs/promises";
97728
+ import { lstat as lstat6, mkdir as mkdir5, readdir as readdir7, readFile as readFile12, realpath as realpath9, rename as rename5, rm as rm4, stat as stat8, utimes, writeFile as writeFile5 } from "node:fs/promises";
97588
97729
  import { dirname as dirname12, join as join34 } from "node:path";
97589
97730
  async function atomicWrite(path8, bytes, signal) {
97590
97731
  const temporary = `${path8}.${randomUUID16()}.tmp`;
@@ -97652,6 +97793,12 @@ var init_scratch_directory = __esm({
97652
97793
  return null;
97653
97794
  }
97654
97795
  };
97796
+ /** Reads a regular file owned by this scratch directory after validating its canonical path. */
97797
+ read = async (path8, signal) => {
97798
+ const resolved = await this.resolveFile(path8);
97799
+ signal.throwIfAborted();
97800
+ return resolved === null ? null : new Uint8Array(await readFile12(resolved, { signal }));
97801
+ };
97655
97802
  removeAll = async () => {
97656
97803
  await rm4(this.#root, { force: true, recursive: true });
97657
97804
  };
@@ -97763,6 +97910,11 @@ var init_external_content_store = __esm({
97763
97910
  signal.throwIfAborted();
97764
97911
  return resolvedPath === null ? null : { mediaType: entry.mediaType, name: entry.name, path: resolvedPath };
97765
97912
  };
97913
+ read = (sessionId, request, signal) => this.#track(async () => {
97914
+ const active = AbortSignal.any([signal, this.#lifetime.signal]);
97915
+ const resolved = await this.#resolve(sessionId, request, active);
97916
+ return resolved === null ? null : this.#scratch.read(resolved.path, active);
97917
+ });
97766
97918
  [Symbol.asyncDispose] = async () => {
97767
97919
  clearInterval(this.#expirationTimer);
97768
97920
  this.#lifetime.abort();
@@ -98371,6 +98523,12 @@ function send(socket, message) {
98371
98523
  }
98372
98524
  socket.send(encodeDaemonToServiceMessage(message));
98373
98525
  }
98526
+ function sendContentChunk(socket, requestId, sequence, bytes) {
98527
+ if (socket.readyState !== WebSocket2.OPEN) {
98528
+ throw new Error("Relay connection is not open");
98529
+ }
98530
+ socket.send(encodeContentChunkFrame({ bytes, requestId, sequence }));
98531
+ }
98374
98532
  function reportOperationFailure(socket, requestId, sessionId, error62, runtimeTimings = []) {
98375
98533
  if (socket.readyState !== WebSocket2.OPEN) {
98376
98534
  return;
@@ -99057,6 +99215,134 @@ var init_agent_settings = __esm({
99057
99215
  }
99058
99216
  });
99059
99217
 
99218
+ // src/runtime/command/registry/content-write/index.ts
99219
+ import { createHash as createHash10 } from "node:crypto";
99220
+ function createContentWriteCommands(context) {
99221
+ const pending = /* @__PURE__ */ new Map();
99222
+ const operations = /* @__PURE__ */ new Set();
99223
+ const fail = (command, error62) => {
99224
+ pending.delete(command.requestId);
99225
+ send(context.socket, {
99226
+ code: "content_write_failed",
99227
+ kind: FILE_FAILED_KIND,
99228
+ message: error62 instanceof Error ? error62.message : "The content could not be stored",
99229
+ requestId: command.requestId,
99230
+ sessionId: command.sessionId
99231
+ });
99232
+ };
99233
+ const write = {
99234
+ kind: CONTENT_WRITE_KIND,
99235
+ handle: (_commandContext, message) => {
99236
+ if (message.kind !== CONTENT_WRITE_KIND) {
99237
+ throw new Error("Command kind mismatch");
99238
+ }
99239
+ const existing = pending.get(message.requestId);
99240
+ if (existing !== void 0) {
99241
+ if (JSON.stringify(existing.command) !== JSON.stringify(message)) {
99242
+ fail(message, new Error("The content request identifier is already in use"));
99243
+ return;
99244
+ }
99245
+ } else {
99246
+ pending.set(message.requestId, {
99247
+ bytes: new Uint8Array(message.byteLength),
99248
+ command: message,
99249
+ hash: createHash10("sha256"),
99250
+ receivedBytes: 0,
99251
+ sequence: 0
99252
+ });
99253
+ }
99254
+ send(context.socket, {
99255
+ kind: CONTENT_READY_KIND,
99256
+ requestId: message.requestId,
99257
+ sessionId: message.sessionId
99258
+ });
99259
+ }
99260
+ };
99261
+ const commit = {
99262
+ kind: CONTENT_COMMIT_KIND,
99263
+ handle: (_commandContext, message) => {
99264
+ if (message.kind !== CONTENT_COMMIT_KIND) {
99265
+ throw new Error("Command kind mismatch");
99266
+ }
99267
+ const state = pending.get(message.requestId);
99268
+ if (state === void 0 || state.command.sessionId !== message.sessionId) {
99269
+ fail(message, new Error("The content upload is unavailable"));
99270
+ return;
99271
+ }
99272
+ pending.delete(message.requestId);
99273
+ const operation = (async () => {
99274
+ try {
99275
+ if (state.receivedBytes !== state.command.byteLength) {
99276
+ throw new Error("The content upload ended at an unexpected length");
99277
+ }
99278
+ if (state.hash.digest("hex") !== state.command.contentKey) {
99279
+ throw new Error("The content upload failed integrity validation");
99280
+ }
99281
+ const stored = await context.externalContent.put({
99282
+ bytes: state.bytes,
99283
+ kind: state.command.contentKind,
99284
+ mediaType: state.command.mediaType,
99285
+ name: state.command.name,
99286
+ sessionId: state.command.sessionId,
99287
+ signal: context.signal
99288
+ });
99289
+ if (stored.key !== state.command.contentKey || stored.byteLength !== state.command.byteLength) {
99290
+ throw new Error("Stored content did not match the upload");
99291
+ }
99292
+ send(context.socket, {
99293
+ byteLength: stored.byteLength,
99294
+ contentKey: stored.key,
99295
+ kind: CONTENT_AVAILABLE_KIND,
99296
+ requestId: message.requestId,
99297
+ sessionId: message.sessionId
99298
+ });
99299
+ } catch (error62) {
99300
+ if (!context.signal.aborted) {
99301
+ fail(message, error62);
99302
+ }
99303
+ }
99304
+ })();
99305
+ operations.add(operation);
99306
+ void operation.finally(() => operations.delete(operation));
99307
+ }
99308
+ };
99309
+ return {
99310
+ commands: [write, commit],
99311
+ receiveChunk: (frame) => {
99312
+ const state = pending.get(frame.requestId);
99313
+ if (state === void 0) {
99314
+ context.diagnostics.daemonDiagnostic("content_chunk_discarded", frame.requestId, {
99315
+ reason: "upload_unavailable",
99316
+ sequence: frame.sequence.toString()
99317
+ });
99318
+ return;
99319
+ }
99320
+ if (frame.sequence !== state.sequence || state.receivedBytes + frame.bytes.byteLength > state.command.byteLength) {
99321
+ fail(state.command, new Error("The content upload contains invalid chunks"));
99322
+ return;
99323
+ }
99324
+ state.bytes.set(frame.bytes, state.receivedBytes);
99325
+ state.hash.update(frame.bytes);
99326
+ pending.set(frame.requestId, {
99327
+ ...state,
99328
+ receivedBytes: state.receivedBytes + frame.bytes.byteLength,
99329
+ sequence: state.sequence + 1
99330
+ });
99331
+ },
99332
+ settle: async () => {
99333
+ pending.clear();
99334
+ await Promise.allSettled([...operations]);
99335
+ }
99336
+ };
99337
+ }
99338
+ var init_content_write = __esm({
99339
+ "src/runtime/command/registry/content-write/index.ts"() {
99340
+ "use strict";
99341
+ init_protocol();
99342
+ init_transport();
99343
+ }
99344
+ });
99345
+
99060
99346
  // src/session/content/session-files.ts
99061
99347
  import { basename as basename9, extname as extname2, isAbsolute as isAbsolute18, join as join37, normalize as normalize3, resolve as resolve18 } from "node:path";
99062
99348
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -99099,7 +99385,7 @@ async function resolveFile(value, workingDirectory, signal) {
99099
99385
  }
99100
99386
  return null;
99101
99387
  }
99102
- async function streamLocalFile(command, send2, signal, workingDirectory, externalContent) {
99388
+ async function streamLocalFile(command, send2, sendChunk, signal, workingDirectory, externalContent) {
99103
99389
  const external = await externalContent.resolve(command.sessionId, command.path, signal);
99104
99390
  const target = external?.path ?? await resolveFile(command.path, workingDirectory, signal);
99105
99391
  if (target === null) {
@@ -99121,14 +99407,8 @@ async function streamLocalFile(command, send2, signal, workingDirectory, externa
99121
99407
  sessionId: command.sessionId
99122
99408
  });
99123
99409
  let sequence = 0;
99124
- for await (const bytes of readFileChunks(target, CHUNK_BYTES, signal)) {
99125
- send2({
99126
- data: Buffer.from(bytes).toString("base64"),
99127
- kind: FILE_CHUNK_KIND,
99128
- requestId: command.requestId,
99129
- sequence,
99130
- sessionId: command.sessionId
99131
- });
99410
+ for await (const bytes of readFileChunks(target, CONTENT_CHUNK_MAX_BYTES, signal)) {
99411
+ sendChunk(command.requestId, sequence, bytes);
99132
99412
  sequence += 1;
99133
99413
  }
99134
99414
  const after = await readPathMetadata(target);
@@ -99142,14 +99422,12 @@ async function streamLocalFile(command, send2, signal, workingDirectory, externa
99142
99422
  sessionId: command.sessionId
99143
99423
  });
99144
99424
  }
99145
- var CHUNK_BYTES;
99146
99425
  var init_session_files = __esm({
99147
99426
  "src/session/content/session-files.ts"() {
99148
99427
  "use strict";
99149
99428
  init_protocol();
99150
99429
  init_raster();
99151
99430
  init_local_files();
99152
- CHUNK_BYTES = 192 * 1024;
99153
99431
  }
99154
99432
  });
99155
99433
 
@@ -99171,6 +99449,7 @@ var init_file_read = __esm({
99171
99449
  void streamLocalFile(
99172
99450
  message,
99173
99451
  (response) => send(socket, response),
99452
+ (requestId, sequence, bytes) => sendContentChunk(socket, requestId, sequence, bytes),
99174
99453
  signal,
99175
99454
  sessions.findSessions([message.sessionId])[0]?.cwd ?? null,
99176
99455
  externalContent
@@ -99813,7 +100092,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
99813
100092
  if (sessionConfigurationMatches(option, selection)) {
99814
100093
  return options;
99815
100094
  }
99816
- return await sessions.configureSession({
100095
+ return (await sessions.configureSession({
99817
100096
  adapter,
99818
100097
  additionalDirectories,
99819
100098
  agent,
@@ -99825,7 +100104,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
99825
100104
  signal,
99826
100105
  value: selection.value,
99827
100106
  valueType: selection.valueType
99828
- });
100107
+ })).configOptions;
99829
100108
  }
99830
100109
  async function applyCompleteSessionConfiguration(sessions, initialOptions, selections, agent, adapter, cwd, additionalDirectories, commandId, sessionId, signal) {
99831
100110
  let options = initialOptions;
@@ -99869,98 +100148,70 @@ var init_apply_session_configuration = __esm({
99869
100148
  });
99870
100149
 
99871
100150
  // src/session/prompt/prompt-artifacts.ts
99872
- function parseReference(resource) {
99873
- let url2;
99874
- try {
99875
- url2 = new URL(resource.uri);
99876
- } catch (cause) {
99877
- throw new PromptArtifactError("artifact_reference_invalid", "The prompt artifact reference is invalid", {
99878
- cause
99879
- });
99880
- }
99881
- const keys = [...url2.searchParams.keys()].sort();
99882
- const artifactId = url2.pathname;
99883
- const encodedByteLength = url2.searchParams.get("byteLength") ?? "";
99884
- const sha256 = url2.searchParams.get("sha256") ?? "";
99885
- const byteLength = Number(encodedByteLength);
99886
- if (url2.protocol !== ARTIFACT_SCHEME || url2.hash.length > 0 || resource.type !== "link" || resource.text.length > 0 || !isIdentifier(artifactId) || keys.length !== 2 || keys[0] !== "byteLength" || keys[1] !== "sha256" || !/^[1-9][0-9]*$/.test(encodedByteLength) || !Number.isSafeInteger(byteLength) || !/^[a-f0-9]{64}$/.test(sha256)) {
99887
- throw new PromptArtifactError("artifact_reference_invalid", "The prompt artifact reference is invalid");
99888
- }
99889
- return new PromptArtifactReference(artifactId, byteLength, sha256, resource);
99890
- }
100151
+ import { createHash as createHash11 } from "node:crypto";
99891
100152
  function isTextMediaType(mediaType) {
99892
100153
  return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType.endsWith("+json") || mediaType === "application/xml" || mediaType.endsWith("+xml");
99893
100154
  }
99894
- async function resolvePromptArtifacts(download, command, signal) {
99895
- const images = [...command.images];
100155
+ async function resolvePromptArtifacts(read, attachments, signal) {
100156
+ const images = [];
99896
100157
  const resources = [];
99897
- for (const resource of command.resources) {
100158
+ for (const attachment of attachments) {
99898
100159
  signal.throwIfAborted();
99899
- if (!resource.uri.startsWith(ARTIFACT_SCHEME)) {
99900
- resources.push(resource);
99901
- continue;
100160
+ const bytes = await read(attachment, signal);
100161
+ if (bytes.byteLength !== attachment.byteLength || createHash11("sha256").update(bytes).digest("hex") !== attachment.contentKey) {
100162
+ throw new PromptArtifactError(
100163
+ "artifact_integrity_failed",
100164
+ "The prompt attachment failed integrity validation"
100165
+ );
99902
100166
  }
99903
- const reference = parseReference(resource);
99904
- const bytes = await download(
99905
- {
99906
- artifactId: reference.artifactId,
99907
- byteLength: reference.byteLength,
99908
- commandId: command.commandId,
99909
- mimeType: reference.resource.mimeType,
99910
- sessionId: command.sessionId,
99911
- sha256: reference.sha256
99912
- },
99913
- signal
99914
- );
99915
- if (resource.mimeType.startsWith("image/")) {
99916
- const data = Buffer.from(bytes).toString("base64");
99917
- if (data.length > MAX_AGENT_IMAGE_BASE64_LENGTH || images.length >= 8) {
100167
+ if (attachment.kind === "image") {
100168
+ if (bytes.byteLength > PROMPT_IMAGE_MAX_BYTES || images.length >= 8) {
99918
100169
  throw new PromptArtifactError(
99919
100170
  "artifact_too_large_for_agent",
99920
- "The prompt artifact exceeds the Agent image limit"
100171
+ "The prompt attachment exceeds the Agent image limit"
99921
100172
  );
99922
100173
  }
99923
- images.push({ data, mimeType: resource.mimeType, name: resource.name });
100174
+ images.push({
100175
+ data: Buffer.from(bytes).toString("base64"),
100176
+ mimeType: attachment.mimeType,
100177
+ name: attachment.name
100178
+ });
99924
100179
  continue;
99925
100180
  }
99926
- if (!isTextMediaType(resource.mimeType)) {
100181
+ if (!isTextMediaType(attachment.mimeType)) {
99927
100182
  throw new PromptArtifactError(
99928
100183
  "artifact_media_type_unsupported",
99929
- "The Agent cannot consume this prompt artifact media type"
100184
+ "The Agent cannot consume this prompt attachment media type"
99930
100185
  );
99931
100186
  }
99932
100187
  if (bytes.byteLength > MAX_AGENT_RESOURCE_BYTE_LENGTH || resources.length >= 16) {
99933
100188
  throw new PromptArtifactError(
99934
100189
  "artifact_too_large_for_agent",
99935
- "The prompt artifact exceeds the Agent resource limit"
100190
+ "The prompt attachment exceeds the Agent resource limit"
99936
100191
  );
99937
100192
  }
99938
100193
  let text2;
99939
100194
  try {
99940
100195
  text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
99941
100196
  } catch (cause) {
99942
- throw new PromptArtifactError("artifact_encoding_invalid", "The prompt artifact is not valid UTF-8 text", {
99943
- cause
99944
- });
100197
+ throw new PromptArtifactError(
100198
+ "artifact_encoding_invalid",
100199
+ "The prompt attachment is not valid UTF-8 text",
100200
+ {
100201
+ cause
100202
+ }
100203
+ );
99945
100204
  }
99946
- resources.push({
99947
- mimeType: resource.mimeType,
99948
- name: resource.name,
99949
- text: text2,
99950
- type: "embedded",
99951
- uri: resource.uri
99952
- });
100205
+ resources.push({ mimeType: attachment.mimeType, text: text2, type: "embedded", uri: attachment.uri });
99953
100206
  }
99954
100207
  return new ResolvedPromptArtifacts(images, resources);
99955
100208
  }
99956
- var ARTIFACT_SCHEME, MAX_AGENT_IMAGE_BASE64_LENGTH, MAX_AGENT_RESOURCE_BYTE_LENGTH, PromptArtifactError, ResolvedPromptArtifacts, PromptArtifactReference;
100209
+ var MAX_AGENT_RESOURCE_BYTE_LENGTH, PromptArtifactError, ResolvedPromptArtifacts;
99957
100210
  var init_prompt_artifacts = __esm({
99958
100211
  "src/session/prompt/prompt-artifacts.ts"() {
99959
100212
  "use strict";
99960
100213
  init_protocol();
99961
100214
  init_operation_error();
99962
- ARTIFACT_SCHEME = "mobius-artifact:";
99963
- MAX_AGENT_IMAGE_BASE64_LENGTH = 4e6;
99964
100215
  MAX_AGENT_RESOURCE_BYTE_LENGTH = 1e6;
99965
100216
  PromptArtifactError = class extends OperationError {
99966
100217
  constructor(code, message, options = {}) {
@@ -99976,18 +100227,6 @@ var init_prompt_artifacts = __esm({
99976
100227
  this.resources = resources;
99977
100228
  }
99978
100229
  };
99979
- PromptArtifactReference = class {
99980
- artifactId;
99981
- byteLength;
99982
- resource;
99983
- sha256;
99984
- constructor(artifactId, byteLength, sha256, resource) {
99985
- this.artifactId = artifactId;
99986
- this.byteLength = byteLength;
99987
- this.resource = resource;
99988
- this.sha256 = sha256;
99989
- }
99990
- };
99991
100230
  }
99992
100231
  });
99993
100232
 
@@ -99997,7 +100236,7 @@ async function executePrompt({
99997
100236
  eventProjection,
99998
100237
  command,
99999
100238
  resolveAgent,
100000
- downloadArtifact,
100239
+ readArtifact,
100001
100240
  reportFailure,
100002
100241
  directories,
100003
100242
  sessions,
@@ -100079,7 +100318,7 @@ async function executePrompt({
100079
100318
  stream.epoch,
100080
100319
  availability.disposition
100081
100320
  );
100082
- const attachments = await resolvePromptArtifacts(downloadArtifact, command, signal);
100321
+ const attachments = await resolvePromptArtifacts(readArtifact, command.attachments, signal);
100083
100322
  let stopReason;
100084
100323
  if (availability.disposition === SessionPromptDisposition.Steer) {
100085
100324
  await sessions.steerPrompt({
@@ -100150,124 +100389,6 @@ var init_execute_prompt = __esm({
100150
100389
  }
100151
100390
  });
100152
100391
 
100153
- // src/runtime/relay/artifact-download.ts
100154
- import { createHash as createHash10 } from "node:crypto";
100155
- import { fetch as fetch4 } from "undici";
100156
- function downloadUrl(config2, artifact) {
100157
- const url2 = relayHttpEndpoint(config2.relayUrl);
100158
- let channelPath = "";
100159
- if (url2.pathname.endsWith("/daemon")) {
100160
- channelPath = url2.pathname.slice(0, -"/daemon".length);
100161
- } else {
100162
- const runtimePath = "/v1/runtimes/connect";
100163
- if (!url2.pathname.endsWith(runtimePath)) {
100164
- throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
100165
- }
100166
- channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
100167
- }
100168
- url2.pathname = `${channelPath}/sessions/${encodeURIComponent(artifact.sessionId)}/commands/${encodeURIComponent(artifact.commandId)}/artifacts/${encodeURIComponent(artifact.artifactId)}/content`;
100169
- return url2;
100170
- }
100171
- async function responseBytes(response, expectedByteLength, signal) {
100172
- const declaredLength = response.headers.get("content-length") ?? "";
100173
- if (!/^(0|[1-9][0-9]*)$/.test(declaredLength) || Number(declaredLength) !== expectedByteLength || response.body === null) {
100174
- throw new PromptArtifactError("artifact_integrity_failed", "The prompt artifact failed integrity validation");
100175
- }
100176
- const reader = response.body.getReader();
100177
- const chunks = [];
100178
- let byteLength = 0;
100179
- try {
100180
- while (true) {
100181
- signal.throwIfAborted();
100182
- const chunk = await reader.read();
100183
- signal.throwIfAborted();
100184
- if (chunk.done) {
100185
- break;
100186
- }
100187
- byteLength += chunk.value.byteLength;
100188
- if (byteLength > expectedByteLength) {
100189
- await reader.cancel("Artifact response exceeded its authorization");
100190
- throw new PromptArtifactError(
100191
- "artifact_integrity_failed",
100192
- "The prompt artifact failed integrity validation"
100193
- );
100194
- }
100195
- chunks.push(chunk.value);
100196
- }
100197
- } finally {
100198
- reader.releaseLock();
100199
- }
100200
- if (byteLength !== expectedByteLength) {
100201
- throw new PromptArtifactError("artifact_integrity_failed", "The prompt artifact failed integrity validation");
100202
- }
100203
- const bytes = new Uint8Array(byteLength);
100204
- let offset = 0;
100205
- for (const chunk of chunks) {
100206
- bytes.set(chunk, offset);
100207
- offset += chunk.byteLength;
100208
- }
100209
- return bytes;
100210
- }
100211
- async function downloadPromptArtifact(config2, artifact, signal) {
100212
- signal.throwIfAborted();
100213
- const timeoutSignal = AbortSignal.timeout(ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS);
100214
- const requestSignal = AbortSignal.any([signal, timeoutSignal]);
100215
- let response;
100216
- try {
100217
- response = await fetch4(downloadUrl(config2, artifact), {
100218
- dispatcher: environmentProxyDispatcher,
100219
- headers: {
100220
- accept: artifact.mimeType,
100221
- "accept-encoding": "identity",
100222
- ...relayAuthorization(config2.relayUrl)
100223
- },
100224
- signal: requestSignal
100225
- });
100226
- if (!response.ok) {
100227
- await response.body?.cancel();
100228
- throw new PromptArtifactError(
100229
- "artifact_download_rejected",
100230
- "The Relay rejected the prompt artifact download"
100231
- );
100232
- }
100233
- if (response.headers.get("content-type") !== artifact.mimeType || response.headers.get("x-mobius-artifact-id") !== artifact.artifactId || response.headers.get("x-mobius-sha256") !== artifact.sha256) {
100234
- await response.body?.cancel();
100235
- throw new PromptArtifactError(
100236
- "artifact_integrity_failed",
100237
- "The prompt artifact failed integrity validation"
100238
- );
100239
- }
100240
- const bytes = await responseBytes(response, artifact.byteLength, requestSignal);
100241
- if (createHash10("sha256").update(bytes).digest("hex") !== artifact.sha256) {
100242
- throw new PromptArtifactError(
100243
- "artifact_integrity_failed",
100244
- "The prompt artifact failed integrity validation"
100245
- );
100246
- }
100247
- return bytes;
100248
- } catch (error62) {
100249
- signal.throwIfAborted();
100250
- if (error62 instanceof PromptArtifactError) {
100251
- throw error62;
100252
- }
100253
- throw new PromptArtifactError(
100254
- timeoutSignal.aborted ? "artifact_download_timeout" : "artifact_download_failed",
100255
- timeoutSignal.aborted ? "The prompt artifact download timed out" : "The prompt artifact could not be downloaded",
100256
- { cause: error62 }
100257
- );
100258
- }
100259
- }
100260
- var ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS;
100261
- var init_artifact_download = __esm({
100262
- "src/runtime/relay/artifact-download.ts"() {
100263
- "use strict";
100264
- init_prompt_artifacts();
100265
- init_environment_proxy();
100266
- init_relay_authentication();
100267
- ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS = 2e4;
100268
- }
100269
- });
100270
-
100271
100392
  // src/runtime/command/registry/prompt.ts
100272
100393
  var sessionPromptCommand;
100273
100394
  var init_prompt2 = __esm({
@@ -100275,7 +100396,6 @@ var init_prompt2 = __esm({
100275
100396
  "use strict";
100276
100397
  init_protocol();
100277
100398
  init_execute_prompt();
100278
- init_artifact_download();
100279
100399
  init_transport();
100280
100400
  sessionPromptCommand = {
100281
100401
  kind: SESSION_PROMPT_KIND,
@@ -100329,7 +100449,17 @@ var init_prompt2 = __esm({
100329
100449
  eventProjection: context.eventProjection,
100330
100450
  command: message,
100331
100451
  resolveAgent: context.resolveAgent,
100332
- downloadArtifact: (artifact, signal) => downloadPromptArtifact(context.config, artifact, signal),
100452
+ readArtifact: async (artifact, signal) => {
100453
+ const bytes = await context.externalContent.read(
100454
+ message.sessionId,
100455
+ `mobius-content:${artifact.kind}:${artifact.contentKey}`,
100456
+ signal
100457
+ );
100458
+ if (bytes === null) {
100459
+ throw new Error("The prompt attachment is unavailable");
100460
+ }
100461
+ return bytes;
100462
+ },
100333
100463
  reportFailure: context.diagnostics.daemonDiagnostic,
100334
100464
  directories: context.directories,
100335
100465
  sessions: context.sessions,
@@ -100422,7 +100552,7 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
100422
100552
  };
100423
100553
  const prepared = command.transient === true ? await sessions.inspectSession(preparation, metrics) : recovery.kind === "history-required" ? await sessions.restoreSession(preparation, metrics, closed) : await sessions.prepareSession(preparation, metrics, closed);
100424
100554
  const configuration = command.configuration;
100425
- const configOptions = configuration === void 0 ? prepared.configOptions : await metrics.measure(
100555
+ const snapshot = configuration === void 0 ? prepared : await metrics.measure(
100426
100556
  "configuration",
100427
100557
  async () => await sessions.configureSession({
100428
100558
  ...preparation,
@@ -100440,7 +100570,8 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
100440
100570
  agentVersion: prepared.agentVersion,
100441
100571
  capabilities: prepared.capabilities,
100442
100572
  commands: prepared.commands,
100443
- configOptions,
100573
+ configOptions: snapshot.configOptions,
100574
+ configurationRevision: snapshot.configurationRevision,
100444
100575
  directoryId: command.directoryId,
100445
100576
  directoryLabel: resolved.directoryLabel,
100446
100577
  kind: "session.ready",
@@ -100573,11 +100704,12 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
100573
100704
  command.additionalDirectoryIds,
100574
100705
  signal
100575
100706
  );
100576
- const configOptions = await sessions.configureSession({
100707
+ const snapshot = await sessions.configureSession({
100577
100708
  additionalDirectories: resolved.additionalDirectories,
100578
100709
  ...await resolveAgent(command.agentId, signal, command.sessionId),
100579
100710
  booleanValue: command.booleanValue,
100580
100711
  configId: command.configId,
100712
+ ...command.expectedConfigurationRevision === void 0 ? {} : { expectedConfigurationRevision: command.expectedConfigurationRevision },
100581
100713
  cwd: resolved.cwd,
100582
100714
  requestId: command.requestId,
100583
100715
  sessionId: command.sessionId,
@@ -100586,7 +100718,7 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
100586
100718
  valueType: command.valueType
100587
100719
  });
100588
100720
  send(socket, {
100589
- configOptions,
100721
+ ...snapshot,
100590
100722
  kind: "session.config.updated",
100591
100723
  requestId: command.requestId,
100592
100724
  sessionId: command.sessionId
@@ -100634,6 +100766,7 @@ async function executeSessionConfigRead(socket, command, config2, resolveAgent,
100634
100766
  );
100635
100767
  send(socket, {
100636
100768
  configOptions: ready.configOptions,
100769
+ configurationRevision: ready.configurationRevision,
100637
100770
  kind: SESSION_CONFIG_READ_RESULT_KIND,
100638
100771
  requestId: command.requestId,
100639
100772
  sessionId: command.sessionId
@@ -100757,6 +100890,7 @@ var init_session5 = __esm({
100757
100890
 
100758
100891
  // src/runtime/command/registry/index.ts
100759
100892
  function createCommandRegistry(context) {
100893
+ const contentWrites = createContentWriteCommands(context);
100760
100894
  const commands = [
100761
100895
  runtimeDiagnosticsCommand,
100762
100896
  runtimePingCommand,
@@ -100765,6 +100899,7 @@ function createCommandRegistry(context) {
100765
100899
  ...createNativeSessionCommands(context),
100766
100900
  ...createAgentCatalogCommands(context),
100767
100901
  ...createAgentSettingsCommands(context),
100902
+ ...contentWrites.commands,
100768
100903
  fileReadCommand,
100769
100904
  sessionCancelCommand,
100770
100905
  sessionCloseCommand,
@@ -100787,16 +100922,22 @@ function createCommandRegistry(context) {
100787
100922
  }
100788
100923
  command.handle(context, message);
100789
100924
  },
100925
+ dispatchChunk: (message) => contentWrites.receiveChunk(decodeContentChunkFrame(message)),
100790
100926
  settle: async () => {
100791
- await Promise.all(commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()]));
100927
+ await Promise.all([
100928
+ contentWrites.settle(),
100929
+ ...commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()])
100930
+ ]);
100792
100931
  }
100793
100932
  };
100794
100933
  }
100795
100934
  var init_registry2 = __esm({
100796
100935
  "src/runtime/command/registry/index.ts"() {
100797
100936
  "use strict";
100937
+ init_protocol();
100798
100938
  init_agent_catalog();
100799
100939
  init_agent_settings();
100940
+ init_content_write();
100800
100941
  init_file_read();
100801
100942
  init_history3();
100802
100943
  init_interactions2();
@@ -100863,6 +101004,7 @@ function connectRelay(url2, signal) {
100863
101004
  dispatcher,
100864
101005
  headers
100865
101006
  });
101007
+ socket.binaryType = "arraybuffer";
100866
101008
  let released = false;
100867
101009
  const release = (reason) => {
100868
101010
  if (released) {
@@ -101254,7 +101396,16 @@ async function runRelayConnection({
101254
101396
  const handleMessage = (event) => {
101255
101397
  const encoded = event.data;
101256
101398
  if (typeof encoded !== "string") {
101257
- failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
101399
+ try {
101400
+ commands.dispatchChunk(encoded);
101401
+ } catch (error62) {
101402
+ failAndClose(
101403
+ new Error("Relay sent an invalid content chunk", { cause: error62 }),
101404
+ 4008,
101405
+ "Invalid content chunk",
101406
+ "protocol"
101407
+ );
101408
+ }
101258
101409
  return;
101259
101410
  }
101260
101411
  try {