@moxt-ai/mobius 0.0.323 → 0.0.325

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 (101) hide show
  1. package/dist/cli/handler/daemon/upgrade.d.ts.map +1 -1
  2. package/dist/cli/handler/daemon/upgrade.js +2 -5
  3. package/dist/cli/handler/daemon/upgrade.js.map +1 -1
  4. package/dist/cli.js +717 -559
  5. package/dist/cli.js.map +4 -4
  6. package/dist/daemon/lifecycle/manager-client.d.ts +1 -1
  7. package/dist/daemon/lifecycle/manager-client.d.ts.map +1 -1
  8. package/dist/daemon/lifecycle/manager-client.js +9 -1
  9. package/dist/daemon/lifecycle/manager-client.js.map +1 -1
  10. package/dist/daemon/lifecycle/worker.js +2 -2
  11. package/dist/daemon/lifecycle/worker.js.map +1 -1
  12. package/dist/daemon/pairing/pair-local-machine.d.ts +1 -0
  13. package/dist/daemon/pairing/pair-local-machine.d.ts.map +1 -1
  14. package/dist/daemon/pairing/pair-local-machine.js +10 -3
  15. package/dist/daemon/pairing/pair-local-machine.js.map +1 -1
  16. package/dist/runtime/command/dispatcher.d.ts +1 -0
  17. package/dist/runtime/command/dispatcher.d.ts.map +1 -1
  18. package/dist/runtime/command/dispatcher.js.map +1 -1
  19. package/dist/runtime/command/registry/content-write/index.d.ts +10 -0
  20. package/dist/runtime/command/registry/content-write/index.d.ts.map +1 -0
  21. package/dist/runtime/command/registry/content-write/index.js +125 -0
  22. package/dist/runtime/command/registry/content-write/index.js.map +1 -0
  23. package/dist/runtime/command/registry/file-read/index.d.ts.map +1 -1
  24. package/dist/runtime/command/registry/file-read/index.js +2 -2
  25. package/dist/runtime/command/registry/file-read/index.js.map +1 -1
  26. package/dist/runtime/command/registry/index.d.ts +2 -1
  27. package/dist/runtime/command/registry/index.d.ts.map +1 -1
  28. package/dist/runtime/command/registry/index.js +9 -1
  29. package/dist/runtime/command/registry/index.js.map +1 -1
  30. package/dist/runtime/command/registry/prompt.d.ts.map +1 -1
  31. package/dist/runtime/command/registry/prompt.js +7 -2
  32. package/dist/runtime/command/registry/prompt.js.map +1 -1
  33. package/dist/runtime/command/registry/session.d.ts.map +1 -1
  34. package/dist/runtime/command/registry/session.js +10 -5
  35. package/dist/runtime/command/registry/session.js.map +1 -1
  36. package/dist/runtime/command/transport.d.ts +1 -0
  37. package/dist/runtime/command/transport.d.ts.map +1 -1
  38. package/dist/runtime/command/transport.js +7 -1
  39. package/dist/runtime/command/transport.js.map +1 -1
  40. package/dist/runtime/relay/connect.d.ts.map +1 -1
  41. package/dist/runtime/relay/connect.js +1 -0
  42. package/dist/runtime/relay/connect.js.map +1 -1
  43. package/dist/runtime/relay/run-relay-connection.d.ts.map +1 -1
  44. package/dist/runtime/relay/run-relay-connection.js +6 -1
  45. package/dist/runtime/relay/run-relay-connection.js.map +1 -1
  46. package/dist/runtime.js +427 -328
  47. package/dist/runtime.js.map +4 -4
  48. package/dist/session/api/session-api.d.ts +11 -4
  49. package/dist/session/api/session-api.d.ts.map +1 -1
  50. package/dist/session/api/session-api.js +3 -1
  51. package/dist/session/api/session-api.js.map +1 -1
  52. package/dist/session/content/external-content-store.d.ts +2 -0
  53. package/dist/session/content/external-content-store.d.ts.map +1 -1
  54. package/dist/session/content/external-content-store.js +5 -0
  55. package/dist/session/content/external-content-store.js.map +1 -1
  56. package/dist/session/content/session-files.d.ts +1 -1
  57. package/dist/session/content/session-files.d.ts.map +1 -1
  58. package/dist/session/content/session-files.js +4 -11
  59. package/dist/session/content/session-files.js.map +1 -1
  60. package/dist/session/live/agent-session.d.ts +2 -2
  61. package/dist/session/live/agent-session.d.ts.map +1 -1
  62. package/dist/session/live/agent-session.js +45 -13
  63. package/dist/session/live/agent-session.js.map +1 -1
  64. package/dist/session/manager/session-manager.d.ts +3 -3
  65. package/dist/session/manager/session-manager.d.ts.map +1 -1
  66. package/dist/session/manager/session-manager.js +2 -2
  67. package/dist/session/manager/session-manager.js.map +1 -1
  68. package/dist/session/prompt/apply-session-configuration.d.ts.map +1 -1
  69. package/dist/session/prompt/apply-session-configuration.js +2 -2
  70. package/dist/session/prompt/apply-session-configuration.js.map +1 -1
  71. package/dist/session/prompt/execute-prompt.d.ts +3 -3
  72. package/dist/session/prompt/execute-prompt.d.ts.map +1 -1
  73. package/dist/session/prompt/execute-prompt.js +2 -2
  74. package/dist/session/prompt/execute-prompt.js.map +1 -1
  75. package/dist/session/prompt/prompt-artifacts.d.ts +7 -22
  76. package/dist/session/prompt/prompt-artifacts.d.ts.map +1 -1
  77. package/dist/session/prompt/prompt-artifacts.js +22 -76
  78. package/dist/session/prompt/prompt-artifacts.js.map +1 -1
  79. package/dist/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  80. package/dist/session/prompt/prompt-lifecycle.js +15 -12
  81. package/dist/session/prompt/prompt-lifecycle.js.map +1 -1
  82. package/dist/session/turn/session-turns.d.ts +5 -9
  83. package/dist/session/turn/session-turns.d.ts.map +1 -1
  84. package/dist/session/turn/session-turns.js +1 -2
  85. package/dist/session/turn/session-turns.js.map +1 -1
  86. package/dist/storage/files/scratch-directory.d.ts +2 -0
  87. package/dist/storage/files/scratch-directory.d.ts.map +1 -1
  88. package/dist/storage/files/scratch-directory.js +7 -1
  89. package/dist/storage/files/scratch-directory.js.map +1 -1
  90. package/dist/util/filesystem/private-path.js +2 -3
  91. package/dist/util/filesystem/private-path.js.map +2 -2
  92. package/dist/util/platform/windows.d.ts.map +1 -1
  93. package/dist/util/platform/windows.js +3 -5
  94. package/dist/util/platform/windows.js.map +1 -1
  95. package/dist/util/process/executable.js +2 -3
  96. package/dist/util/process/executable.js.map +2 -2
  97. package/package.json +2 -2
  98. package/dist/runtime/relay/artifact-download.d.ts +0 -5
  99. package/dist/runtime/relay/artifact-download.d.ts.map +0 -1
  100. package/dist/runtime/relay/artifact-download.js +0 -105
  101. package/dist/runtime/relay/artifact-download.js.map +0 -1
package/dist/runtime.js CHANGED
@@ -500,11 +500,10 @@ function terminateProcess2(child, signal) {
500
500
  var windowsPermissions = `
501
501
  $ErrorActionPreference = 'Stop'
502
502
  $ProgressPreference = 'SilentlyContinue'
503
- Import-Module (Join-Path $PSHOME 'Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1')
504
503
  $item = Get-Item -LiteralPath $env:MOBIUS_PERMISSION_PATH -Force
505
504
  if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'Unsafe path' }
506
505
  $sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
507
- $acl = Get-Acl -LiteralPath $item.FullName
506
+ $acl = $item.GetAccessControl()
508
507
  $owner = $acl.GetOwner([Security.Principal.SecurityIdentifier])
509
508
  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' }
510
509
  if ($env:MOBIUS_PERMISSION_PRIVATE -eq '1') {
@@ -518,7 +517,7 @@ if ($env:MOBIUS_PERMISSION_PRIVATE -eq '1') {
518
517
  $private.SetOwner($sid)
519
518
  $private.SetAccessRuleProtection($true, $false)
520
519
  $private.AddAccessRule($rule)
521
- Set-Acl -LiteralPath $item.FullName -AclObject $private
520
+ $item.SetAccessControl($private)
522
521
  }
523
522
  `;
524
523
  var windowsPermissionsTimeoutMs = 3e4;
@@ -21387,6 +21386,8 @@ var sessionConfigureCommandSchema = external_exports.object({
21387
21386
  ...sessionIdentityFields,
21388
21387
  booleanValue: external_exports.boolean(),
21389
21388
  configId: external_exports.string().min(1).max(128),
21389
+ // Omitted by legacy clients: explicit edits retain last-write-wins behavior.
21390
+ expectedConfigurationRevision: identifierStringSchema.optional(),
21390
21391
  kind: external_exports.literal(SESSION_CONFIGURE_KIND),
21391
21392
  requestId: identifierStringSchema,
21392
21393
  value: external_exports.string().max(500),
@@ -21394,6 +21395,8 @@ var sessionConfigureCommandSchema = external_exports.object({
21394
21395
  }).readonly();
21395
21396
  var sessionConfigUpdatedEventSchema = external_exports.object({
21396
21397
  configOptions: sessionConfigOptionsSchema,
21398
+ // An opaque Runtime generation token; absent on older Runtimes.
21399
+ configurationRevision: identifierStringSchema.optional(),
21397
21400
  kind: external_exports.literal(SESSION_CONFIG_UPDATED_KIND),
21398
21401
  requestId: identifierStringSchema,
21399
21402
  sessionId: identifierStringSchema
@@ -21412,6 +21415,8 @@ var sessionConfigReadCommandSchema = external_exports.object({
21412
21415
  }).readonly();
21413
21416
  var sessionConfigReadResultSchema = external_exports.object({
21414
21417
  configOptions: sessionConfigOptionsSchema,
21418
+ // An opaque Runtime generation token; absent on older Runtimes.
21419
+ configurationRevision: identifierStringSchema.optional(),
21415
21420
  kind: external_exports.literal(SESSION_CONFIG_READ_RESULT_KIND),
21416
21421
  requestId: identifierStringSchema,
21417
21422
  sessionId: identifierStringSchema
@@ -21445,9 +21450,17 @@ var encodeSessionRenamedEvent = (message) => encodeZodMessage(message, sessionRe
21445
21450
  // ../mobius-sdk/src/protocol/session/files/index.ts
21446
21451
  var FILE_READ_KIND = "file.read";
21447
21452
  var FILE_BEGIN_KIND = "file.begin";
21448
- var FILE_CHUNK_KIND = "file.chunk";
21449
21453
  var FILE_END_KIND = "file.end";
21450
21454
  var FILE_FAILED_KIND = "file.failed";
21455
+ var CONTENT_WRITE_KIND = "content.write";
21456
+ var CONTENT_COMMIT_KIND = "content.commit";
21457
+ var CONTENT_READY_KIND = "content.ready";
21458
+ var CONTENT_AVAILABLE_KIND = "content.available";
21459
+ var CONTENT_CHUNK_MAX_BYTES = 192 * 1024;
21460
+ var CONTENT_WRITE_MAX_BYTES = 32 * 1024 * 1024;
21461
+ var PROMPT_IMAGE_MAX_BYTES = 5e6;
21462
+ var CONTENT_CHUNK_MAGIC = new Uint8Array([77, 66, 67, 49]);
21463
+ var CONTENT_CHUNK_HEADER_BYTES = CONTENT_CHUNK_MAGIC.byteLength + 1 + 4;
21451
21464
  var fileReadCommandSchema = external_exports.object({
21452
21465
  kind: external_exports.literal(FILE_READ_KIND),
21453
21466
  maxBytes: external_exports.number().int().positive().max(128 * 1024 * 1024),
@@ -21464,13 +21477,86 @@ var fileBeginEventSchema = external_exports.object({
21464
21477
  requestId: identifierStringSchema,
21465
21478
  sessionId: identifierStringSchema
21466
21479
  }).readonly();
21467
- var fileChunkEventSchema = external_exports.object({
21468
- data: external_exports.string().min(1).max(4e5),
21469
- kind: external_exports.literal(FILE_CHUNK_KIND),
21480
+ var contentWriteCommandSchema = external_exports.object({
21481
+ byteLength: external_exports.number().int().positive().max(CONTENT_WRITE_MAX_BYTES),
21482
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
21483
+ contentKind: external_exports.enum(["image", "resource"]),
21484
+ kind: external_exports.literal(CONTENT_WRITE_KIND),
21485
+ mediaType: external_exports.string().min(1).max(255),
21486
+ name: external_exports.string().min(1).max(500),
21487
+ requestId: identifierStringSchema,
21488
+ sessionId: identifierStringSchema
21489
+ }).readonly();
21490
+ var contentCommitCommandSchema = external_exports.object({
21491
+ kind: external_exports.literal(CONTENT_COMMIT_KIND),
21492
+ requestId: identifierStringSchema,
21493
+ sessionId: identifierStringSchema
21494
+ }).readonly();
21495
+ var contentReadyEventSchema = external_exports.object({
21496
+ kind: external_exports.literal(CONTENT_READY_KIND),
21497
+ requestId: identifierStringSchema,
21498
+ sessionId: identifierStringSchema
21499
+ }).readonly();
21500
+ var contentAvailableEventSchema = external_exports.object({
21501
+ byteLength: external_exports.number().int().positive().max(CONTENT_WRITE_MAX_BYTES),
21502
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
21503
+ kind: external_exports.literal(CONTENT_AVAILABLE_KIND),
21470
21504
  requestId: identifierStringSchema,
21471
- sequence: external_exports.number().int().nonnegative().max(1024),
21472
21505
  sessionId: identifierStringSchema
21473
21506
  }).readonly();
21507
+ function binaryBytes(value) {
21508
+ if (value instanceof ArrayBuffer) {
21509
+ return new Uint8Array(value);
21510
+ }
21511
+ if (ArrayBuffer.isView(value)) {
21512
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
21513
+ }
21514
+ throw new Error("Content chunk must be a binary WebSocket frame");
21515
+ }
21516
+ function encodeContentChunkFrame(frame) {
21517
+ if (!isIdentifier(frame.requestId)) {
21518
+ throw new Error("Content chunk request identifier is invalid");
21519
+ }
21520
+ if (!Number.isSafeInteger(frame.sequence) || frame.sequence < 0 || frame.sequence > 4294967295) {
21521
+ throw new Error("Content chunk sequence is invalid");
21522
+ }
21523
+ if (frame.bytes.byteLength < 1 || frame.bytes.byteLength > CONTENT_CHUNK_MAX_BYTES) {
21524
+ throw new Error("Content chunk size is invalid");
21525
+ }
21526
+ const requestId = new TextEncoder().encode(frame.requestId);
21527
+ if (requestId.byteLength > 255) {
21528
+ throw new Error("Content chunk request identifier is too long");
21529
+ }
21530
+ const encoded = new Uint8Array(CONTENT_CHUNK_HEADER_BYTES + requestId.byteLength + frame.bytes.byteLength);
21531
+ encoded.set(CONTENT_CHUNK_MAGIC, 0);
21532
+ encoded[CONTENT_CHUNK_MAGIC.byteLength] = requestId.byteLength;
21533
+ new DataView(encoded.buffer).setUint32(CONTENT_CHUNK_MAGIC.byteLength + 1, frame.sequence);
21534
+ encoded.set(requestId, CONTENT_CHUNK_HEADER_BYTES);
21535
+ encoded.set(frame.bytes, CONTENT_CHUNK_HEADER_BYTES + requestId.byteLength);
21536
+ return encoded;
21537
+ }
21538
+ function decodeContentChunkFrame(value) {
21539
+ const encoded = binaryBytes(value);
21540
+ if (encoded.byteLength <= CONTENT_CHUNK_HEADER_BYTES || !CONTENT_CHUNK_MAGIC.every((byte, index2) => encoded[index2] === byte)) {
21541
+ throw new Error("Content chunk frame header is invalid");
21542
+ }
21543
+ const requestIdLength = encoded[CONTENT_CHUNK_MAGIC.byteLength] ?? 0;
21544
+ const payloadOffset = CONTENT_CHUNK_HEADER_BYTES + requestIdLength;
21545
+ if (requestIdLength < 1 || payloadOffset >= encoded.byteLength) {
21546
+ throw new Error("Content chunk frame length is invalid");
21547
+ }
21548
+ const requestId = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
21549
+ encoded.subarray(CONTENT_CHUNK_HEADER_BYTES, payloadOffset)
21550
+ );
21551
+ const bytes = encoded.slice(payloadOffset);
21552
+ const sequence = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength).getUint32(
21553
+ CONTENT_CHUNK_MAGIC.byteLength + 1
21554
+ );
21555
+ if (!isIdentifier(requestId) || bytes.byteLength > CONTENT_CHUNK_MAX_BYTES) {
21556
+ throw new Error("Content chunk frame is invalid");
21557
+ }
21558
+ return { bytes, requestId, sequence };
21559
+ }
21474
21560
  var fileEndEventSchema = external_exports.object({
21475
21561
  kind: external_exports.literal(FILE_END_KIND),
21476
21562
  requestId: identifierStringSchema,
@@ -21487,12 +21573,18 @@ var decodeFileReadCommand = (encoded) => decodeZodMessage(encoded, fileReadComma
21487
21573
  var encodeFileReadCommand = (message) => encodeZodMessage(message, fileReadCommandSchema);
21488
21574
  var decodeFileBeginEvent = (encoded) => decodeZodMessage(encoded, fileBeginEventSchema);
21489
21575
  var encodeFileBeginEvent = (message) => encodeZodMessage(message, fileBeginEventSchema);
21490
- var decodeFileChunkEvent = (encoded) => decodeZodMessage(encoded, fileChunkEventSchema);
21491
- var encodeFileChunkEvent = (message) => encodeZodMessage(message, fileChunkEventSchema);
21492
21576
  var decodeFileEndEvent = (encoded) => decodeZodMessage(encoded, fileEndEventSchema);
21493
21577
  var encodeFileEndEvent = (message) => encodeZodMessage(message, fileEndEventSchema);
21494
21578
  var decodeFileFailedEvent = (encoded) => decodeZodMessage(encoded, fileFailedEventSchema);
21495
21579
  var encodeFileFailedEvent = (message) => encodeZodMessage(message, fileFailedEventSchema);
21580
+ var decodeContentWriteCommand = (encoded) => decodeZodMessage(encoded, contentWriteCommandSchema);
21581
+ var encodeContentWriteCommand = (message) => encodeZodMessage(message, contentWriteCommandSchema);
21582
+ var decodeContentCommitCommand = (encoded) => decodeZodMessage(encoded, contentCommitCommandSchema);
21583
+ var encodeContentCommitCommand = (message) => encodeZodMessage(message, contentCommitCommandSchema);
21584
+ var decodeContentReadyEvent = (encoded) => decodeZodMessage(encoded, contentReadyEventSchema);
21585
+ var encodeContentReadyEvent = (message) => encodeZodMessage(message, contentReadyEventSchema);
21586
+ var decodeContentAvailableEvent = (encoded) => decodeZodMessage(encoded, contentAvailableEventSchema);
21587
+ var encodeContentAvailableEvent = (message) => encodeZodMessage(message, contentAvailableEventSchema);
21496
21588
 
21497
21589
  // ../mobius-sdk/src/protocol/shared/tool-input.ts
21498
21590
  var MAX_CONTENT_LENGTH = 1e6;
@@ -21749,6 +21841,7 @@ var sessionReadyEventSchema = external_exports.object({
21749
21841
  capabilities: sessionCapabilitiesSchema,
21750
21842
  commands: external_exports.array(sessionCommandSchema).max(500).readonly(),
21751
21843
  configOptions: sessionConfigOptionsSchema,
21844
+ configurationRevision: identifierStringSchema.optional(),
21752
21845
  directoryId: DIRECTORY_IDENTIFIER,
21753
21846
  directoryLabel: external_exports.string().min(1).max(MAX_WORKSPACE_PATH_LENGTH),
21754
21847
  kind: external_exports.literal(SESSION_READY_KIND),
@@ -21836,21 +21929,16 @@ var SessionHistoryPolicy = Object.freeze({
21836
21929
  ExecutionOnly: "execution-only"
21837
21930
  });
21838
21931
  var MAX_PROMPT_LENGTH = 1e5;
21839
- var MAX_PROMPT_ATTACHMENT_LENGTH = 6e6;
21840
- var promptImageSchema = external_exports.object({
21841
- data: external_exports.string().min(1).max(4e6),
21932
+ var MAX_PROMPT_ATTACHMENT_BYTES = 32 * 1024 * 1024;
21933
+ var promptAttachmentSchema = external_exports.object({
21934
+ byteLength: external_exports.number().int().positive().max(MAX_PROMPT_ATTACHMENT_BYTES),
21935
+ contentKey: external_exports.string().regex(/^[a-f0-9]{64}$/u),
21936
+ kind: external_exports.enum(["image", "resource"]),
21842
21937
  mimeType: external_exports.string().min(1).max(255),
21843
- name: external_exports.string().min(1).max(500)
21844
- }).readonly();
21845
- var promptResourceSchema = external_exports.object({
21846
- mimeType: external_exports.string().max(255),
21847
21938
  name: external_exports.string().min(1).max(500),
21848
- text: external_exports.string().max(1e6),
21849
- type: external_exports.enum(["embedded", "link"]),
21850
21939
  uri: external_exports.string().min(1).max(4096)
21851
21940
  }).readonly();
21852
- var decodePromptImagesValue = (value) => decodeZodValue(value, external_exports.array(promptImageSchema).max(8).readonly());
21853
- var decodePromptResourcesValue = (value) => decodeZodValue(value, external_exports.array(promptResourceSchema).max(16).readonly());
21941
+ var decodePromptAttachmentsValue = (value) => decodeZodValue(value, external_exports.array(promptAttachmentSchema).max(16).readonly());
21854
21942
  function readPromptDisposition(value) {
21855
21943
  const field = value["disposition"];
21856
21944
  if (field === void 0 || field === SessionPromptDisposition.Reject) {
@@ -21869,13 +21957,12 @@ function decodeSessionPromptCommand(encoded) {
21869
21957
  const hasSessionConfigurations = Object.hasOwn(value, "sessionConfigurations");
21870
21958
  assertKind(value, SESSION_PROMPT_KIND);
21871
21959
  const prompt = readText(value, "prompt", MAX_PROMPT_LENGTH);
21872
- const images = decodePromptImagesValue(value["images"]);
21873
- const resources = decodePromptResourcesValue(value["resources"]);
21874
- const attachmentLength = images.reduce((total, image) => total + image.data.length, 0) + resources.reduce((total, resource) => total + resource.text.length, 0);
21875
- if (attachmentLength > MAX_PROMPT_ATTACHMENT_LENGTH) {
21960
+ const attachments = !Object.hasOwn(value, "attachments") && Array.isArray(value["images"]) && Array.isArray(value["resources"]) ? [] : decodePromptAttachmentsValue(value["attachments"]);
21961
+ const attachmentBytes = attachments.reduce((total, attachment) => total + attachment.byteLength, 0);
21962
+ if (attachmentBytes > MAX_PROMPT_ATTACHMENT_BYTES) {
21876
21963
  throw new ProtocolMessageError("Prompt attachments are too large");
21877
21964
  }
21878
- if (prompt.trim().length === 0 && images.length === 0 && resources.length === 0) {
21965
+ if (prompt.trim().length === 0 && attachments.length === 0) {
21879
21966
  throw new ProtocolMessageError("Prompt must contain content");
21880
21967
  }
21881
21968
  const disposition = readPromptDisposition(value);
@@ -21895,13 +21982,12 @@ function decodeSessionPromptCommand(encoded) {
21895
21982
  commandId: readIdentifier(value, "commandId"),
21896
21983
  directoryId: readDirectoryIdentifier(value, "directoryId"),
21897
21984
  disposition,
21898
- images,
21985
+ attachments,
21899
21986
  ...value["historyPolicy"] === void 0 ? {} : {
21900
21987
  historyPolicy: readHistoryPolicy(value)
21901
21988
  },
21902
21989
  kind: SESSION_PROMPT_KIND,
21903
21990
  prompt,
21904
- resources,
21905
21991
  ...hasSessionConfigurations ? { sessionConfigurations } : {},
21906
21992
  sessionId: readIdentifier(value, "sessionId"),
21907
21993
  targetCommandId
@@ -21937,7 +22023,15 @@ function decodeSessionPromptDispositionCommand(encoded) {
21937
22023
  };
21938
22024
  }
21939
22025
  function encodeSessionPromptCommand(message) {
21940
- return JSON.stringify(decodeSessionPromptCommand(JSON.stringify(message)));
22026
+ const command = decodeSessionPromptCommand(JSON.stringify(message));
22027
+ return JSON.stringify({
22028
+ ...command,
22029
+ // Keep text prompts compatible with the previous Runtime release while
22030
+ // attachments move to the content-key protocol. Older Runtimes ignore
22031
+ // `attachments` and require both legacy arrays to be present.
22032
+ images: [],
22033
+ resources: []
22034
+ });
21941
22035
  }
21942
22036
  function encodeSessionCancelCommand(message) {
21943
22037
  return JSON.stringify(decodeSessionCancelCommand(JSON.stringify(message)));
@@ -22022,9 +22116,7 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
22022
22116
  external_exports.object({
22023
22117
  commandId: identifierStringSchema,
22024
22118
  prompt: external_exports.string().max(1e5),
22025
- images: external_exports.array(promptImageSchema).max(8).readonly(),
22026
- // Absent from daemons that predate it; a client then cannot restore the prompt's files.
22027
- resources: external_exports.array(promptResourceSchema).max(16).readonly().optional()
22119
+ attachments: external_exports.array(promptAttachmentSchema).max(16).readonly()
22028
22120
  }).readonly()
22029
22121
  ).readonly(),
22030
22122
  turn: activeTurnSnapshotSchema.nullable()
@@ -22626,6 +22718,16 @@ var serviceToDaemonSignatures = {
22626
22718
  decodeAgentCatalogDiscoverCommand
22627
22719
  ),
22628
22720
  [FILE_READ_KIND]: defineMessageSignature(FILE_READ_KIND, encodeFileReadCommand, decodeFileReadCommand),
22721
+ [CONTENT_WRITE_KIND]: defineMessageSignature(
22722
+ CONTENT_WRITE_KIND,
22723
+ encodeContentWriteCommand,
22724
+ decodeContentWriteCommand
22725
+ ),
22726
+ [CONTENT_COMMIT_KIND]: defineMessageSignature(
22727
+ CONTENT_COMMIT_KIND,
22728
+ encodeContentCommitCommand,
22729
+ decodeContentCommitCommand
22730
+ ),
22629
22731
  [SESSION_LIST_KIND]: defineMessageSignature(SESSION_LIST_KIND, encodeSessionListCommand, decodeSessionListCommand),
22630
22732
  [SESSION_QUERY_KIND]: defineMessageSignature(
22631
22733
  SESSION_QUERY_KIND,
@@ -22785,9 +22887,14 @@ var daemonToServiceSignatures = {
22785
22887
  decodeAgentCatalogDiscoveryFailedEvent
22786
22888
  ),
22787
22889
  [FILE_BEGIN_KIND]: defineMessageSignature(FILE_BEGIN_KIND, encodeFileBeginEvent, decodeFileBeginEvent),
22788
- [FILE_CHUNK_KIND]: defineMessageSignature(FILE_CHUNK_KIND, encodeFileChunkEvent, decodeFileChunkEvent),
22789
22890
  [FILE_END_KIND]: defineMessageSignature(FILE_END_KIND, encodeFileEndEvent, decodeFileEndEvent),
22790
22891
  [FILE_FAILED_KIND]: defineMessageSignature(FILE_FAILED_KIND, encodeFileFailedEvent, decodeFileFailedEvent),
22892
+ [CONTENT_READY_KIND]: defineMessageSignature(CONTENT_READY_KIND, encodeContentReadyEvent, decodeContentReadyEvent),
22893
+ [CONTENT_AVAILABLE_KIND]: defineMessageSignature(
22894
+ CONTENT_AVAILABLE_KIND,
22895
+ encodeContentAvailableEvent,
22896
+ decodeContentAvailableEvent
22897
+ ),
22791
22898
  [SESSION_LISTED_KIND]: defineMessageSignature(
22792
22899
  SESSION_LISTED_KIND,
22793
22900
  encodeSessionsListedEvent,
@@ -32275,7 +32382,7 @@ import { fetch as fetch3 } from "undici";
32275
32382
  // package.json
32276
32383
  var package_default = {
32277
32384
  name: "@moxt-ai/mobius",
32278
- version: "0.0.323",
32385
+ version: "0.0.325",
32279
32386
  description: "Mobius CLI for pairing and running local AI agent services.",
32280
32387
  type: "module",
32281
32388
  bin: {
@@ -83028,59 +83135,6 @@ async function openRuntimeInstallationStore(options, signal) {
83028
83135
  return await RuntimeInstallationStore.open(options, signal);
83029
83136
  }
83030
83137
 
83031
- // src/daemon/control/local-client.ts
83032
- var MAXIMUM_MESSAGE_BYTES = 64 * 1024;
83033
- var managerPlatform = platformSupportFor(process.platform);
83034
-
83035
- // src/daemon/control/protocol.ts
83036
- var workerLaunch = external_exports.object({
83037
- executable: external_exports.string().min(1),
83038
- entry: external_exports.string().min(1),
83039
- version: external_exports.string().min(1).max(128)
83040
- }).readonly();
83041
- var transition = {
83042
- id: external_exports.string().min(1).max(256),
83043
- context: external_exports.string().max(256),
83044
- previous: workerLaunch,
83045
- next: workerLaunch
83046
- };
83047
- var daemonSwitchState = external_exports.discriminatedUnion("kind", [
83048
- external_exports.object({ ...transition, kind: external_exports.literal("switching") }),
83049
- external_exports.object({ ...transition, kind: external_exports.literal("switched") }),
83050
- external_exports.object({
83051
- ...transition,
83052
- kind: external_exports.literal("rolled-back"),
83053
- code: external_exports.string(),
83054
- message: external_exports.string(),
83055
- stdout: external_exports.string().max(4096),
83056
- stderr: external_exports.string().max(4096),
83057
- retryAfter: external_exports.number().int().nonnegative()
83058
- })
83059
- ]).readonly();
83060
- var lifecycleCommand = external_exports.discriminatedUnion("kind", [
83061
- external_exports.object({ kind: external_exports.literal("status") }),
83062
- external_exports.object({ kind: external_exports.literal("ensure"), launch: workerLaunch }),
83063
- external_exports.object({ kind: external_exports.literal("stop") }),
83064
- external_exports.object({
83065
- kind: external_exports.literal("switch"),
83066
- id: external_exports.string().min(1).max(256),
83067
- context: external_exports.string().max(256),
83068
- launch: workerLaunch
83069
- }),
83070
- external_exports.object({ kind: external_exports.literal("switch-status"), wait: external_exports.boolean() })
83071
- ]).readonly();
83072
- var lifecycleResponse = external_exports.discriminatedUnion("kind", [
83073
- external_exports.object({
83074
- kind: external_exports.literal("status"),
83075
- protocolVersion: external_exports.literal(2),
83076
- worker: external_exports.object({ pid: external_exports.number().int().positive(), version: external_exports.string() }).readonly().nullable()
83077
- }),
83078
- external_exports.object({ kind: external_exports.literal("switch-status"), state: daemonSwitchState.nullable() }),
83079
- external_exports.object({ kind: external_exports.literal("stopped") }),
83080
- external_exports.object({ kind: external_exports.literal("switching") }),
83081
- external_exports.object({ kind: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() })
83082
- ]).readonly();
83083
-
83084
83138
  // src/daemon/pairing/pair-local-machine.ts
83085
83139
  var PAIRING_TIMEOUT_MILLISECONDS = 2e4;
83086
83140
  var PAIRING_CODE_PATTERN = /^[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){4}$/;
@@ -83559,7 +83613,8 @@ var PreparedSession = class {
83559
83613
  configOptions;
83560
83614
  prompted;
83561
83615
  restored;
83562
- constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
83616
+ configurationRevision;
83617
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored, configurationRevision) {
83563
83618
  this.agentName = agentName;
83564
83619
  this.agentVersion = agentVersion;
83565
83620
  this.capabilities = capabilities;
@@ -83567,6 +83622,7 @@ var PreparedSession = class {
83567
83622
  this.configOptions = configOptions;
83568
83623
  this.prompted = prompted;
83569
83624
  this.restored = restored;
83625
+ this.configurationRevision = configurationRevision;
83570
83626
  }
83571
83627
  };
83572
83628
  var AgentSettingsSnapshot = class {
@@ -84385,7 +84441,7 @@ async function restoreSession(session, signal, recovery, origin) {
84385
84441
  session.store.remove(session.sessionId);
84386
84442
  stored = session.metrics.measureSync("session-state-read", () => session.store.read(session.sessionId));
84387
84443
  }
84388
- let lifecycleResponse2;
84444
+ let lifecycleResponse;
84389
84445
  if (origin?.kind === "fork") {
84390
84446
  if (!session.capabilities.unstableForkSession) {
84391
84447
  throw new AcpExecutionError(
@@ -84431,7 +84487,7 @@ async function restoreSession(session, signal, recovery, origin) {
84431
84487
  if (recovery.kind === "execution-only") {
84432
84488
  session.startExecutionOnlyCapture();
84433
84489
  }
84434
- lifecycleResponse2 = forked;
84490
+ lifecycleResponse = forked;
84435
84491
  } else if (stored instanceof FoundStoredSession && stored.session.agentId === session.agentId && stored.session.cwd === session.cwd && arraysEqual(stored.session.additionalDirectories, session.additionalDirectories)) {
84436
84492
  session.selectNativeSession(stored.session.nativeSessionId);
84437
84493
  session.setPrompted(stored.session.prompted);
@@ -84445,13 +84501,13 @@ async function restoreSession(session, signal, recovery, origin) {
84445
84501
  "native_history_unsupported"
84446
84502
  );
84447
84503
  }
84448
- lifecycleResponse2 = await session.metrics.measure(
84504
+ lifecycleResponse = await session.metrics.measure(
84449
84505
  "session-load",
84450
84506
  async () => (await session.loadSession(signal, "ACP agent exited while loading a session")).response
84451
84507
  );
84452
84508
  } else if (recovery.kind === "execution-only") {
84453
84509
  try {
84454
- lifecycleResponse2 = await session.metrics.measure(
84510
+ lifecycleResponse = await session.metrics.measure(
84455
84511
  "session-resume",
84456
84512
  async () => await session.race(
84457
84513
  session.agentRequest(
@@ -84481,7 +84537,7 @@ async function restoreSession(session, signal, recovery, origin) {
84481
84537
  }
84482
84538
  session.startExecutionOnlyCapture();
84483
84539
  } else if (session.capabilities.resumeSession) {
84484
- lifecycleResponse2 = await session.metrics.measure(
84540
+ lifecycleResponse = await session.metrics.measure(
84485
84541
  "session-resume",
84486
84542
  async () => await session.race(
84487
84543
  session.agentRequest(
@@ -84499,7 +84555,7 @@ async function restoreSession(session, signal, recovery, origin) {
84499
84555
  )
84500
84556
  );
84501
84557
  } else if (session.capabilities.loadSession) {
84502
- lifecycleResponse2 = await session.metrics.measure(
84558
+ lifecycleResponse = await session.metrics.measure(
84503
84559
  "session-load",
84504
84560
  async () => (await session.loadSession(signal, "ACP agent exited while loading a session")).response
84505
84561
  );
@@ -84542,10 +84598,10 @@ async function restoreSession(session, signal, recovery, origin) {
84542
84598
  if (recovery.kind === "execution-only") {
84543
84599
  session.startExecutionOnlyCapture();
84544
84600
  }
84545
- lifecycleResponse2 = created;
84601
+ lifecycleResponse = created;
84546
84602
  }
84547
- session.restrictSessionCapabilities(lifecycleResponse2);
84548
- session.setConfigOptions(session.sessionConfigProtocol.normalizeLifecycle(lifecycleResponse2));
84603
+ session.restrictSessionCapabilities(lifecycleResponse);
84604
+ session.setConfigOptions(session.sessionConfigProtocol.normalizeLifecycle(lifecycleResponse));
84549
84605
  if (stored instanceof FoundStoredSession && session.restored) {
84550
84606
  await session.restoreStoredConfigurations(stored.session, signal);
84551
84607
  }
@@ -84606,7 +84662,8 @@ var AgentSession = class _AgentSession {
84606
84662
  #agentVersion = "";
84607
84663
  #capabilities = unsupportedSessionCapabilities();
84608
84664
  #commands = [];
84609
- #configRevision = 0;
84665
+ #configRevision = crypto.randomUUID();
84666
+ #configurationChanges = new SerialTaskQueue();
84610
84667
  #configOptions = [];
84611
84668
  #sessionConfigurations = new SessionConfigurations();
84612
84669
  #nativeHistoryOwnership = { kind: "unbound" };
@@ -84775,7 +84832,8 @@ var AgentSession = class _AgentSession {
84775
84832
  this.#commands,
84776
84833
  this.#configOptions,
84777
84834
  this.#prompted,
84778
- this.#restored
84835
+ this.#restored,
84836
+ this.#configRevision
84779
84837
  );
84780
84838
  ensureHistoryLoaded = async (signal) => {
84781
84839
  if (!this.#capabilities.loadSession) {
@@ -84905,12 +84963,23 @@ var AgentSession = class _AgentSession {
84905
84963
  const configured = await this.#adapter.configureSession(this.#target(), selection, signal);
84906
84964
  if (this.#configRevision === configRevision) {
84907
84965
  this.#configOptions = configured;
84966
+ this.#configRevision = crypto.randomUUID();
84967
+ await this.#publishConfiguration();
84908
84968
  }
84909
84969
  };
84910
- configure = async (options) => {
84970
+ // The revision check and Agent mutation share one queue, including legacy callers.
84971
+ // A lost response may have committed: reread after a conflict, never replay an edit blindly.
84972
+ configure = async (options) => this.#configurationChanges.run(async (signal) => {
84973
+ if (options.expectedConfigurationRevision !== void 0 && options.expectedConfigurationRevision !== this.#configRevision) {
84974
+ throw new AcpExecutionError(
84975
+ "Session settings changed. Review the current settings before applying your selection again.",
84976
+ {},
84977
+ "session_configuration_conflict"
84978
+ );
84979
+ }
84911
84980
  await this.#applyConfiguration(
84912
84981
  options.valueType === "boolean" ? { type: "boolean", configId: options.configId, booleanValue: options.booleanValue } : { type: "select", configId: options.configId, value: options.value },
84913
- options.signal
84982
+ signal
84914
84983
  );
84915
84984
  const applied = this.#configOptions.find((option) => option.id === options.configId);
84916
84985
  if (applied === void 0) {
@@ -84918,7 +84987,24 @@ var AgentSession = class _AgentSession {
84918
84987
  }
84919
84988
  this.#sessionConfigurations.select(applied);
84920
84989
  this.writeStoredSession((/* @__PURE__ */ new Date()).toISOString());
84921
- return this.#configOptions;
84990
+ return { configOptions: this.#configOptions, configurationRevision: this.#configRevision };
84991
+ }, options.signal);
84992
+ #publishConfiguration = async () => {
84993
+ const event = this.#store.events.append(this.#mobiusSessionId, {
84994
+ type: "acp",
84995
+ payload: {
84996
+ sessionUpdate: "config_option_update",
84997
+ configOptions: this.#configOptions.map((option) => ({
84998
+ category: option.category,
84999
+ description: option.description,
85000
+ id: option.id,
85001
+ name: option.name,
85002
+ ...option.type === "boolean" ? { type: "boolean", currentValue: option.booleanValue } : { type: "select", currentValue: option.currentValue, options: option.values }
85003
+ }))
85004
+ }
85005
+ });
85006
+ await this.#getObserver().onSessionEvent?.(event);
85007
+ await this.emitConfigUpdate();
84922
85008
  };
84923
85009
  #target = () => ({
84924
85010
  nativeSessionId: this.#updates.sessionId(),
@@ -85096,6 +85182,13 @@ var AgentSession = class _AgentSession {
85096
85182
  this.#nativeHistoryOwnership = { kind: "agent-owned" };
85097
85183
  };
85098
85184
  #processEvent = async (event) => {
85185
+ if (event.kind === "configuration" || event.kind === "mode") {
85186
+ this.#configOptions = event.kind === "configuration" ? event.configOptions : this.#configOptions.map(
85187
+ (option) => option.category === "mode" ? { ...option, currentValue: event.modeId } : option
85188
+ );
85189
+ this.#configRevision = crypto.randomUUID();
85190
+ this.#captureSelectedConfigurationValues();
85191
+ }
85099
85192
  if (this.#sessionLoad.kind === "capturing") {
85100
85193
  await this.#sessionLoad.history.record(event.payload);
85101
85194
  } else if (this.#sessionLoad.kind === "loaded" || this.#sessionLoad.kind === "execution-only") {
@@ -85116,17 +85209,7 @@ var AgentSession = class _AgentSession {
85116
85209
  });
85117
85210
  return;
85118
85211
  case "configuration":
85119
- this.#configOptions = event.configOptions;
85120
- this.#configRevision += 1;
85121
- this.#captureSelectedConfigurationValues();
85122
- await this.emitConfigUpdate();
85123
- return;
85124
85212
  case "mode":
85125
- this.#configOptions = this.#configOptions.map(
85126
- (option) => option.category === "mode" ? { ...option, currentValue: event.modeId } : option
85127
- );
85128
- this.#configRevision += 1;
85129
- this.#captureSelectedConfigurationValues();
85130
85213
  await this.emitConfigUpdate();
85131
85214
  return;
85132
85215
  case "info":
@@ -85189,6 +85272,7 @@ var AgentSession = class _AgentSession {
85189
85272
  }
85190
85273
  setConfigOptions(options) {
85191
85274
  this.#configOptions = options;
85275
+ this.#configRevision = crypto.randomUUID();
85192
85276
  }
85193
85277
  setNativeHistoryOwnership(ownership) {
85194
85278
  this.#nativeHistoryOwnership = ownership;
@@ -85210,6 +85294,7 @@ var AgentSession = class _AgentSession {
85210
85294
  emitConfigUpdate = async () => {
85211
85295
  await this.#getObserver().onConfig({
85212
85296
  configOptions: this.#configOptions,
85297
+ configurationRevision: this.#configRevision,
85213
85298
  kind: SESSION_CONFIG_UPDATED_KIND,
85214
85299
  requestId: `agent-update-${crypto.randomUUID()}`,
85215
85300
  sessionId: this.#mobiusSessionId
@@ -85608,7 +85693,7 @@ var SessionManager2 = class {
85608
85693
  (option) => option.id === "model" || option.category === "model"
85609
85694
  );
85610
85695
  const modelIsFixed = modelOption2?.type === "select" && modelOption2.values.length < 2 && modelOption2.currentValue === model;
85611
- const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : await runtime.configure({
85696
+ const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : (await runtime.configure({
85612
85697
  ...options2,
85613
85698
  booleanValue: false,
85614
85699
  configId: modelOption2?.id ?? "model",
@@ -85616,7 +85701,7 @@ var SessionManager2 = class {
85616
85701
  sessionId: `agent-settings-${crypto.randomUUID()}`,
85617
85702
  value: model,
85618
85703
  valueType: "select"
85619
- });
85704
+ })).configOptions;
85620
85705
  return new AgentSettingsSnapshot(
85621
85706
  prepared.agentName,
85622
85707
  prepared.agentVersion,
@@ -86452,19 +86537,22 @@ function promptContent(command) {
86452
86537
  if (command.prompt.length > 0) {
86453
86538
  content.push({ text: command.prompt, type: "text" });
86454
86539
  }
86455
- for (const image of command.images) {
86456
- content.push({
86457
- data: image.data,
86458
- mimeType: image.mimeType,
86459
- type: "image"
86460
- });
86461
- }
86462
- for (const resource of command.resources) {
86540
+ for (const attachment of command.attachments) {
86463
86541
  content.push(
86464
- resource.type === "embedded" ? {
86465
- resource: { mimeType: resource.mimeType, text: resource.text, uri: resource.uri },
86466
- type: "resource"
86467
- } : { mimeType: resource.mimeType, name: resource.name, type: "resource_link", uri: resource.uri }
86542
+ attachment.kind === "image" ? {
86543
+ byteLength: attachment.byteLength,
86544
+ imageKey: attachment.contentKey,
86545
+ mimeType: attachment.mimeType,
86546
+ type: "external_image",
86547
+ uri: attachment.uri
86548
+ } : {
86549
+ byteLength: attachment.byteLength,
86550
+ encoding: "text",
86551
+ mimeType: attachment.mimeType,
86552
+ resourceKey: attachment.contentKey,
86553
+ type: "external_resource",
86554
+ uri: attachment.uri
86555
+ }
86468
86556
  );
86469
86557
  }
86470
86558
  return content;
@@ -87143,8 +87231,7 @@ var SessionTurns = class {
87143
87231
  queued.push({
87144
87232
  commandId: candidate.command.commandId,
87145
87233
  prompt: candidate.command.prompt,
87146
- images: candidate.command.images,
87147
- resources: candidate.command.resources
87234
+ attachments: candidate.command.attachments
87148
87235
  });
87149
87236
  }
87150
87237
  return { turn: turnSnapshot ?? null, queued };
@@ -87441,7 +87528,7 @@ import { join as join27 } from "node:path";
87441
87528
 
87442
87529
  // src/storage/files/scratch-directory.ts
87443
87530
  import { randomUUID as randomUUID15 } from "node:crypto";
87444
- import { lstat as lstat4, mkdir as mkdir5, readdir as readdir6, realpath as realpath7, rename as rename4, rm as rm3, stat as stat6, utimes, writeFile as writeFile2 } from "node:fs/promises";
87531
+ import { lstat as lstat4, mkdir as mkdir5, readdir as readdir6, readFile as readFile6, realpath as realpath7, rename as rename4, rm as rm3, stat as stat6, utimes, writeFile as writeFile2 } from "node:fs/promises";
87445
87532
  import { dirname as dirname11, join as join26 } from "node:path";
87446
87533
  async function atomicWrite(path7, bytes, signal) {
87447
87534
  const temporary = `${path7}.${randomUUID15()}.tmp`;
@@ -87505,6 +87592,12 @@ var ScratchDirectory = class {
87505
87592
  return null;
87506
87593
  }
87507
87594
  };
87595
+ /** Reads a regular file owned by this scratch directory after validating its canonical path. */
87596
+ read = async (path7, signal) => {
87597
+ const resolved = await this.resolveFile(path7);
87598
+ signal.throwIfAborted();
87599
+ return resolved === null ? null : new Uint8Array(await readFile6(resolved, { signal }));
87600
+ };
87508
87601
  removeAll = async () => {
87509
87602
  await rm3(this.#root, { force: true, recursive: true });
87510
87603
  };
@@ -87606,6 +87699,11 @@ var LocalExternalContentStore = class {
87606
87699
  signal.throwIfAborted();
87607
87700
  return resolvedPath === null ? null : { mediaType: entry.mediaType, name: entry.name, path: resolvedPath };
87608
87701
  };
87702
+ read = (sessionId, request, signal) => this.#track(async () => {
87703
+ const active = AbortSignal.any([signal, this.#lifetime.signal]);
87704
+ const resolved = await this.#resolve(sessionId, request, active);
87705
+ return resolved === null ? null : this.#scratch.read(resolved.path, active);
87706
+ });
87609
87707
  [Symbol.asyncDispose] = async () => {
87610
87708
  clearInterval(this.#expirationTimer);
87611
87709
  this.#lifetime.abort();
@@ -88271,6 +88369,12 @@ function send(socket, message) {
88271
88369
  }
88272
88370
  socket.send(encodeDaemonToServiceMessage(message));
88273
88371
  }
88372
+ function sendContentChunk(socket, requestId, sequence, bytes) {
88373
+ if (socket.readyState !== WebSocket2.OPEN) {
88374
+ throw new Error("Relay connection is not open");
88375
+ }
88376
+ socket.send(encodeContentChunkFrame({ bytes, requestId, sequence }));
88377
+ }
88274
88378
  function reportOperationFailure(socket, requestId, sessionId, error62, runtimeTimings = []) {
88275
88379
  if (socket.readyState !== WebSocket2.OPEN) {
88276
88380
  return;
@@ -88890,13 +88994,133 @@ function createAgentSettingsCommands(context) {
88890
88994
  ];
88891
88995
  }
88892
88996
 
88997
+ // src/runtime/command/registry/content-write/index.ts
88998
+ import { createHash as createHash9 } from "node:crypto";
88999
+ function createContentWriteCommands(context) {
89000
+ const pending = /* @__PURE__ */ new Map();
89001
+ const operations = /* @__PURE__ */ new Set();
89002
+ const fail = (command, error62) => {
89003
+ pending.delete(command.requestId);
89004
+ send(context.socket, {
89005
+ code: "content_write_failed",
89006
+ kind: FILE_FAILED_KIND,
89007
+ message: error62 instanceof Error ? error62.message : "The content could not be stored",
89008
+ requestId: command.requestId,
89009
+ sessionId: command.sessionId
89010
+ });
89011
+ };
89012
+ const write = {
89013
+ kind: CONTENT_WRITE_KIND,
89014
+ handle: (_commandContext, message) => {
89015
+ if (message.kind !== CONTENT_WRITE_KIND) {
89016
+ throw new Error("Command kind mismatch");
89017
+ }
89018
+ const existing = pending.get(message.requestId);
89019
+ if (existing !== void 0) {
89020
+ if (JSON.stringify(existing.command) !== JSON.stringify(message)) {
89021
+ fail(message, new Error("The content request identifier is already in use"));
89022
+ return;
89023
+ }
89024
+ } else {
89025
+ pending.set(message.requestId, {
89026
+ bytes: new Uint8Array(message.byteLength),
89027
+ command: message,
89028
+ hash: createHash9("sha256"),
89029
+ receivedBytes: 0,
89030
+ sequence: 0
89031
+ });
89032
+ }
89033
+ send(context.socket, {
89034
+ kind: CONTENT_READY_KIND,
89035
+ requestId: message.requestId,
89036
+ sessionId: message.sessionId
89037
+ });
89038
+ }
89039
+ };
89040
+ const commit = {
89041
+ kind: CONTENT_COMMIT_KIND,
89042
+ handle: (_commandContext, message) => {
89043
+ if (message.kind !== CONTENT_COMMIT_KIND) {
89044
+ throw new Error("Command kind mismatch");
89045
+ }
89046
+ const state = pending.get(message.requestId);
89047
+ if (state === void 0 || state.command.sessionId !== message.sessionId) {
89048
+ fail(message, new Error("The content upload is unavailable"));
89049
+ return;
89050
+ }
89051
+ pending.delete(message.requestId);
89052
+ const operation = (async () => {
89053
+ try {
89054
+ if (state.receivedBytes !== state.command.byteLength) {
89055
+ throw new Error("The content upload ended at an unexpected length");
89056
+ }
89057
+ if (state.hash.digest("hex") !== state.command.contentKey) {
89058
+ throw new Error("The content upload failed integrity validation");
89059
+ }
89060
+ const stored = await context.externalContent.put({
89061
+ bytes: state.bytes,
89062
+ kind: state.command.contentKind,
89063
+ mediaType: state.command.mediaType,
89064
+ name: state.command.name,
89065
+ sessionId: state.command.sessionId,
89066
+ signal: context.signal
89067
+ });
89068
+ if (stored.key !== state.command.contentKey || stored.byteLength !== state.command.byteLength) {
89069
+ throw new Error("Stored content did not match the upload");
89070
+ }
89071
+ send(context.socket, {
89072
+ byteLength: stored.byteLength,
89073
+ contentKey: stored.key,
89074
+ kind: CONTENT_AVAILABLE_KIND,
89075
+ requestId: message.requestId,
89076
+ sessionId: message.sessionId
89077
+ });
89078
+ } catch (error62) {
89079
+ if (!context.signal.aborted) {
89080
+ fail(message, error62);
89081
+ }
89082
+ }
89083
+ })();
89084
+ operations.add(operation);
89085
+ void operation.finally(() => operations.delete(operation));
89086
+ }
89087
+ };
89088
+ return {
89089
+ commands: [write, commit],
89090
+ receiveChunk: (frame) => {
89091
+ const state = pending.get(frame.requestId);
89092
+ if (state === void 0) {
89093
+ context.diagnostics.daemonDiagnostic("content_chunk_discarded", frame.requestId, {
89094
+ reason: "upload_unavailable",
89095
+ sequence: frame.sequence.toString()
89096
+ });
89097
+ return;
89098
+ }
89099
+ if (frame.sequence !== state.sequence || state.receivedBytes + frame.bytes.byteLength > state.command.byteLength) {
89100
+ fail(state.command, new Error("The content upload contains invalid chunks"));
89101
+ return;
89102
+ }
89103
+ state.bytes.set(frame.bytes, state.receivedBytes);
89104
+ state.hash.update(frame.bytes);
89105
+ pending.set(frame.requestId, {
89106
+ ...state,
89107
+ receivedBytes: state.receivedBytes + frame.bytes.byteLength,
89108
+ sequence: state.sequence + 1
89109
+ });
89110
+ },
89111
+ settle: async () => {
89112
+ pending.clear();
89113
+ await Promise.allSettled([...operations]);
89114
+ }
89115
+ };
89116
+ }
89117
+
88893
89118
  // src/runtime/command/registry/file-read/index.ts
88894
89119
  import { WebSocket as WebSocket5 } from "undici";
88895
89120
 
88896
89121
  // src/session/content/session-files.ts
88897
89122
  import { basename as basename8, extname as extname2, isAbsolute as isAbsolute16, join as join31, normalize as normalize3, resolve as resolve17 } from "node:path";
88898
89123
  import { fileURLToPath as fileURLToPath2 } from "node:url";
88899
- var CHUNK_BYTES = 192 * 1024;
88900
89124
  function requestedPath(value) {
88901
89125
  if (value.includes("\0")) {
88902
89126
  return null;
@@ -88936,7 +89160,7 @@ async function resolveFile(value, workingDirectory, signal) {
88936
89160
  }
88937
89161
  return null;
88938
89162
  }
88939
- async function streamLocalFile(command, send2, signal, workingDirectory, externalContent) {
89163
+ async function streamLocalFile(command, send2, sendChunk, signal, workingDirectory, externalContent) {
88940
89164
  const external = await externalContent.resolve(command.sessionId, command.path, signal);
88941
89165
  const target = external?.path ?? await resolveFile(command.path, workingDirectory, signal);
88942
89166
  if (target === null) {
@@ -88958,14 +89182,8 @@ async function streamLocalFile(command, send2, signal, workingDirectory, externa
88958
89182
  sessionId: command.sessionId
88959
89183
  });
88960
89184
  let sequence = 0;
88961
- for await (const bytes of readFileChunks(target, CHUNK_BYTES, signal)) {
88962
- send2({
88963
- data: Buffer.from(bytes).toString("base64"),
88964
- kind: FILE_CHUNK_KIND,
88965
- requestId: command.requestId,
88966
- sequence,
88967
- sessionId: command.sessionId
88968
- });
89185
+ for await (const bytes of readFileChunks(target, CONTENT_CHUNK_MAX_BYTES, signal)) {
89186
+ sendChunk(command.requestId, sequence, bytes);
88969
89187
  sequence += 1;
88970
89188
  }
88971
89189
  const after = await readPathMetadata(target);
@@ -88990,6 +89208,7 @@ var fileReadCommand = {
88990
89208
  void streamLocalFile(
88991
89209
  message,
88992
89210
  (response) => send(socket, response),
89211
+ (requestId, sequence, bytes) => sendContentChunk(socket, requestId, sequence, bytes),
88993
89212
  signal,
88994
89213
  sessions.findSessions([message.sessionId])[0]?.cwd ?? null,
88995
89214
  externalContent
@@ -89587,7 +89806,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
89587
89806
  if (sessionConfigurationMatches(option, selection)) {
89588
89807
  return options;
89589
89808
  }
89590
- return await sessions.configureSession({
89809
+ return (await sessions.configureSession({
89591
89810
  adapter,
89592
89811
  additionalDirectories,
89593
89812
  agent,
@@ -89599,7 +89818,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
89599
89818
  signal,
89600
89819
  value: selection.value,
89601
89820
  valueType: selection.valueType
89602
- });
89821
+ })).configOptions;
89603
89822
  }
89604
89823
  async function applyCompleteSessionConfiguration(sessions, initialOptions, selections, agent, adapter, cwd, additionalDirectories, commandId, sessionId, signal) {
89605
89824
  let options = initialOptions;
@@ -89636,8 +89855,7 @@ function warnIgnoredSetting(event, commandId, sessionId, agentId, selection, cat
89636
89855
  }
89637
89856
 
89638
89857
  // src/session/prompt/prompt-artifacts.ts
89639
- var ARTIFACT_SCHEME = "mobius-artifact:";
89640
- var MAX_AGENT_IMAGE_BASE64_LENGTH = 4e6;
89858
+ import { createHash as createHash10 } from "node:crypto";
89641
89859
  var MAX_AGENT_RESOURCE_BYTE_LENGTH = 1e6;
89642
89860
  var PromptArtifactError = class extends OperationError {
89643
89861
  constructor(code, message, options = {}) {
@@ -89653,99 +89871,60 @@ var ResolvedPromptArtifacts = class {
89653
89871
  this.resources = resources;
89654
89872
  }
89655
89873
  };
89656
- var PromptArtifactReference = class {
89657
- artifactId;
89658
- byteLength;
89659
- resource;
89660
- sha256;
89661
- constructor(artifactId, byteLength, sha256, resource) {
89662
- this.artifactId = artifactId;
89663
- this.byteLength = byteLength;
89664
- this.resource = resource;
89665
- this.sha256 = sha256;
89666
- }
89667
- };
89668
- function parseReference(resource) {
89669
- let url2;
89670
- try {
89671
- url2 = new URL(resource.uri);
89672
- } catch (cause) {
89673
- throw new PromptArtifactError("artifact_reference_invalid", "The prompt artifact reference is invalid", {
89674
- cause
89675
- });
89676
- }
89677
- const keys = [...url2.searchParams.keys()].sort();
89678
- const artifactId = url2.pathname;
89679
- const encodedByteLength = url2.searchParams.get("byteLength") ?? "";
89680
- const sha256 = url2.searchParams.get("sha256") ?? "";
89681
- const byteLength = Number(encodedByteLength);
89682
- 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)) {
89683
- throw new PromptArtifactError("artifact_reference_invalid", "The prompt artifact reference is invalid");
89684
- }
89685
- return new PromptArtifactReference(artifactId, byteLength, sha256, resource);
89686
- }
89687
89874
  function isTextMediaType(mediaType) {
89688
89875
  return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType.endsWith("+json") || mediaType === "application/xml" || mediaType.endsWith("+xml");
89689
89876
  }
89690
- async function resolvePromptArtifacts(download, command, signal) {
89691
- const images = [...command.images];
89877
+ async function resolvePromptArtifacts(read, attachments, signal) {
89878
+ const images = [];
89692
89879
  const resources = [];
89693
- for (const resource of command.resources) {
89880
+ for (const attachment of attachments) {
89694
89881
  signal.throwIfAborted();
89695
- if (!resource.uri.startsWith(ARTIFACT_SCHEME)) {
89696
- resources.push(resource);
89697
- continue;
89882
+ const bytes = await read(attachment, signal);
89883
+ if (bytes.byteLength !== attachment.byteLength || createHash10("sha256").update(bytes).digest("hex") !== attachment.contentKey) {
89884
+ throw new PromptArtifactError(
89885
+ "artifact_integrity_failed",
89886
+ "The prompt attachment failed integrity validation"
89887
+ );
89698
89888
  }
89699
- const reference = parseReference(resource);
89700
- const bytes = await download(
89701
- {
89702
- artifactId: reference.artifactId,
89703
- byteLength: reference.byteLength,
89704
- commandId: command.commandId,
89705
- mimeType: reference.resource.mimeType,
89706
- sessionId: command.sessionId,
89707
- sha256: reference.sha256
89708
- },
89709
- signal
89710
- );
89711
- if (resource.mimeType.startsWith("image/")) {
89712
- const data = Buffer.from(bytes).toString("base64");
89713
- if (data.length > MAX_AGENT_IMAGE_BASE64_LENGTH || images.length >= 8) {
89889
+ if (attachment.kind === "image") {
89890
+ if (bytes.byteLength > PROMPT_IMAGE_MAX_BYTES || images.length >= 8) {
89714
89891
  throw new PromptArtifactError(
89715
89892
  "artifact_too_large_for_agent",
89716
- "The prompt artifact exceeds the Agent image limit"
89893
+ "The prompt attachment exceeds the Agent image limit"
89717
89894
  );
89718
89895
  }
89719
- images.push({ data, mimeType: resource.mimeType, name: resource.name });
89896
+ images.push({
89897
+ data: Buffer.from(bytes).toString("base64"),
89898
+ mimeType: attachment.mimeType,
89899
+ name: attachment.name
89900
+ });
89720
89901
  continue;
89721
89902
  }
89722
- if (!isTextMediaType(resource.mimeType)) {
89903
+ if (!isTextMediaType(attachment.mimeType)) {
89723
89904
  throw new PromptArtifactError(
89724
89905
  "artifact_media_type_unsupported",
89725
- "The Agent cannot consume this prompt artifact media type"
89906
+ "The Agent cannot consume this prompt attachment media type"
89726
89907
  );
89727
89908
  }
89728
89909
  if (bytes.byteLength > MAX_AGENT_RESOURCE_BYTE_LENGTH || resources.length >= 16) {
89729
89910
  throw new PromptArtifactError(
89730
89911
  "artifact_too_large_for_agent",
89731
- "The prompt artifact exceeds the Agent resource limit"
89912
+ "The prompt attachment exceeds the Agent resource limit"
89732
89913
  );
89733
89914
  }
89734
89915
  let text2;
89735
89916
  try {
89736
89917
  text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
89737
89918
  } catch (cause) {
89738
- throw new PromptArtifactError("artifact_encoding_invalid", "The prompt artifact is not valid UTF-8 text", {
89739
- cause
89740
- });
89919
+ throw new PromptArtifactError(
89920
+ "artifact_encoding_invalid",
89921
+ "The prompt attachment is not valid UTF-8 text",
89922
+ {
89923
+ cause
89924
+ }
89925
+ );
89741
89926
  }
89742
- resources.push({
89743
- mimeType: resource.mimeType,
89744
- name: resource.name,
89745
- text: text2,
89746
- type: "embedded",
89747
- uri: resource.uri
89748
- });
89927
+ resources.push({ mimeType: attachment.mimeType, text: text2, type: "embedded", uri: attachment.uri });
89749
89928
  }
89750
89929
  return new ResolvedPromptArtifacts(images, resources);
89751
89930
  }
@@ -89756,7 +89935,7 @@ async function executePrompt({
89756
89935
  eventProjection,
89757
89936
  command,
89758
89937
  resolveAgent,
89759
- downloadArtifact,
89938
+ readArtifact,
89760
89939
  reportFailure,
89761
89940
  directories,
89762
89941
  sessions,
@@ -89838,7 +90017,7 @@ async function executePrompt({
89838
90017
  stream.epoch,
89839
90018
  availability.disposition
89840
90019
  );
89841
- const attachments = await resolvePromptArtifacts(downloadArtifact, command, signal);
90020
+ const attachments = await resolvePromptArtifacts(readArtifact, command.attachments, signal);
89842
90021
  let stopReason;
89843
90022
  if (availability.disposition === SessionPromptDisposition.Steer) {
89844
90023
  await sessions.steerPrompt({
@@ -89898,115 +90077,6 @@ async function executePrompt({
89898
90077
  }
89899
90078
  }
89900
90079
 
89901
- // src/runtime/relay/artifact-download.ts
89902
- import { createHash as createHash9 } from "node:crypto";
89903
- import { fetch as fetch4 } from "undici";
89904
- var ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS = 2e4;
89905
- function downloadUrl(config2, artifact) {
89906
- const url2 = relayHttpEndpoint(config2.relayUrl);
89907
- let channelPath = "";
89908
- if (url2.pathname.endsWith("/daemon")) {
89909
- channelPath = url2.pathname.slice(0, -"/daemon".length);
89910
- } else {
89911
- const runtimePath = "/v1/runtimes/connect";
89912
- if (!url2.pathname.endsWith(runtimePath)) {
89913
- throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
89914
- }
89915
- channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
89916
- }
89917
- url2.pathname = `${channelPath}/sessions/${encodeURIComponent(artifact.sessionId)}/commands/${encodeURIComponent(artifact.commandId)}/artifacts/${encodeURIComponent(artifact.artifactId)}/content`;
89918
- return url2;
89919
- }
89920
- async function responseBytes(response, expectedByteLength, signal) {
89921
- const declaredLength = response.headers.get("content-length") ?? "";
89922
- if (!/^(0|[1-9][0-9]*)$/.test(declaredLength) || Number(declaredLength) !== expectedByteLength || response.body === null) {
89923
- throw new PromptArtifactError("artifact_integrity_failed", "The prompt artifact failed integrity validation");
89924
- }
89925
- const reader = response.body.getReader();
89926
- const chunks = [];
89927
- let byteLength = 0;
89928
- try {
89929
- while (true) {
89930
- signal.throwIfAborted();
89931
- const chunk = await reader.read();
89932
- signal.throwIfAborted();
89933
- if (chunk.done) {
89934
- break;
89935
- }
89936
- byteLength += chunk.value.byteLength;
89937
- if (byteLength > expectedByteLength) {
89938
- await reader.cancel("Artifact response exceeded its authorization");
89939
- throw new PromptArtifactError(
89940
- "artifact_integrity_failed",
89941
- "The prompt artifact failed integrity validation"
89942
- );
89943
- }
89944
- chunks.push(chunk.value);
89945
- }
89946
- } finally {
89947
- reader.releaseLock();
89948
- }
89949
- if (byteLength !== expectedByteLength) {
89950
- throw new PromptArtifactError("artifact_integrity_failed", "The prompt artifact failed integrity validation");
89951
- }
89952
- const bytes = new Uint8Array(byteLength);
89953
- let offset = 0;
89954
- for (const chunk of chunks) {
89955
- bytes.set(chunk, offset);
89956
- offset += chunk.byteLength;
89957
- }
89958
- return bytes;
89959
- }
89960
- async function downloadPromptArtifact(config2, artifact, signal) {
89961
- signal.throwIfAborted();
89962
- const timeoutSignal = AbortSignal.timeout(ARTIFACT_DOWNLOAD_TIMEOUT_MILLISECONDS);
89963
- const requestSignal = AbortSignal.any([signal, timeoutSignal]);
89964
- let response;
89965
- try {
89966
- response = await fetch4(downloadUrl(config2, artifact), {
89967
- dispatcher: environmentProxyDispatcher,
89968
- headers: {
89969
- accept: artifact.mimeType,
89970
- "accept-encoding": "identity",
89971
- ...relayAuthorization(config2.relayUrl)
89972
- },
89973
- signal: requestSignal
89974
- });
89975
- if (!response.ok) {
89976
- await response.body?.cancel();
89977
- throw new PromptArtifactError(
89978
- "artifact_download_rejected",
89979
- "The Relay rejected the prompt artifact download"
89980
- );
89981
- }
89982
- 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) {
89983
- await response.body?.cancel();
89984
- throw new PromptArtifactError(
89985
- "artifact_integrity_failed",
89986
- "The prompt artifact failed integrity validation"
89987
- );
89988
- }
89989
- const bytes = await responseBytes(response, artifact.byteLength, requestSignal);
89990
- if (createHash9("sha256").update(bytes).digest("hex") !== artifact.sha256) {
89991
- throw new PromptArtifactError(
89992
- "artifact_integrity_failed",
89993
- "The prompt artifact failed integrity validation"
89994
- );
89995
- }
89996
- return bytes;
89997
- } catch (error62) {
89998
- signal.throwIfAborted();
89999
- if (error62 instanceof PromptArtifactError) {
90000
- throw error62;
90001
- }
90002
- throw new PromptArtifactError(
90003
- timeoutSignal.aborted ? "artifact_download_timeout" : "artifact_download_failed",
90004
- timeoutSignal.aborted ? "The prompt artifact download timed out" : "The prompt artifact could not be downloaded",
90005
- { cause: error62 }
90006
- );
90007
- }
90008
- }
90009
-
90010
90080
  // src/runtime/command/registry/prompt.ts
90011
90081
  var sessionPromptCommand = {
90012
90082
  kind: SESSION_PROMPT_KIND,
@@ -90060,7 +90130,17 @@ var sessionPromptCommand = {
90060
90130
  eventProjection: context.eventProjection,
90061
90131
  command: message,
90062
90132
  resolveAgent: context.resolveAgent,
90063
- downloadArtifact: (artifact, signal) => downloadPromptArtifact(context.config, artifact, signal),
90133
+ readArtifact: async (artifact, signal) => {
90134
+ const bytes = await context.externalContent.read(
90135
+ message.sessionId,
90136
+ `mobius-content:${artifact.kind}:${artifact.contentKey}`,
90137
+ signal
90138
+ );
90139
+ if (bytes === null) {
90140
+ throw new Error("The prompt attachment is unavailable");
90141
+ }
90142
+ return bytes;
90143
+ },
90064
90144
  reportFailure: context.diagnostics.daemonDiagnostic,
90065
90145
  directories: context.directories,
90066
90146
  sessions: context.sessions,
@@ -90142,7 +90222,7 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
90142
90222
  };
90143
90223
  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);
90144
90224
  const configuration = command.configuration;
90145
- const configOptions = configuration === void 0 ? prepared.configOptions : await metrics.measure(
90225
+ const snapshot = configuration === void 0 ? prepared : await metrics.measure(
90146
90226
  "configuration",
90147
90227
  async () => await sessions.configureSession({
90148
90228
  ...preparation,
@@ -90160,7 +90240,8 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
90160
90240
  agentVersion: prepared.agentVersion,
90161
90241
  capabilities: prepared.capabilities,
90162
90242
  commands: prepared.commands,
90163
- configOptions,
90243
+ configOptions: snapshot.configOptions,
90244
+ configurationRevision: snapshot.configurationRevision,
90164
90245
  directoryId: command.directoryId,
90165
90246
  directoryLabel: resolved.directoryLabel,
90166
90247
  kind: "session.ready",
@@ -90293,11 +90374,12 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
90293
90374
  command.additionalDirectoryIds,
90294
90375
  signal
90295
90376
  );
90296
- const configOptions = await sessions.configureSession({
90377
+ const snapshot = await sessions.configureSession({
90297
90378
  additionalDirectories: resolved.additionalDirectories,
90298
90379
  ...await resolveAgent(command.agentId, signal, command.sessionId),
90299
90380
  booleanValue: command.booleanValue,
90300
90381
  configId: command.configId,
90382
+ ...command.expectedConfigurationRevision === void 0 ? {} : { expectedConfigurationRevision: command.expectedConfigurationRevision },
90301
90383
  cwd: resolved.cwd,
90302
90384
  requestId: command.requestId,
90303
90385
  sessionId: command.sessionId,
@@ -90306,7 +90388,7 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
90306
90388
  valueType: command.valueType
90307
90389
  });
90308
90390
  send(socket, {
90309
- configOptions,
90391
+ ...snapshot,
90310
90392
  kind: "session.config.updated",
90311
90393
  requestId: command.requestId,
90312
90394
  sessionId: command.sessionId
@@ -90426,6 +90508,7 @@ async function executeSessionConfigRead(socket, command, config2, resolveAgent,
90426
90508
  );
90427
90509
  send(socket, {
90428
90510
  configOptions: ready.configOptions,
90511
+ configurationRevision: ready.configurationRevision,
90429
90512
  kind: SESSION_CONFIG_READ_RESULT_KIND,
90430
90513
  requestId: command.requestId,
90431
90514
  sessionId: command.sessionId
@@ -90465,6 +90548,7 @@ var sessionConfigReadCommand = {
90465
90548
 
90466
90549
  // src/runtime/command/registry/index.ts
90467
90550
  function createCommandRegistry(context) {
90551
+ const contentWrites = createContentWriteCommands(context);
90468
90552
  const commands = [
90469
90553
  runtimeDiagnosticsCommand,
90470
90554
  runtimePingCommand,
@@ -90473,6 +90557,7 @@ function createCommandRegistry(context) {
90473
90557
  ...createNativeSessionCommands(context),
90474
90558
  ...createAgentCatalogCommands(context),
90475
90559
  ...createAgentSettingsCommands(context),
90560
+ ...contentWrites.commands,
90476
90561
  fileReadCommand,
90477
90562
  sessionCancelCommand,
90478
90563
  sessionCloseCommand,
@@ -90495,8 +90580,12 @@ function createCommandRegistry(context) {
90495
90580
  }
90496
90581
  command.handle(context, message);
90497
90582
  },
90583
+ dispatchChunk: (message) => contentWrites.receiveChunk(decodeContentChunkFrame(message)),
90498
90584
  settle: async () => {
90499
- await Promise.all(commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()]));
90585
+ await Promise.all([
90586
+ contentWrites.settle(),
90587
+ ...commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()])
90588
+ ]);
90500
90589
  }
90501
90590
  };
90502
90591
  }
@@ -90558,6 +90647,7 @@ function connectRelay(url2, signal) {
90558
90647
  dispatcher,
90559
90648
  headers
90560
90649
  });
90650
+ socket.binaryType = "arraybuffer";
90561
90651
  let released = false;
90562
90652
  const release = (reason) => {
90563
90653
  if (released) {
@@ -90921,7 +91011,16 @@ async function runRelayConnection({
90921
91011
  const handleMessage = (event) => {
90922
91012
  const encoded = event.data;
90923
91013
  if (typeof encoded !== "string") {
90924
- failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
91014
+ try {
91015
+ commands.dispatchChunk(encoded);
91016
+ } catch (error62) {
91017
+ failAndClose(
91018
+ new Error("Relay sent an invalid content chunk", { cause: error62 }),
91019
+ 4008,
91020
+ "Invalid content chunk",
91021
+ "protocol"
91022
+ );
91023
+ }
90925
91024
  return;
90926
91025
  }
90927
91026
  try {