@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/cli.js CHANGED
@@ -780,8 +780,8 @@ var require_gt = __commonJS({
780
780
  "../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/gt.js"(exports, module) {
781
781
  "use strict";
782
782
  var compare = require_compare();
783
- var gt4 = (a, b2, loose) => compare(a, b2, loose) > 0;
784
- module.exports = gt4;
783
+ var gt5 = (a, b2, loose) => compare(a, b2, loose) > 0;
784
+ module.exports = gt5;
785
785
  }
786
786
  });
787
787
 
@@ -841,7 +841,7 @@ var require_cmp = __commonJS({
841
841
  "use strict";
842
842
  var eq3 = require_eq();
843
843
  var neq = require_neq();
844
- var gt4 = require_gt();
844
+ var gt5 = require_gt();
845
845
  var gte3 = require_gte();
846
846
  var lt3 = require_lt();
847
847
  var lte3 = require_lte();
@@ -870,7 +870,7 @@ var require_cmp = __commonJS({
870
870
  case "!=":
871
871
  return neq(a, b2, loose);
872
872
  case ">":
873
- return gt4(a, b2, loose);
873
+ return gt5(a, b2, loose);
874
874
  case ">=":
875
875
  return gte3(a, b2, loose);
876
876
  case "<":
@@ -1599,7 +1599,7 @@ var require_min_version = __commonJS({
1599
1599
  "use strict";
1600
1600
  var SemVer = require_semver();
1601
1601
  var Range = require_range();
1602
- var gt4 = require_gt();
1602
+ var gt5 = require_gt();
1603
1603
  var minVersion = (range, loose) => {
1604
1604
  range = new Range(range, loose);
1605
1605
  let minver = new SemVer("0.0.0");
@@ -1627,7 +1627,7 @@ var require_min_version = __commonJS({
1627
1627
  /* fallthrough */
1628
1628
  case "":
1629
1629
  case ">=":
1630
- if (!setMin || gt4(compver, setMin)) {
1630
+ if (!setMin || gt5(compver, setMin)) {
1631
1631
  setMin = compver;
1632
1632
  }
1633
1633
  break;
@@ -1639,7 +1639,7 @@ var require_min_version = __commonJS({
1639
1639
  throw new Error(`Unexpected operation: ${comparator.operator}`);
1640
1640
  }
1641
1641
  });
1642
- if (setMin && (!minver || gt4(minver, setMin))) {
1642
+ if (setMin && (!minver || gt5(minver, setMin))) {
1643
1643
  minver = setMin;
1644
1644
  }
1645
1645
  }
@@ -1677,7 +1677,7 @@ var require_outside = __commonJS({
1677
1677
  var { ANY } = Comparator;
1678
1678
  var Range = require_range();
1679
1679
  var satisfies3 = require_satisfies();
1680
- var gt4 = require_gt();
1680
+ var gt5 = require_gt();
1681
1681
  var lt3 = require_lt();
1682
1682
  var lte3 = require_lte();
1683
1683
  var gte3 = require_gte();
@@ -1687,7 +1687,7 @@ var require_outside = __commonJS({
1687
1687
  let gtfn, ltefn, ltfn, comp, ecomp;
1688
1688
  switch (hilo) {
1689
1689
  case ">":
1690
- gtfn = gt4;
1690
+ gtfn = gt5;
1691
1691
  ltefn = lte3;
1692
1692
  ltfn = lt3;
1693
1693
  comp = ">";
@@ -1696,7 +1696,7 @@ var require_outside = __commonJS({
1696
1696
  case "<":
1697
1697
  gtfn = lt3;
1698
1698
  ltefn = gte3;
1699
- ltfn = gt4;
1699
+ ltfn = gt5;
1700
1700
  comp = "<";
1701
1701
  ecomp = "<=";
1702
1702
  break;
@@ -1874,10 +1874,10 @@ var require_subset = __commonJS({
1874
1874
  }
1875
1875
  }
1876
1876
  const eqSet = /* @__PURE__ */ new Set();
1877
- let gt4, lt3;
1877
+ let gt5, lt3;
1878
1878
  for (const c of sub) {
1879
1879
  if (c.operator === ">" || c.operator === ">=") {
1880
- gt4 = higherGT(gt4, c, options);
1880
+ gt5 = higherGT(gt5, c, options);
1881
1881
  } else if (c.operator === "<" || c.operator === "<=") {
1882
1882
  lt3 = lowerLT(lt3, c, options);
1883
1883
  } else {
@@ -1888,16 +1888,16 @@ var require_subset = __commonJS({
1888
1888
  return null;
1889
1889
  }
1890
1890
  let gtltComp;
1891
- if (gt4 && lt3) {
1892
- gtltComp = compare(gt4.semver, lt3.semver, options);
1891
+ if (gt5 && lt3) {
1892
+ gtltComp = compare(gt5.semver, lt3.semver, options);
1893
1893
  if (gtltComp > 0) {
1894
1894
  return null;
1895
- } else if (gtltComp === 0 && (gt4.operator !== ">=" || lt3.operator !== "<=")) {
1895
+ } else if (gtltComp === 0 && (gt5.operator !== ">=" || lt3.operator !== "<=")) {
1896
1896
  return null;
1897
1897
  }
1898
1898
  }
1899
1899
  for (const eq3 of eqSet) {
1900
- if (gt4 && !satisfies3(eq3, String(gt4), options)) {
1900
+ if (gt5 && !satisfies3(eq3, String(gt5), options)) {
1901
1901
  return null;
1902
1902
  }
1903
1903
  if (lt3 && !satisfies3(eq3, String(lt3), options)) {
@@ -1913,25 +1913,25 @@ var require_subset = __commonJS({
1913
1913
  let higher, lower;
1914
1914
  let hasDomLT, hasDomGT;
1915
1915
  let needDomLTPre = lt3 && !options.includePrerelease && lt3.semver.prerelease.length ? lt3.semver : false;
1916
- let needDomGTPre = gt4 && !options.includePrerelease && gt4.semver.prerelease.length ? gt4.semver : false;
1916
+ let needDomGTPre = gt5 && !options.includePrerelease && gt5.semver.prerelease.length ? gt5.semver : false;
1917
1917
  if (needDomLTPre && needDomLTPre.prerelease.length === 1 && lt3.operator === "<" && needDomLTPre.prerelease[0] === 0) {
1918
1918
  needDomLTPre = false;
1919
1919
  }
1920
1920
  for (const c of dom) {
1921
1921
  hasDomGT = hasDomGT || c.operator === ">" || c.operator === ">=";
1922
1922
  hasDomLT = hasDomLT || c.operator === "<" || c.operator === "<=";
1923
- if (gt4) {
1923
+ if (gt5) {
1924
1924
  if (needDomGTPre) {
1925
1925
  if (c.semver.prerelease && c.semver.prerelease.length && c.semver.major === needDomGTPre.major && c.semver.minor === needDomGTPre.minor && c.semver.patch === needDomGTPre.patch) {
1926
1926
  needDomGTPre = false;
1927
1927
  }
1928
1928
  }
1929
1929
  if (c.operator === ">" || c.operator === ">=") {
1930
- higher = higherGT(gt4, c, options);
1931
- if (higher === c && higher !== gt4) {
1930
+ higher = higherGT(gt5, c, options);
1931
+ if (higher === c && higher !== gt5) {
1932
1932
  return false;
1933
1933
  }
1934
- } else if (gt4.operator === ">=" && !c.test(gt4.semver)) {
1934
+ } else if (gt5.operator === ">=" && !c.test(gt5.semver)) {
1935
1935
  return false;
1936
1936
  }
1937
1937
  }
@@ -1950,14 +1950,14 @@ var require_subset = __commonJS({
1950
1950
  return false;
1951
1951
  }
1952
1952
  }
1953
- if (!c.operator && (lt3 || gt4) && gtltComp !== 0) {
1953
+ if (!c.operator && (lt3 || gt5) && gtltComp !== 0) {
1954
1954
  return false;
1955
1955
  }
1956
1956
  }
1957
- if (gt4 && hasDomLT && !lt3 && gtltComp !== 0) {
1957
+ if (gt5 && hasDomLT && !lt3 && gtltComp !== 0) {
1958
1958
  return false;
1959
1959
  }
1960
- if (lt3 && hasDomGT && !gt4 && gtltComp !== 0) {
1960
+ if (lt3 && hasDomGT && !gt5 && gtltComp !== 0) {
1961
1961
  return false;
1962
1962
  }
1963
1963
  if (needDomGTPre || needDomLTPre) {
@@ -2006,7 +2006,7 @@ var require_semver2 = __commonJS({
2006
2006
  var compareBuild = require_compare_build();
2007
2007
  var sort = require_sort();
2008
2008
  var rsort = require_rsort();
2009
- var gt4 = require_gt();
2009
+ var gt5 = require_gt();
2010
2010
  var lt3 = require_lt();
2011
2011
  var eq3 = require_eq();
2012
2012
  var neq = require_neq();
@@ -2045,7 +2045,7 @@ var require_semver2 = __commonJS({
2045
2045
  compareBuild,
2046
2046
  sort,
2047
2047
  rsort,
2048
- gt: gt4,
2048
+ gt: gt5,
2049
2049
  lt: lt3,
2050
2050
  eq: eq3,
2051
2051
  neq,
@@ -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.325",
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,
@@ -91243,243 +91350,9 @@ var init_version_data = __esm({
91243
91350
  }
91244
91351
  });
91245
91352
 
91246
- // src/storage/daemon/control-token.ts
91247
- import { randomBytes } from "node:crypto";
91248
- import { readFile as readFile8, writeFile as writeFile3 } from "node:fs/promises";
91249
- import { join as join26 } from "node:path";
91250
- function controlTokenPath(stateDirectory) {
91251
- return join26(stateDirectory, "manager.token");
91252
- }
91253
- async function readOrCreateControlToken(stateDirectory) {
91254
- const path8 = controlTokenPath(stateDirectory);
91255
- try {
91256
- return (await readFile8(path8, "utf8")).trim();
91257
- } catch (cause) {
91258
- if (!isNodeError(cause, "ENOENT")) {
91259
- throw cause;
91260
- }
91261
- }
91262
- const token = randomBytes(32).toString("base64url");
91263
- try {
91264
- await writeFile3(path8, `${token}
91265
- `, { flag: "wx", mode: 384 });
91266
- return token;
91267
- } catch (cause) {
91268
- if (!isNodeError(cause, "EEXIST")) {
91269
- throw cause;
91270
- }
91271
- return (await readFile8(path8, "utf8")).trim();
91272
- }
91273
- }
91274
- var init_control_token = __esm({
91275
- "src/storage/daemon/control-token.ts"() {
91276
- "use strict";
91277
- init_node_error();
91278
- }
91279
- });
91280
-
91281
- // src/daemon/control/local-client.ts
91282
- import { createHash as createHash6 } from "node:crypto";
91283
- import { createConnection } from "node:net";
91284
- function controlSocketPath(stateDirectory, endpoint = "manager") {
91285
- const installationHash = createHash6("sha256").update(endpoint === "manager" ? stateDirectory : `${stateDirectory}:worker`).digest("hex").slice(0, 32);
91286
- return managerPlatform.controlSocketPath(installationHash);
91287
- }
91288
- async function readSocketMessage(socket, signal) {
91289
- signal.throwIfAborted();
91290
- return await new Promise((resolve21, reject2) => {
91291
- let encoded = "";
91292
- const finish = (error62, value) => {
91293
- signal.removeEventListener("abort", abort);
91294
- socket.removeAllListeners("data");
91295
- socket.removeAllListeners("error");
91296
- socket.removeAllListeners("end");
91297
- if (error62 === void 0) {
91298
- resolve21(value);
91299
- } else {
91300
- reject2(error62);
91301
- }
91302
- };
91303
- const abort = () => {
91304
- socket.destroy();
91305
- finish(signal.reason);
91306
- };
91307
- socket.on("data", (chunk) => {
91308
- encoded += chunk.toString("utf8");
91309
- if (encoded.length > MAXIMUM_MESSAGE_BYTES) {
91310
- finish(new Error("The manager message is too large"));
91311
- socket.destroy();
91312
- return;
91313
- }
91314
- const newline = encoded.indexOf("\n");
91315
- if (newline >= 0) {
91316
- try {
91317
- finish(void 0, JSON.parse(encoded.slice(0, newline)));
91318
- } catch (cause) {
91319
- finish(cause);
91320
- }
91321
- }
91322
- });
91323
- socket.once("error", finish);
91324
- socket.once("end", () => finish(new Error("The manager connection closed without a response")));
91325
- signal.addEventListener("abort", abort, { once: true });
91326
- });
91327
- }
91328
- async function sendLocalRequest(stateDirectory, endpoint, command, signal) {
91329
- const token = await readOrCreateControlToken(stateDirectory);
91330
- const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
91331
- deadline.throwIfAborted();
91332
- const socket = createConnection(controlSocketPath(stateDirectory, endpoint));
91333
- try {
91334
- await new Promise((resolve21, reject2) => {
91335
- const abort = () => {
91336
- socket.destroy();
91337
- reject2(deadline.reason);
91338
- };
91339
- socket.once("connect", () => {
91340
- deadline.removeEventListener("abort", abort);
91341
- resolve21();
91342
- });
91343
- socket.once("error", reject2);
91344
- deadline.addEventListener("abort", abort, { once: true });
91345
- });
91346
- socket.write(`${JSON.stringify({ ...command, token })}
91347
- `);
91348
- return await readSocketMessage(socket, deadline);
91349
- } finally {
91350
- socket.destroy();
91351
- }
91352
- }
91353
- var CONTROL_TIMEOUT_MILLISECONDS, MAXIMUM_MESSAGE_BYTES, managerPlatform;
91354
- var init_local_client = __esm({
91355
- "src/daemon/control/local-client.ts"() {
91356
- "use strict";
91357
- init_control_token();
91358
- init_platform();
91359
- CONTROL_TIMEOUT_MILLISECONDS = 13e4;
91360
- MAXIMUM_MESSAGE_BYTES = 64 * 1024;
91361
- managerPlatform = platformSupportFor(process.platform);
91362
- }
91363
- });
91364
-
91365
- // src/daemon/control/protocol.ts
91366
- var workerLaunch, transition, daemonSwitchState, lifecycleCommand, lifecycleResponse;
91367
- var init_protocol2 = __esm({
91368
- "src/daemon/control/protocol.ts"() {
91369
- "use strict";
91370
- init_zod();
91371
- workerLaunch = external_exports.object({
91372
- executable: external_exports.string().min(1),
91373
- entry: external_exports.string().min(1),
91374
- version: external_exports.string().min(1).max(128)
91375
- }).readonly();
91376
- transition = {
91377
- id: external_exports.string().min(1).max(256),
91378
- context: external_exports.string().max(256),
91379
- previous: workerLaunch,
91380
- next: workerLaunch
91381
- };
91382
- daemonSwitchState = external_exports.discriminatedUnion("kind", [
91383
- external_exports.object({ ...transition, kind: external_exports.literal("switching") }),
91384
- external_exports.object({ ...transition, kind: external_exports.literal("switched") }),
91385
- external_exports.object({
91386
- ...transition,
91387
- kind: external_exports.literal("rolled-back"),
91388
- code: external_exports.string(),
91389
- message: external_exports.string(),
91390
- stdout: external_exports.string().max(4096),
91391
- stderr: external_exports.string().max(4096),
91392
- retryAfter: external_exports.number().int().nonnegative()
91393
- })
91394
- ]).readonly();
91395
- lifecycleCommand = external_exports.discriminatedUnion("kind", [
91396
- external_exports.object({ kind: external_exports.literal("status") }),
91397
- external_exports.object({ kind: external_exports.literal("ensure"), launch: workerLaunch }),
91398
- external_exports.object({ kind: external_exports.literal("stop") }),
91399
- external_exports.object({
91400
- kind: external_exports.literal("switch"),
91401
- id: external_exports.string().min(1).max(256),
91402
- context: external_exports.string().max(256),
91403
- launch: workerLaunch
91404
- }),
91405
- external_exports.object({ kind: external_exports.literal("switch-status"), wait: external_exports.boolean() })
91406
- ]).readonly();
91407
- lifecycleResponse = external_exports.discriminatedUnion("kind", [
91408
- external_exports.object({
91409
- kind: external_exports.literal("status"),
91410
- protocolVersion: external_exports.literal(2),
91411
- worker: external_exports.object({ pid: external_exports.number().int().positive(), version: external_exports.string() }).readonly().nullable()
91412
- }),
91413
- external_exports.object({ kind: external_exports.literal("switch-status"), state: daemonSwitchState.nullable() }),
91414
- external_exports.object({ kind: external_exports.literal("stopped") }),
91415
- external_exports.object({ kind: external_exports.literal("switching") }),
91416
- external_exports.object({ kind: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() })
91417
- ]).readonly();
91418
- }
91419
- });
91420
-
91421
- // src/daemon/control/switch-status.ts
91422
- async function readSwitchStatus(directory, signal, wait = false) {
91423
- const response = lifecycleResponse.parse(
91424
- await sendLocalRequest(directory, "manager", { kind: "switch-status", wait }, signal)
91425
- );
91426
- if (response.kind !== "switch-status") {
91427
- throw new Error("The manager could not return the switch status");
91428
- }
91429
- return response.state;
91430
- }
91431
- var init_switch_status = __esm({
91432
- "src/daemon/control/switch-status.ts"() {
91433
- "use strict";
91434
- init_local_client();
91435
- init_protocol2();
91436
- }
91437
- });
91438
-
91439
- // src/daemon/upgrades/data.ts
91440
- async function prepareVersionData(version2, signal, environment = process.env, fallbackVersion = null) {
91441
- signal.throwIfAborted();
91442
- const root = readStateDirectory(environment);
91443
- const targetVersion = runtimeUpgradeVersionSchema.parse(version2);
91444
- if (await versionDataExists(root, targetVersion, signal)) {
91445
- return;
91446
- }
91447
- const previous = await readCurrentDataVersion(root, signal) ?? fallbackVersion;
91448
- if (previous === null) {
91449
- return;
91450
- }
91451
- const leftBehind = await copyVersionData(root, runtimeUpgradeVersionSchema.parse(previous), targetVersion, signal);
91452
- for (const entry of leftBehind) {
91453
- process.stderr.write(
91454
- `Mobius did not carry ${entry} into version ${targetVersion}: an earlier Mobius ran with that installation as its state root.
91455
- `
91456
- );
91457
- }
91458
- }
91459
- async function discardRolledBackData(version2, signal) {
91460
- await discardVersionData(readStateDirectory(), runtimeUpgradeVersionSchema.parse(version2), signal);
91461
- }
91462
- async function recordCurrentData(version2, signal) {
91463
- await writeCurrentDataVersion(readStateDirectory(), runtimeUpgradeVersionSchema.parse(version2), signal);
91464
- }
91465
- async function prepareUpgradeData(controlDirectory, version2, signal) {
91466
- const transition2 = await readSwitchStatus(controlDirectory, signal);
91467
- const previous = transition2?.kind === "switching" && transition2.next.version === version2 ? transition2.previous.version : null;
91468
- await prepareVersionData(version2, signal, process.env, previous);
91469
- }
91470
- var init_data = __esm({
91471
- "src/daemon/upgrades/data.ts"() {
91472
- "use strict";
91473
- init_protocol();
91474
- init_version_data();
91475
- init_state_directory();
91476
- init_switch_status();
91477
- }
91478
- });
91479
-
91480
91353
  // src/daemon/pairing/pair-local-machine.ts
91481
91354
  import { hostname as hostname3 } from "node:os";
91482
- import { basename as basename7, join as join27, resolve as resolve16 } from "node:path";
91355
+ import { basename as basename7, join as join26, resolve as resolve16 } from "node:path";
91483
91356
  import { fetch as fetch3 } from "undici";
91484
91357
  function hasExactKeys2(value, expectedKeys) {
91485
91358
  const keys = Object.keys(value);
@@ -91732,11 +91605,15 @@ async function openMobiusControlInstallation(signal, environment) {
91732
91605
  );
91733
91606
  }
91734
91607
  async function openMobiusCliInstallation(signal, environment) {
91735
- await prepareVersionData(package_default.version, signal, environment);
91608
+ const root = readStateDirectory(environment);
91609
+ const version2 = await readCurrentDataVersion(root, signal) ?? package_default.version;
91610
+ return await openMobiusVersionInstallation(version2, signal, environment);
91611
+ }
91612
+ async function openMobiusVersionInstallation(version2, signal, environment) {
91736
91613
  return await openRuntimeInstallationStore(
91737
91614
  {
91738
91615
  applicationId: MOBIUS_CLI_APPLICATION_ID,
91739
- stateDirectory: join27(readStateDirectory(environment), "versions", package_default.version)
91616
+ stateDirectory: join26(readStateDirectory(environment), "versions", version2)
91740
91617
  },
91741
91618
  signal
91742
91619
  );
@@ -91775,11 +91652,11 @@ var init_pair_local_machine = __esm({
91775
91652
  init_resolve_catalog_agent();
91776
91653
  init_config();
91777
91654
  init_runtime_installation_store();
91655
+ init_version_data();
91778
91656
  init_local_files();
91779
91657
  init_state_directory();
91780
91658
  init_environment_proxy();
91781
91659
  init_value_utils();
91782
- init_data();
91783
91660
  PAIRING_TIMEOUT_MILLISECONDS = 2e4;
91784
91661
  PAIRING_CODE_PATTERN = /^[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){4}$/;
91785
91662
  TOKEN_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
@@ -92008,20 +91885,20 @@ var init_agents = __esm({
92008
91885
  });
92009
91886
 
92010
91887
  // src/storage/daemon/manager-lock.ts
92011
- import { readFile as readFile9, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
92012
- import { join as join28 } from "node:path";
91888
+ import { readFile as readFile8, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
91889
+ import { join as join27 } from "node:path";
92013
91890
  async function acquireManagerLock(stateDirectory, name2 = "manager.lock") {
92014
- const path8 = join28(stateDirectory, name2);
91891
+ const path8 = join27(stateDirectory, name2);
92015
91892
  for (let attempt = 0; attempt < 2; attempt += 1) {
92016
91893
  try {
92017
- await writeFile4(path8, `${process.pid.toString()}
91894
+ await writeFile3(path8, `${process.pid.toString()}
92018
91895
  `, { flag: "wx", mode: 384 });
92019
91896
  return async () => await unlink4(path8).catch(() => void 0);
92020
91897
  } catch (cause) {
92021
91898
  if (!isNodeError(cause, "EEXIST")) {
92022
91899
  throw cause;
92023
91900
  }
92024
- const encoded = await readFile9(path8, "utf8").catch(() => "");
91901
+ const encoded = await readFile8(path8, "utf8").catch(() => "");
92025
91902
  const pid = Number(encoded.trim());
92026
91903
  if (Number.isSafeInteger(pid) && pid > 1) {
92027
91904
  try {
@@ -92040,7 +91917,7 @@ async function acquireManagerLock(stateDirectory, name2 = "manager.lock") {
92040
91917
  }
92041
91918
  async function managerLockExists(stateDirectory) {
92042
91919
  try {
92043
- await readFile9(join28(stateDirectory, "manager.lock"), "utf8");
91920
+ await readFile8(join27(stateDirectory, "manager.lock"), "utf8");
92044
91921
  return true;
92045
91922
  } catch (cause) {
92046
91923
  if (isNodeError(cause, "ENOENT")) {
@@ -92056,6 +91933,125 @@ var init_manager_lock = __esm({
92056
91933
  }
92057
91934
  });
92058
91935
 
91936
+ // src/storage/daemon/control-token.ts
91937
+ import { randomBytes } from "node:crypto";
91938
+ import { readFile as readFile9, writeFile as writeFile4 } from "node:fs/promises";
91939
+ import { join as join28 } from "node:path";
91940
+ function controlTokenPath(stateDirectory) {
91941
+ return join28(stateDirectory, "manager.token");
91942
+ }
91943
+ async function readOrCreateControlToken(stateDirectory) {
91944
+ const path8 = controlTokenPath(stateDirectory);
91945
+ try {
91946
+ return (await readFile9(path8, "utf8")).trim();
91947
+ } catch (cause) {
91948
+ if (!isNodeError(cause, "ENOENT")) {
91949
+ throw cause;
91950
+ }
91951
+ }
91952
+ const token = randomBytes(32).toString("base64url");
91953
+ try {
91954
+ await writeFile4(path8, `${token}
91955
+ `, { flag: "wx", mode: 384 });
91956
+ return token;
91957
+ } catch (cause) {
91958
+ if (!isNodeError(cause, "EEXIST")) {
91959
+ throw cause;
91960
+ }
91961
+ return (await readFile9(path8, "utf8")).trim();
91962
+ }
91963
+ }
91964
+ var init_control_token = __esm({
91965
+ "src/storage/daemon/control-token.ts"() {
91966
+ "use strict";
91967
+ init_node_error();
91968
+ }
91969
+ });
91970
+
91971
+ // src/daemon/control/local-client.ts
91972
+ import { createHash as createHash6 } from "node:crypto";
91973
+ import { createConnection } from "node:net";
91974
+ function controlSocketPath(stateDirectory, endpoint = "manager") {
91975
+ const installationHash = createHash6("sha256").update(endpoint === "manager" ? stateDirectory : `${stateDirectory}:worker`).digest("hex").slice(0, 32);
91976
+ return managerPlatform.controlSocketPath(installationHash);
91977
+ }
91978
+ async function readSocketMessage(socket, signal) {
91979
+ signal.throwIfAborted();
91980
+ return await new Promise((resolve21, reject2) => {
91981
+ let encoded = "";
91982
+ const finish = (error62, value) => {
91983
+ signal.removeEventListener("abort", abort);
91984
+ socket.removeAllListeners("data");
91985
+ socket.removeAllListeners("error");
91986
+ socket.removeAllListeners("end");
91987
+ if (error62 === void 0) {
91988
+ resolve21(value);
91989
+ } else {
91990
+ reject2(error62);
91991
+ }
91992
+ };
91993
+ const abort = () => {
91994
+ socket.destroy();
91995
+ finish(signal.reason);
91996
+ };
91997
+ socket.on("data", (chunk) => {
91998
+ encoded += chunk.toString("utf8");
91999
+ if (encoded.length > MAXIMUM_MESSAGE_BYTES) {
92000
+ finish(new Error("The manager message is too large"));
92001
+ socket.destroy();
92002
+ return;
92003
+ }
92004
+ const newline = encoded.indexOf("\n");
92005
+ if (newline >= 0) {
92006
+ try {
92007
+ finish(void 0, JSON.parse(encoded.slice(0, newline)));
92008
+ } catch (cause) {
92009
+ finish(cause);
92010
+ }
92011
+ }
92012
+ });
92013
+ socket.once("error", finish);
92014
+ socket.once("end", () => finish(new Error("The manager connection closed without a response")));
92015
+ signal.addEventListener("abort", abort, { once: true });
92016
+ });
92017
+ }
92018
+ async function sendLocalRequest(stateDirectory, endpoint, command, signal) {
92019
+ const token = await readOrCreateControlToken(stateDirectory);
92020
+ const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
92021
+ deadline.throwIfAborted();
92022
+ const socket = createConnection(controlSocketPath(stateDirectory, endpoint));
92023
+ try {
92024
+ await new Promise((resolve21, reject2) => {
92025
+ const abort = () => {
92026
+ socket.destroy();
92027
+ reject2(deadline.reason);
92028
+ };
92029
+ socket.once("connect", () => {
92030
+ deadline.removeEventListener("abort", abort);
92031
+ resolve21();
92032
+ });
92033
+ socket.once("error", reject2);
92034
+ deadline.addEventListener("abort", abort, { once: true });
92035
+ });
92036
+ socket.write(`${JSON.stringify({ ...command, token })}
92037
+ `);
92038
+ return await readSocketMessage(socket, deadline);
92039
+ } finally {
92040
+ socket.destroy();
92041
+ }
92042
+ }
92043
+ var CONTROL_TIMEOUT_MILLISECONDS, MAXIMUM_MESSAGE_BYTES, managerPlatform;
92044
+ var init_local_client = __esm({
92045
+ "src/daemon/control/local-client.ts"() {
92046
+ "use strict";
92047
+ init_control_token();
92048
+ init_platform();
92049
+ CONTROL_TIMEOUT_MILLISECONDS = 13e4;
92050
+ MAXIMUM_MESSAGE_BYTES = 64 * 1024;
92051
+ managerPlatform = platformSupportFor(process.platform);
92052
+ }
92053
+ });
92054
+
92059
92055
  // src/daemon/control/local-server.ts
92060
92056
  import { createServer } from "node:net";
92061
92057
  async function serveLocalControl(stateDirectory, endpoint, signal, handle, onListening = () => void 0, onResponse = () => void 0) {
@@ -92126,6 +92122,121 @@ var init_local_server = __esm({
92126
92122
  }
92127
92123
  });
92128
92124
 
92125
+ // src/daemon/control/protocol.ts
92126
+ var workerLaunch, transition, daemonSwitchState, lifecycleCommand, lifecycleResponse;
92127
+ var init_protocol2 = __esm({
92128
+ "src/daemon/control/protocol.ts"() {
92129
+ "use strict";
92130
+ init_zod();
92131
+ workerLaunch = external_exports.object({
92132
+ executable: external_exports.string().min(1),
92133
+ entry: external_exports.string().min(1),
92134
+ version: external_exports.string().min(1).max(128)
92135
+ }).readonly();
92136
+ transition = {
92137
+ id: external_exports.string().min(1).max(256),
92138
+ context: external_exports.string().max(256),
92139
+ previous: workerLaunch,
92140
+ next: workerLaunch
92141
+ };
92142
+ daemonSwitchState = external_exports.discriminatedUnion("kind", [
92143
+ external_exports.object({ ...transition, kind: external_exports.literal("switching") }),
92144
+ external_exports.object({ ...transition, kind: external_exports.literal("switched") }),
92145
+ external_exports.object({
92146
+ ...transition,
92147
+ kind: external_exports.literal("rolled-back"),
92148
+ code: external_exports.string(),
92149
+ message: external_exports.string(),
92150
+ stdout: external_exports.string().max(4096),
92151
+ stderr: external_exports.string().max(4096),
92152
+ retryAfter: external_exports.number().int().nonnegative()
92153
+ })
92154
+ ]).readonly();
92155
+ lifecycleCommand = external_exports.discriminatedUnion("kind", [
92156
+ external_exports.object({ kind: external_exports.literal("status") }),
92157
+ external_exports.object({ kind: external_exports.literal("ensure"), launch: workerLaunch }),
92158
+ external_exports.object({ kind: external_exports.literal("stop") }),
92159
+ external_exports.object({
92160
+ kind: external_exports.literal("switch"),
92161
+ id: external_exports.string().min(1).max(256),
92162
+ context: external_exports.string().max(256),
92163
+ launch: workerLaunch
92164
+ }),
92165
+ external_exports.object({ kind: external_exports.literal("switch-status"), wait: external_exports.boolean() })
92166
+ ]).readonly();
92167
+ lifecycleResponse = external_exports.discriminatedUnion("kind", [
92168
+ external_exports.object({
92169
+ kind: external_exports.literal("status"),
92170
+ protocolVersion: external_exports.literal(2),
92171
+ worker: external_exports.object({ pid: external_exports.number().int().positive(), version: external_exports.string() }).readonly().nullable()
92172
+ }),
92173
+ external_exports.object({ kind: external_exports.literal("switch-status"), state: daemonSwitchState.nullable() }),
92174
+ external_exports.object({ kind: external_exports.literal("stopped") }),
92175
+ external_exports.object({ kind: external_exports.literal("switching") }),
92176
+ external_exports.object({ kind: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() })
92177
+ ]).readonly();
92178
+ }
92179
+ });
92180
+
92181
+ // src/daemon/control/switch-status.ts
92182
+ async function readSwitchStatus(directory, signal, wait = false) {
92183
+ const response = lifecycleResponse.parse(
92184
+ await sendLocalRequest(directory, "manager", { kind: "switch-status", wait }, signal)
92185
+ );
92186
+ if (response.kind !== "switch-status") {
92187
+ throw new Error("The manager could not return the switch status");
92188
+ }
92189
+ return response.state;
92190
+ }
92191
+ var init_switch_status = __esm({
92192
+ "src/daemon/control/switch-status.ts"() {
92193
+ "use strict";
92194
+ init_local_client();
92195
+ init_protocol2();
92196
+ }
92197
+ });
92198
+
92199
+ // src/daemon/upgrades/data.ts
92200
+ async function prepareVersionData(version2, signal, environment = process.env, fallbackVersion = null) {
92201
+ signal.throwIfAborted();
92202
+ const root = readStateDirectory(environment);
92203
+ const targetVersion = runtimeUpgradeVersionSchema.parse(version2);
92204
+ if (await versionDataExists(root, targetVersion, signal)) {
92205
+ return;
92206
+ }
92207
+ const previous = await readCurrentDataVersion(root, signal) ?? fallbackVersion;
92208
+ if (previous === null) {
92209
+ return;
92210
+ }
92211
+ const leftBehind = await copyVersionData(root, runtimeUpgradeVersionSchema.parse(previous), targetVersion, signal);
92212
+ for (const entry of leftBehind) {
92213
+ process.stderr.write(
92214
+ `Mobius did not carry ${entry} into version ${targetVersion}: an earlier Mobius ran with that installation as its state root.
92215
+ `
92216
+ );
92217
+ }
92218
+ }
92219
+ async function discardRolledBackData(version2, signal) {
92220
+ await discardVersionData(readStateDirectory(), runtimeUpgradeVersionSchema.parse(version2), signal);
92221
+ }
92222
+ async function recordCurrentData(version2, signal) {
92223
+ await writeCurrentDataVersion(readStateDirectory(), runtimeUpgradeVersionSchema.parse(version2), signal);
92224
+ }
92225
+ async function prepareUpgradeData(controlDirectory, version2, signal) {
92226
+ const transition2 = await readSwitchStatus(controlDirectory, signal);
92227
+ const previous = transition2?.kind === "switching" && transition2.next.version === version2 ? transition2.previous.version : null;
92228
+ await prepareVersionData(version2, signal, process.env, previous);
92229
+ }
92230
+ var init_data = __esm({
92231
+ "src/daemon/upgrades/data.ts"() {
92232
+ "use strict";
92233
+ init_protocol();
92234
+ init_version_data();
92235
+ init_state_directory();
92236
+ init_switch_status();
92237
+ }
92238
+ });
92239
+
92129
92240
  // src/storage/logs/bounded-log.ts
92130
92241
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
92131
92242
  import { join as join29 } from "node:path";
@@ -93059,7 +93170,9 @@ async function waitForManagerExit(stateDirectory, signal) {
93059
93170
  }
93060
93171
  throw deadline.reason;
93061
93172
  }
93062
- async function ensureWorkingProcess(stateDirectory, version2, signal) {
93173
+ async function ensureWorkingProcess(stateDirectory, cliVersion, signal) {
93174
+ const current = await readCurrentDataVersion(readStateDirectory(), signal);
93175
+ const version2 = current !== null && (0, import_semver2.gt)(current, cliVersion) ? current : cliVersion;
93063
93176
  const launch = await prepareDaemonReleaseVersion(version2, signal);
93064
93177
  const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
93065
93178
  await ensureManager(stateDirectory, launch, deadline);
@@ -93072,6 +93185,7 @@ async function ensureWorkingProcess(stateDirectory, version2, signal) {
93072
93185
  if (response.kind !== "status" || response.worker?.version !== version2) {
93073
93186
  throw new Error("The working daemon did not start");
93074
93187
  }
93188
+ return version2;
93075
93189
  }
93076
93190
  async function readWorkingProcessVersion(stateDirectory, signal) {
93077
93191
  try {
@@ -93147,10 +93261,14 @@ async function stopManager(stateDirectory, signal) {
93147
93261
  throw cause;
93148
93262
  }
93149
93263
  }
93264
+ var import_semver2;
93150
93265
  var init_manager_client = __esm({
93151
93266
  "src/daemon/lifecycle/manager-client.ts"() {
93152
93267
  "use strict";
93268
+ import_semver2 = __toESM(require_semver2(), 1);
93153
93269
  init_manager_lock();
93270
+ init_version_data();
93271
+ init_state_directory();
93154
93272
  init_node_error();
93155
93273
  init_local_client();
93156
93274
  init_protocol2();
@@ -93501,7 +93619,8 @@ var init_session_api = __esm({
93501
93619
  configOptions;
93502
93620
  prompted;
93503
93621
  restored;
93504
- constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
93622
+ configurationRevision;
93623
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored, configurationRevision) {
93505
93624
  this.agentName = agentName;
93506
93625
  this.agentVersion = agentVersion;
93507
93626
  this.capabilities = capabilities;
@@ -93509,6 +93628,7 @@ var init_session_api = __esm({
93509
93628
  this.configOptions = configOptions;
93510
93629
  this.prompted = prompted;
93511
93630
  this.restored = restored;
93631
+ this.configurationRevision = configurationRevision;
93512
93632
  }
93513
93633
  };
93514
93634
  AgentSettingsSnapshot = class {
@@ -94573,6 +94693,7 @@ var init_agent_session = __esm({
94573
94693
  init_prompt_content();
94574
94694
  init_session_capabilities();
94575
94695
  init_session_config_protocol();
94696
+ init_serial_task_queue();
94576
94697
  init_value_utils();
94577
94698
  init_session_api();
94578
94699
  init_session_history();
@@ -94632,7 +94753,8 @@ var init_agent_session = __esm({
94632
94753
  #agentVersion = "";
94633
94754
  #capabilities = unsupportedSessionCapabilities();
94634
94755
  #commands = [];
94635
- #configRevision = 0;
94756
+ #configRevision = crypto.randomUUID();
94757
+ #configurationChanges = new SerialTaskQueue();
94636
94758
  #configOptions = [];
94637
94759
  #sessionConfigurations = new SessionConfigurations();
94638
94760
  #nativeHistoryOwnership = { kind: "unbound" };
@@ -94801,7 +94923,8 @@ var init_agent_session = __esm({
94801
94923
  this.#commands,
94802
94924
  this.#configOptions,
94803
94925
  this.#prompted,
94804
- this.#restored
94926
+ this.#restored,
94927
+ this.#configRevision
94805
94928
  );
94806
94929
  ensureHistoryLoaded = async (signal) => {
94807
94930
  if (!this.#capabilities.loadSession) {
@@ -94931,12 +95054,23 @@ var init_agent_session = __esm({
94931
95054
  const configured = await this.#adapter.configureSession(this.#target(), selection, signal);
94932
95055
  if (this.#configRevision === configRevision) {
94933
95056
  this.#configOptions = configured;
95057
+ this.#configRevision = crypto.randomUUID();
95058
+ await this.#publishConfiguration();
94934
95059
  }
94935
95060
  };
94936
- configure = async (options) => {
95061
+ // The revision check and Agent mutation share one queue, including legacy callers.
95062
+ // A lost response may have committed: reread after a conflict, never replay an edit blindly.
95063
+ configure = async (options) => this.#configurationChanges.run(async (signal) => {
95064
+ if (options.expectedConfigurationRevision !== void 0 && options.expectedConfigurationRevision !== this.#configRevision) {
95065
+ throw new AcpExecutionError(
95066
+ "Session settings changed. Review the current settings before applying your selection again.",
95067
+ {},
95068
+ "session_configuration_conflict"
95069
+ );
95070
+ }
94937
95071
  await this.#applyConfiguration(
94938
95072
  options.valueType === "boolean" ? { type: "boolean", configId: options.configId, booleanValue: options.booleanValue } : { type: "select", configId: options.configId, value: options.value },
94939
- options.signal
95073
+ signal
94940
95074
  );
94941
95075
  const applied = this.#configOptions.find((option) => option.id === options.configId);
94942
95076
  if (applied === void 0) {
@@ -94944,7 +95078,24 @@ var init_agent_session = __esm({
94944
95078
  }
94945
95079
  this.#sessionConfigurations.select(applied);
94946
95080
  this.writeStoredSession((/* @__PURE__ */ new Date()).toISOString());
94947
- return this.#configOptions;
95081
+ return { configOptions: this.#configOptions, configurationRevision: this.#configRevision };
95082
+ }, options.signal);
95083
+ #publishConfiguration = async () => {
95084
+ const event = this.#store.events.append(this.#mobiusSessionId, {
95085
+ type: "acp",
95086
+ payload: {
95087
+ sessionUpdate: "config_option_update",
95088
+ configOptions: this.#configOptions.map((option) => ({
95089
+ category: option.category,
95090
+ description: option.description,
95091
+ id: option.id,
95092
+ name: option.name,
95093
+ ...option.type === "boolean" ? { type: "boolean", currentValue: option.booleanValue } : { type: "select", currentValue: option.currentValue, options: option.values }
95094
+ }))
95095
+ }
95096
+ });
95097
+ await this.#getObserver().onSessionEvent?.(event);
95098
+ await this.emitConfigUpdate();
94948
95099
  };
94949
95100
  #target = () => ({
94950
95101
  nativeSessionId: this.#updates.sessionId(),
@@ -95122,6 +95273,13 @@ var init_agent_session = __esm({
95122
95273
  this.#nativeHistoryOwnership = { kind: "agent-owned" };
95123
95274
  };
95124
95275
  #processEvent = async (event) => {
95276
+ if (event.kind === "configuration" || event.kind === "mode") {
95277
+ this.#configOptions = event.kind === "configuration" ? event.configOptions : this.#configOptions.map(
95278
+ (option) => option.category === "mode" ? { ...option, currentValue: event.modeId } : option
95279
+ );
95280
+ this.#configRevision = crypto.randomUUID();
95281
+ this.#captureSelectedConfigurationValues();
95282
+ }
95125
95283
  if (this.#sessionLoad.kind === "capturing") {
95126
95284
  await this.#sessionLoad.history.record(event.payload);
95127
95285
  } else if (this.#sessionLoad.kind === "loaded" || this.#sessionLoad.kind === "execution-only") {
@@ -95142,17 +95300,7 @@ var init_agent_session = __esm({
95142
95300
  });
95143
95301
  return;
95144
95302
  case "configuration":
95145
- this.#configOptions = event.configOptions;
95146
- this.#configRevision += 1;
95147
- this.#captureSelectedConfigurationValues();
95148
- await this.emitConfigUpdate();
95149
- return;
95150
95303
  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
95304
  await this.emitConfigUpdate();
95157
95305
  return;
95158
95306
  case "info":
@@ -95215,6 +95363,7 @@ var init_agent_session = __esm({
95215
95363
  }
95216
95364
  setConfigOptions(options) {
95217
95365
  this.#configOptions = options;
95366
+ this.#configRevision = crypto.randomUUID();
95218
95367
  }
95219
95368
  setNativeHistoryOwnership(ownership) {
95220
95369
  this.#nativeHistoryOwnership = ownership;
@@ -95236,6 +95385,7 @@ var init_agent_session = __esm({
95236
95385
  emitConfigUpdate = async () => {
95237
95386
  await this.#getObserver().onConfig({
95238
95387
  configOptions: this.#configOptions,
95388
+ configurationRevision: this.#configRevision,
95239
95389
  kind: SESSION_CONFIG_UPDATED_KIND,
95240
95390
  requestId: `agent-update-${crypto.randomUUID()}`,
95241
95391
  sessionId: this.#mobiusSessionId
@@ -95663,7 +95813,7 @@ var init_session_manager = __esm({
95663
95813
  (option) => option.id === "model" || option.category === "model"
95664
95814
  );
95665
95815
  const modelIsFixed = modelOption2?.type === "select" && modelOption2.values.length < 2 && modelOption2.currentValue === model;
95666
- const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : await runtime.configure({
95816
+ const configOptions = model === void 0 || modelIsFixed ? prepared.configOptions : (await runtime.configure({
95667
95817
  ...options2,
95668
95818
  booleanValue: false,
95669
95819
  configId: modelOption2?.id ?? "model",
@@ -95671,7 +95821,7 @@ var init_session_manager = __esm({
95671
95821
  sessionId: `agent-settings-${crypto.randomUUID()}`,
95672
95822
  value: model,
95673
95823
  valueType: "select"
95674
- });
95824
+ })).configOptions;
95675
95825
  return new AgentSettingsSnapshot(
95676
95826
  prepared.agentName,
95677
95827
  prepared.agentVersion,
@@ -96525,19 +96675,22 @@ function promptContent(command) {
96525
96675
  if (command.prompt.length > 0) {
96526
96676
  content.push({ text: command.prompt, type: "text" });
96527
96677
  }
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) {
96678
+ for (const attachment of command.attachments) {
96536
96679
  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 }
96680
+ attachment.kind === "image" ? {
96681
+ byteLength: attachment.byteLength,
96682
+ imageKey: attachment.contentKey,
96683
+ mimeType: attachment.mimeType,
96684
+ type: "external_image",
96685
+ uri: attachment.uri
96686
+ } : {
96687
+ byteLength: attachment.byteLength,
96688
+ encoding: "text",
96689
+ mimeType: attachment.mimeType,
96690
+ resourceKey: attachment.contentKey,
96691
+ type: "external_resource",
96692
+ uri: attachment.uri
96693
+ }
96541
96694
  );
96542
96695
  }
96543
96696
  return content;
@@ -97258,8 +97411,7 @@ var init_session_turns = __esm({
97258
97411
  queued.push({
97259
97412
  commandId: candidate.command.commandId,
97260
97413
  prompt: candidate.command.prompt,
97261
- images: candidate.command.images,
97262
- resources: candidate.command.resources
97414
+ attachments: candidate.command.attachments
97263
97415
  });
97264
97416
  }
97265
97417
  return { turn: turnSnapshot ?? null, queued };
@@ -97584,7 +97736,7 @@ var init_session_layer = __esm({
97584
97736
 
97585
97737
  // src/storage/files/scratch-directory.ts
97586
97738
  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";
97739
+ 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
97740
  import { dirname as dirname12, join as join34 } from "node:path";
97589
97741
  async function atomicWrite(path8, bytes, signal) {
97590
97742
  const temporary = `${path8}.${randomUUID16()}.tmp`;
@@ -97652,6 +97804,12 @@ var init_scratch_directory = __esm({
97652
97804
  return null;
97653
97805
  }
97654
97806
  };
97807
+ /** Reads a regular file owned by this scratch directory after validating its canonical path. */
97808
+ read = async (path8, signal) => {
97809
+ const resolved = await this.resolveFile(path8);
97810
+ signal.throwIfAborted();
97811
+ return resolved === null ? null : new Uint8Array(await readFile12(resolved, { signal }));
97812
+ };
97655
97813
  removeAll = async () => {
97656
97814
  await rm4(this.#root, { force: true, recursive: true });
97657
97815
  };
@@ -97763,6 +97921,11 @@ var init_external_content_store = __esm({
97763
97921
  signal.throwIfAborted();
97764
97922
  return resolvedPath === null ? null : { mediaType: entry.mediaType, name: entry.name, path: resolvedPath };
97765
97923
  };
97924
+ read = (sessionId, request, signal) => this.#track(async () => {
97925
+ const active = AbortSignal.any([signal, this.#lifetime.signal]);
97926
+ const resolved = await this.#resolve(sessionId, request, active);
97927
+ return resolved === null ? null : this.#scratch.read(resolved.path, active);
97928
+ });
97766
97929
  [Symbol.asyncDispose] = async () => {
97767
97930
  clearInterval(this.#expirationTimer);
97768
97931
  this.#lifetime.abort();
@@ -98371,6 +98534,12 @@ function send(socket, message) {
98371
98534
  }
98372
98535
  socket.send(encodeDaemonToServiceMessage(message));
98373
98536
  }
98537
+ function sendContentChunk(socket, requestId, sequence, bytes) {
98538
+ if (socket.readyState !== WebSocket2.OPEN) {
98539
+ throw new Error("Relay connection is not open");
98540
+ }
98541
+ socket.send(encodeContentChunkFrame({ bytes, requestId, sequence }));
98542
+ }
98374
98543
  function reportOperationFailure(socket, requestId, sessionId, error62, runtimeTimings = []) {
98375
98544
  if (socket.readyState !== WebSocket2.OPEN) {
98376
98545
  return;
@@ -99057,6 +99226,134 @@ var init_agent_settings = __esm({
99057
99226
  }
99058
99227
  });
99059
99228
 
99229
+ // src/runtime/command/registry/content-write/index.ts
99230
+ import { createHash as createHash10 } from "node:crypto";
99231
+ function createContentWriteCommands(context) {
99232
+ const pending = /* @__PURE__ */ new Map();
99233
+ const operations = /* @__PURE__ */ new Set();
99234
+ const fail = (command, error62) => {
99235
+ pending.delete(command.requestId);
99236
+ send(context.socket, {
99237
+ code: "content_write_failed",
99238
+ kind: FILE_FAILED_KIND,
99239
+ message: error62 instanceof Error ? error62.message : "The content could not be stored",
99240
+ requestId: command.requestId,
99241
+ sessionId: command.sessionId
99242
+ });
99243
+ };
99244
+ const write = {
99245
+ kind: CONTENT_WRITE_KIND,
99246
+ handle: (_commandContext, message) => {
99247
+ if (message.kind !== CONTENT_WRITE_KIND) {
99248
+ throw new Error("Command kind mismatch");
99249
+ }
99250
+ const existing = pending.get(message.requestId);
99251
+ if (existing !== void 0) {
99252
+ if (JSON.stringify(existing.command) !== JSON.stringify(message)) {
99253
+ fail(message, new Error("The content request identifier is already in use"));
99254
+ return;
99255
+ }
99256
+ } else {
99257
+ pending.set(message.requestId, {
99258
+ bytes: new Uint8Array(message.byteLength),
99259
+ command: message,
99260
+ hash: createHash10("sha256"),
99261
+ receivedBytes: 0,
99262
+ sequence: 0
99263
+ });
99264
+ }
99265
+ send(context.socket, {
99266
+ kind: CONTENT_READY_KIND,
99267
+ requestId: message.requestId,
99268
+ sessionId: message.sessionId
99269
+ });
99270
+ }
99271
+ };
99272
+ const commit = {
99273
+ kind: CONTENT_COMMIT_KIND,
99274
+ handle: (_commandContext, message) => {
99275
+ if (message.kind !== CONTENT_COMMIT_KIND) {
99276
+ throw new Error("Command kind mismatch");
99277
+ }
99278
+ const state = pending.get(message.requestId);
99279
+ if (state === void 0 || state.command.sessionId !== message.sessionId) {
99280
+ fail(message, new Error("The content upload is unavailable"));
99281
+ return;
99282
+ }
99283
+ pending.delete(message.requestId);
99284
+ const operation = (async () => {
99285
+ try {
99286
+ if (state.receivedBytes !== state.command.byteLength) {
99287
+ throw new Error("The content upload ended at an unexpected length");
99288
+ }
99289
+ if (state.hash.digest("hex") !== state.command.contentKey) {
99290
+ throw new Error("The content upload failed integrity validation");
99291
+ }
99292
+ const stored = await context.externalContent.put({
99293
+ bytes: state.bytes,
99294
+ kind: state.command.contentKind,
99295
+ mediaType: state.command.mediaType,
99296
+ name: state.command.name,
99297
+ sessionId: state.command.sessionId,
99298
+ signal: context.signal
99299
+ });
99300
+ if (stored.key !== state.command.contentKey || stored.byteLength !== state.command.byteLength) {
99301
+ throw new Error("Stored content did not match the upload");
99302
+ }
99303
+ send(context.socket, {
99304
+ byteLength: stored.byteLength,
99305
+ contentKey: stored.key,
99306
+ kind: CONTENT_AVAILABLE_KIND,
99307
+ requestId: message.requestId,
99308
+ sessionId: message.sessionId
99309
+ });
99310
+ } catch (error62) {
99311
+ if (!context.signal.aborted) {
99312
+ fail(message, error62);
99313
+ }
99314
+ }
99315
+ })();
99316
+ operations.add(operation);
99317
+ void operation.finally(() => operations.delete(operation));
99318
+ }
99319
+ };
99320
+ return {
99321
+ commands: [write, commit],
99322
+ receiveChunk: (frame) => {
99323
+ const state = pending.get(frame.requestId);
99324
+ if (state === void 0) {
99325
+ context.diagnostics.daemonDiagnostic("content_chunk_discarded", frame.requestId, {
99326
+ reason: "upload_unavailable",
99327
+ sequence: frame.sequence.toString()
99328
+ });
99329
+ return;
99330
+ }
99331
+ if (frame.sequence !== state.sequence || state.receivedBytes + frame.bytes.byteLength > state.command.byteLength) {
99332
+ fail(state.command, new Error("The content upload contains invalid chunks"));
99333
+ return;
99334
+ }
99335
+ state.bytes.set(frame.bytes, state.receivedBytes);
99336
+ state.hash.update(frame.bytes);
99337
+ pending.set(frame.requestId, {
99338
+ ...state,
99339
+ receivedBytes: state.receivedBytes + frame.bytes.byteLength,
99340
+ sequence: state.sequence + 1
99341
+ });
99342
+ },
99343
+ settle: async () => {
99344
+ pending.clear();
99345
+ await Promise.allSettled([...operations]);
99346
+ }
99347
+ };
99348
+ }
99349
+ var init_content_write = __esm({
99350
+ "src/runtime/command/registry/content-write/index.ts"() {
99351
+ "use strict";
99352
+ init_protocol();
99353
+ init_transport();
99354
+ }
99355
+ });
99356
+
99060
99357
  // src/session/content/session-files.ts
99061
99358
  import { basename as basename9, extname as extname2, isAbsolute as isAbsolute18, join as join37, normalize as normalize3, resolve as resolve18 } from "node:path";
99062
99359
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -99099,7 +99396,7 @@ async function resolveFile(value, workingDirectory, signal) {
99099
99396
  }
99100
99397
  return null;
99101
99398
  }
99102
- async function streamLocalFile(command, send2, signal, workingDirectory, externalContent) {
99399
+ async function streamLocalFile(command, send2, sendChunk, signal, workingDirectory, externalContent) {
99103
99400
  const external = await externalContent.resolve(command.sessionId, command.path, signal);
99104
99401
  const target = external?.path ?? await resolveFile(command.path, workingDirectory, signal);
99105
99402
  if (target === null) {
@@ -99121,14 +99418,8 @@ async function streamLocalFile(command, send2, signal, workingDirectory, externa
99121
99418
  sessionId: command.sessionId
99122
99419
  });
99123
99420
  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
- });
99421
+ for await (const bytes of readFileChunks(target, CONTENT_CHUNK_MAX_BYTES, signal)) {
99422
+ sendChunk(command.requestId, sequence, bytes);
99132
99423
  sequence += 1;
99133
99424
  }
99134
99425
  const after = await readPathMetadata(target);
@@ -99142,14 +99433,12 @@ async function streamLocalFile(command, send2, signal, workingDirectory, externa
99142
99433
  sessionId: command.sessionId
99143
99434
  });
99144
99435
  }
99145
- var CHUNK_BYTES;
99146
99436
  var init_session_files = __esm({
99147
99437
  "src/session/content/session-files.ts"() {
99148
99438
  "use strict";
99149
99439
  init_protocol();
99150
99440
  init_raster();
99151
99441
  init_local_files();
99152
- CHUNK_BYTES = 192 * 1024;
99153
99442
  }
99154
99443
  });
99155
99444
 
@@ -99171,6 +99460,7 @@ var init_file_read = __esm({
99171
99460
  void streamLocalFile(
99172
99461
  message,
99173
99462
  (response) => send(socket, response),
99463
+ (requestId, sequence, bytes) => sendContentChunk(socket, requestId, sequence, bytes),
99174
99464
  signal,
99175
99465
  sessions.findSessions([message.sessionId])[0]?.cwd ?? null,
99176
99466
  externalContent
@@ -99813,7 +100103,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
99813
100103
  if (sessionConfigurationMatches(option, selection)) {
99814
100104
  return options;
99815
100105
  }
99816
- return await sessions.configureSession({
100106
+ return (await sessions.configureSession({
99817
100107
  adapter,
99818
100108
  additionalDirectories,
99819
100109
  agent,
@@ -99825,7 +100115,7 @@ async function applySessionConfigurationSelection(sessions, options, selection,
99825
100115
  signal,
99826
100116
  value: selection.value,
99827
100117
  valueType: selection.valueType
99828
- });
100118
+ })).configOptions;
99829
100119
  }
99830
100120
  async function applyCompleteSessionConfiguration(sessions, initialOptions, selections, agent, adapter, cwd, additionalDirectories, commandId, sessionId, signal) {
99831
100121
  let options = initialOptions;
@@ -99869,98 +100159,70 @@ var init_apply_session_configuration = __esm({
99869
100159
  });
99870
100160
 
99871
100161
  // 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
- }
100162
+ import { createHash as createHash11 } from "node:crypto";
99891
100163
  function isTextMediaType(mediaType) {
99892
100164
  return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType.endsWith("+json") || mediaType === "application/xml" || mediaType.endsWith("+xml");
99893
100165
  }
99894
- async function resolvePromptArtifacts(download, command, signal) {
99895
- const images = [...command.images];
100166
+ async function resolvePromptArtifacts(read, attachments, signal) {
100167
+ const images = [];
99896
100168
  const resources = [];
99897
- for (const resource of command.resources) {
100169
+ for (const attachment of attachments) {
99898
100170
  signal.throwIfAborted();
99899
- if (!resource.uri.startsWith(ARTIFACT_SCHEME)) {
99900
- resources.push(resource);
99901
- continue;
100171
+ const bytes = await read(attachment, signal);
100172
+ if (bytes.byteLength !== attachment.byteLength || createHash11("sha256").update(bytes).digest("hex") !== attachment.contentKey) {
100173
+ throw new PromptArtifactError(
100174
+ "artifact_integrity_failed",
100175
+ "The prompt attachment failed integrity validation"
100176
+ );
99902
100177
  }
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) {
100178
+ if (attachment.kind === "image") {
100179
+ if (bytes.byteLength > PROMPT_IMAGE_MAX_BYTES || images.length >= 8) {
99918
100180
  throw new PromptArtifactError(
99919
100181
  "artifact_too_large_for_agent",
99920
- "The prompt artifact exceeds the Agent image limit"
100182
+ "The prompt attachment exceeds the Agent image limit"
99921
100183
  );
99922
100184
  }
99923
- images.push({ data, mimeType: resource.mimeType, name: resource.name });
100185
+ images.push({
100186
+ data: Buffer.from(bytes).toString("base64"),
100187
+ mimeType: attachment.mimeType,
100188
+ name: attachment.name
100189
+ });
99924
100190
  continue;
99925
100191
  }
99926
- if (!isTextMediaType(resource.mimeType)) {
100192
+ if (!isTextMediaType(attachment.mimeType)) {
99927
100193
  throw new PromptArtifactError(
99928
100194
  "artifact_media_type_unsupported",
99929
- "The Agent cannot consume this prompt artifact media type"
100195
+ "The Agent cannot consume this prompt attachment media type"
99930
100196
  );
99931
100197
  }
99932
100198
  if (bytes.byteLength > MAX_AGENT_RESOURCE_BYTE_LENGTH || resources.length >= 16) {
99933
100199
  throw new PromptArtifactError(
99934
100200
  "artifact_too_large_for_agent",
99935
- "The prompt artifact exceeds the Agent resource limit"
100201
+ "The prompt attachment exceeds the Agent resource limit"
99936
100202
  );
99937
100203
  }
99938
100204
  let text2;
99939
100205
  try {
99940
100206
  text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
99941
100207
  } catch (cause) {
99942
- throw new PromptArtifactError("artifact_encoding_invalid", "The prompt artifact is not valid UTF-8 text", {
99943
- cause
99944
- });
100208
+ throw new PromptArtifactError(
100209
+ "artifact_encoding_invalid",
100210
+ "The prompt attachment is not valid UTF-8 text",
100211
+ {
100212
+ cause
100213
+ }
100214
+ );
99945
100215
  }
99946
- resources.push({
99947
- mimeType: resource.mimeType,
99948
- name: resource.name,
99949
- text: text2,
99950
- type: "embedded",
99951
- uri: resource.uri
99952
- });
100216
+ resources.push({ mimeType: attachment.mimeType, text: text2, type: "embedded", uri: attachment.uri });
99953
100217
  }
99954
100218
  return new ResolvedPromptArtifacts(images, resources);
99955
100219
  }
99956
- var ARTIFACT_SCHEME, MAX_AGENT_IMAGE_BASE64_LENGTH, MAX_AGENT_RESOURCE_BYTE_LENGTH, PromptArtifactError, ResolvedPromptArtifacts, PromptArtifactReference;
100220
+ var MAX_AGENT_RESOURCE_BYTE_LENGTH, PromptArtifactError, ResolvedPromptArtifacts;
99957
100221
  var init_prompt_artifacts = __esm({
99958
100222
  "src/session/prompt/prompt-artifacts.ts"() {
99959
100223
  "use strict";
99960
100224
  init_protocol();
99961
100225
  init_operation_error();
99962
- ARTIFACT_SCHEME = "mobius-artifact:";
99963
- MAX_AGENT_IMAGE_BASE64_LENGTH = 4e6;
99964
100226
  MAX_AGENT_RESOURCE_BYTE_LENGTH = 1e6;
99965
100227
  PromptArtifactError = class extends OperationError {
99966
100228
  constructor(code, message, options = {}) {
@@ -99976,18 +100238,6 @@ var init_prompt_artifacts = __esm({
99976
100238
  this.resources = resources;
99977
100239
  }
99978
100240
  };
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
100241
  }
99992
100242
  });
99993
100243
 
@@ -99997,7 +100247,7 @@ async function executePrompt({
99997
100247
  eventProjection,
99998
100248
  command,
99999
100249
  resolveAgent,
100000
- downloadArtifact,
100250
+ readArtifact,
100001
100251
  reportFailure,
100002
100252
  directories,
100003
100253
  sessions,
@@ -100079,7 +100329,7 @@ async function executePrompt({
100079
100329
  stream.epoch,
100080
100330
  availability.disposition
100081
100331
  );
100082
- const attachments = await resolvePromptArtifacts(downloadArtifact, command, signal);
100332
+ const attachments = await resolvePromptArtifacts(readArtifact, command.attachments, signal);
100083
100333
  let stopReason;
100084
100334
  if (availability.disposition === SessionPromptDisposition.Steer) {
100085
100335
  await sessions.steerPrompt({
@@ -100150,124 +100400,6 @@ var init_execute_prompt = __esm({
100150
100400
  }
100151
100401
  });
100152
100402
 
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
100403
  // src/runtime/command/registry/prompt.ts
100272
100404
  var sessionPromptCommand;
100273
100405
  var init_prompt2 = __esm({
@@ -100275,7 +100407,6 @@ var init_prompt2 = __esm({
100275
100407
  "use strict";
100276
100408
  init_protocol();
100277
100409
  init_execute_prompt();
100278
- init_artifact_download();
100279
100410
  init_transport();
100280
100411
  sessionPromptCommand = {
100281
100412
  kind: SESSION_PROMPT_KIND,
@@ -100329,7 +100460,17 @@ var init_prompt2 = __esm({
100329
100460
  eventProjection: context.eventProjection,
100330
100461
  command: message,
100331
100462
  resolveAgent: context.resolveAgent,
100332
- downloadArtifact: (artifact, signal) => downloadPromptArtifact(context.config, artifact, signal),
100463
+ readArtifact: async (artifact, signal) => {
100464
+ const bytes = await context.externalContent.read(
100465
+ message.sessionId,
100466
+ `mobius-content:${artifact.kind}:${artifact.contentKey}`,
100467
+ signal
100468
+ );
100469
+ if (bytes === null) {
100470
+ throw new Error("The prompt attachment is unavailable");
100471
+ }
100472
+ return bytes;
100473
+ },
100333
100474
  reportFailure: context.diagnostics.daemonDiagnostic,
100334
100475
  directories: context.directories,
100335
100476
  sessions: context.sessions,
@@ -100422,7 +100563,7 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
100422
100563
  };
100423
100564
  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
100565
  const configuration = command.configuration;
100425
- const configOptions = configuration === void 0 ? prepared.configOptions : await metrics.measure(
100566
+ const snapshot = configuration === void 0 ? prepared : await metrics.measure(
100426
100567
  "configuration",
100427
100568
  async () => await sessions.configureSession({
100428
100569
  ...preparation,
@@ -100440,7 +100581,8 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
100440
100581
  agentVersion: prepared.agentVersion,
100441
100582
  capabilities: prepared.capabilities,
100442
100583
  commands: prepared.commands,
100443
- configOptions,
100584
+ configOptions: snapshot.configOptions,
100585
+ configurationRevision: snapshot.configurationRevision,
100444
100586
  directoryId: command.directoryId,
100445
100587
  directoryLabel: resolved.directoryLabel,
100446
100588
  kind: "session.ready",
@@ -100573,11 +100715,12 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
100573
100715
  command.additionalDirectoryIds,
100574
100716
  signal
100575
100717
  );
100576
- const configOptions = await sessions.configureSession({
100718
+ const snapshot = await sessions.configureSession({
100577
100719
  additionalDirectories: resolved.additionalDirectories,
100578
100720
  ...await resolveAgent(command.agentId, signal, command.sessionId),
100579
100721
  booleanValue: command.booleanValue,
100580
100722
  configId: command.configId,
100723
+ ...command.expectedConfigurationRevision === void 0 ? {} : { expectedConfigurationRevision: command.expectedConfigurationRevision },
100581
100724
  cwd: resolved.cwd,
100582
100725
  requestId: command.requestId,
100583
100726
  sessionId: command.sessionId,
@@ -100586,7 +100729,7 @@ async function executeSessionConfigure(socket, command, config2, resolveAgent, d
100586
100729
  valueType: command.valueType
100587
100730
  });
100588
100731
  send(socket, {
100589
- configOptions,
100732
+ ...snapshot,
100590
100733
  kind: "session.config.updated",
100591
100734
  requestId: command.requestId,
100592
100735
  sessionId: command.sessionId
@@ -100634,6 +100777,7 @@ async function executeSessionConfigRead(socket, command, config2, resolveAgent,
100634
100777
  );
100635
100778
  send(socket, {
100636
100779
  configOptions: ready.configOptions,
100780
+ configurationRevision: ready.configurationRevision,
100637
100781
  kind: SESSION_CONFIG_READ_RESULT_KIND,
100638
100782
  requestId: command.requestId,
100639
100783
  sessionId: command.sessionId
@@ -100757,6 +100901,7 @@ var init_session5 = __esm({
100757
100901
 
100758
100902
  // src/runtime/command/registry/index.ts
100759
100903
  function createCommandRegistry(context) {
100904
+ const contentWrites = createContentWriteCommands(context);
100760
100905
  const commands = [
100761
100906
  runtimeDiagnosticsCommand,
100762
100907
  runtimePingCommand,
@@ -100765,6 +100910,7 @@ function createCommandRegistry(context) {
100765
100910
  ...createNativeSessionCommands(context),
100766
100911
  ...createAgentCatalogCommands(context),
100767
100912
  ...createAgentSettingsCommands(context),
100913
+ ...contentWrites.commands,
100768
100914
  fileReadCommand,
100769
100915
  sessionCancelCommand,
100770
100916
  sessionCloseCommand,
@@ -100787,16 +100933,22 @@ function createCommandRegistry(context) {
100787
100933
  }
100788
100934
  command.handle(context, message);
100789
100935
  },
100936
+ dispatchChunk: (message) => contentWrites.receiveChunk(decodeContentChunkFrame(message)),
100790
100937
  settle: async () => {
100791
- await Promise.all(commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()]));
100938
+ await Promise.all([
100939
+ contentWrites.settle(),
100940
+ ...commands.flatMap((command) => command.settle === void 0 ? [] : [command.settle()])
100941
+ ]);
100792
100942
  }
100793
100943
  };
100794
100944
  }
100795
100945
  var init_registry2 = __esm({
100796
100946
  "src/runtime/command/registry/index.ts"() {
100797
100947
  "use strict";
100948
+ init_protocol();
100798
100949
  init_agent_catalog();
100799
100950
  init_agent_settings();
100951
+ init_content_write();
100800
100952
  init_file_read();
100801
100953
  init_history3();
100802
100954
  init_interactions2();
@@ -100863,6 +101015,7 @@ function connectRelay(url2, signal) {
100863
101015
  dispatcher,
100864
101016
  headers
100865
101017
  });
101018
+ socket.binaryType = "arraybuffer";
100866
101019
  let released = false;
100867
101020
  const release = (reason) => {
100868
101021
  if (released) {
@@ -101254,7 +101407,16 @@ async function runRelayConnection({
101254
101407
  const handleMessage = (event) => {
101255
101408
  const encoded = event.data;
101256
101409
  if (typeof encoded !== "string") {
101257
- failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
101410
+ try {
101411
+ commands.dispatchChunk(encoded);
101412
+ } catch (error62) {
101413
+ failAndClose(
101414
+ new Error("Relay sent an invalid content chunk", { cause: error62 }),
101415
+ 4008,
101416
+ "Invalid content chunk",
101417
+ "protocol"
101418
+ );
101419
+ }
101258
101420
  return;
101259
101421
  }
101260
101422
  try {
@@ -102001,7 +102163,7 @@ async function runWorker(version2, signal) {
102001
102163
  async function runWorkerRuntime(version2, combined, shutdown, rejectionGuard) {
102002
102164
  const control = await openMobiusControlInstallation(combined, process.env);
102003
102165
  await prepareUpgradeData(control.stateDirectory, version2, combined);
102004
- const installation = await openMobiusCliInstallation(combined, process.env);
102166
+ const installation = await openMobiusVersionInstallation(version2, combined, process.env);
102005
102167
  const deadline = AbortSignal.any([combined, AbortSignal.timeout(15e3)]);
102006
102168
  let release = null;
102007
102169
  while (release === null) {
@@ -102173,11 +102335,7 @@ function registerUpgradeCommand(program2, context) {
102173
102335
  program2.command("upgrade").description("start the daemon if needed and upgrade it to the newest npm release").action(async () => {
102174
102336
  const signal = context.shutdownSignal;
102175
102337
  const control = await openMobiusControlInstallation(signal, process.env);
102176
- const running = await readWorkingProcessVersion(control.stateDirectory, signal);
102177
- const current = running ?? context.version;
102178
- if (running === null) {
102179
- await ensureWorkingProcess(control.stateDirectory, context.version, signal);
102180
- }
102338
+ const current = await readWorkingProcessVersion(control.stateDirectory, signal) ?? await ensureWorkingProcess(control.stateDirectory, context.version, signal);
102181
102339
  await reloadDaemon(control.stateDirectory, signal);
102182
102340
  const attempt = await requestDaemonReleaseUpgrade(control.stateDirectory, current, signal);
102183
102341
  if (attempt.kind === "current") {
@@ -102653,9 +102811,9 @@ var init_cli = __esm({
102653
102811
  });
102654
102812
 
102655
102813
  // src/cli/entrypoint.ts
102656
- var import_semver2 = __toESM(require_semver2(), 1);
102814
+ var import_semver3 = __toESM(require_semver2(), 1);
102657
102815
  init_package();
102658
- if (!(0, import_semver2.satisfies)(process.versions.node, package_default.engines.node)) {
102816
+ if (!(0, import_semver3.satisfies)(process.versions.node, package_default.engines.node)) {
102659
102817
  process.stderr.write(
102660
102818
  `Mobius requires Node.js ${package_default.engines.node}; found ${process.versions.node} at ${process.execPath}. Update Node.js and reinstall Mobius.
102661
102819
  `