@moxt-ai/mobius 0.0.31 → 0.0.32

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 (45) hide show
  1. package/dist/acp/activity.d.ts +1 -1
  2. package/dist/acp/activity.d.ts.map +1 -1
  3. package/dist/acp/activity.js +8 -6
  4. package/dist/acp/activity.js.map +1 -1
  5. package/dist/acp/session-manager.d.ts +2 -0
  6. package/dist/acp/session-manager.d.ts.map +1 -1
  7. package/dist/acp/session-manager.js +23 -10
  8. package/dist/acp/session-manager.js.map +1 -1
  9. package/dist/agent-catalog/agent-discovery.d.ts.map +1 -1
  10. package/dist/agent-catalog/agent-discovery.js +4 -0
  11. package/dist/agent-catalog/agent-discovery.js.map +1 -1
  12. package/dist/agent-catalog/local-agent.d.ts.map +1 -1
  13. package/dist/agent-catalog/local-agent.js +1 -0
  14. package/dist/agent-catalog/local-agent.js.map +1 -1
  15. package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -1
  16. package/dist/agent-installation/agent-installation-manager.js +3 -0
  17. package/dist/agent-installation/agent-installation-manager.js.map +1 -1
  18. package/dist/authorization/local-raster.d.ts +1 -0
  19. package/dist/authorization/local-raster.d.ts.map +1 -1
  20. package/dist/authorization/local-raster.js +17 -0
  21. package/dist/authorization/local-raster.js.map +1 -1
  22. package/dist/cli.js +131 -328
  23. package/dist/cli.js.map +4 -4
  24. package/dist/config.d.ts.map +1 -1
  25. package/dist/config.js +3 -10
  26. package/dist/config.js.map +1 -1
  27. package/dist/index.js +132 -338
  28. package/dist/index.js.map +4 -4
  29. package/dist/native-sessions/service.d.ts.map +1 -1
  30. package/dist/native-sessions/service.js +1 -30
  31. package/dist/native-sessions/service.js.map +1 -1
  32. package/dist/relay-connection/session-event-outbox.d.ts +0 -2
  33. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
  34. package/dist/relay-connection/session-event-outbox.js +6 -12
  35. package/dist/relay-connection/session-event-outbox.js.map +1 -1
  36. package/dist/run-daemon.d.ts.map +1 -1
  37. package/dist/run-daemon.js +47 -5
  38. package/dist/run-daemon.js.map +1 -1
  39. package/dist/runtime.js +130 -327
  40. package/dist/runtime.js.map +4 -4
  41. package/package.json +1 -1
  42. package/dist/native-sessions/codex-history.d.ts +0 -11
  43. package/dist/native-sessions/codex-history.d.ts.map +0 -1
  44. package/dist/native-sessions/codex-history.js +0 -233
  45. package/dist/native-sessions/codex-history.js.map +0 -1
package/dist/index.js CHANGED
@@ -20487,6 +20487,7 @@ var decodeNativeSessionTurnForkCommand = (encoded) => decode4(nativeSessionTurnF
20487
20487
  var SESSION_EVENT_KIND = "session.event";
20488
20488
  var SESSION_EVENT_ACK_KIND = "session.event.ack";
20489
20489
  var SESSION_EVENT_COMMITTED_KIND = "session.event.committed";
20490
+ var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
20490
20491
  var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
20491
20492
  var SessionEventRejectionCode = {
20492
20493
  SessionDeleted: "session_deleted",
@@ -20533,6 +20534,12 @@ var eventAcknowledgementSchema = external_exports.strictObject({
20533
20534
  sequence: external_exports.string().min(1).max(40).regex(DECIMAL_SEQUENCE_PATTERN),
20534
20535
  sessionId: external_exports.string().min(1).max(128).regex(IDENTIFIER_PATTERN4)
20535
20536
  }).readonly();
20537
+ var eventDeferredSchema = external_exports.strictObject({
20538
+ eventId: external_exports.string().min(1).max(128).regex(IDENTIFIER_PATTERN4),
20539
+ kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
20540
+ retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
20541
+ sessionId: external_exports.string().min(1).max(128).regex(IDENTIFIER_PATTERN4)
20542
+ }).readonly();
20536
20543
  var eventRejectionSchema = external_exports.strictObject({
20537
20544
  code: external_exports.enum(SessionEventRejectionCode),
20538
20545
  eventId: external_exports.string().min(1).max(128).regex(IDENTIFIER_PATTERN4),
@@ -20566,6 +20573,7 @@ var encode4 = (message, schema) => {
20566
20573
  var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
20567
20574
  var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
20568
20575
  var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
20576
+ var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
20569
20577
  var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
20570
20578
 
20571
20579
  // src/agent-catalog/local-agent.ts
@@ -21096,6 +21104,7 @@ var InstallableAgentDefinition = class {
21096
21104
  };
21097
21105
  var MissingAgentDefinition = class extends InstallableAgentDefinition {
21098
21106
  status = "agent_not_installed";
21107
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
21099
21108
  constructor(id, label) {
21100
21109
  super(id, label);
21101
21110
  }
@@ -21192,25 +21201,16 @@ function createDaemonConfig(connection, agents = [], installableAgents = []) {
21192
21201
  }
21193
21202
  function loadDaemonConfig() {
21194
21203
  const relayUrl = new URL(
21195
- // biome-ignore lint/complexity/useLiteralKeys: Environment variables use an index signature.
21196
21204
  process.env["MOBIUS_RELAY_URL"] ?? `ws://127.0.0.1:8787${mobiusApiPath("/channels/local/daemon")}`
21197
21205
  );
21198
- relayUrl.searchParams.set(
21199
- "token",
21200
- // biome-ignore lint/complexity/useLiteralKeys: Environment variables use an index signature.
21201
- process.env["MOBIUS_RELAY_TOKEN"] ?? "local-development-token"
21202
- );
21206
+ relayUrl.searchParams.set("token", process.env["MOBIUS_RELAY_TOKEN"] ?? "local-development-token");
21203
21207
  if (relayUrl.protocol !== "ws:" && relayUrl.protocol !== "wss:") {
21204
21208
  throw new Error("MOBIUS_RELAY_URL must use ws or wss");
21205
21209
  }
21206
21210
  return createDaemonConfig(
21207
21211
  {
21208
21212
  defaultWorkspace: loadDefaultWorkspace(),
21209
- auditDatabasePath: (
21210
- // biome-ignore lint/complexity/useLiteralKeys: Environment variables use an index signature.
21211
- process.env["MOBIUS_AUDIT_DATABASE"] ?? fileURLToPath(new URL("../../../.mobius/daemon.sqlite", import.meta.url))
21212
- ),
21213
- // biome-ignore lint/complexity/useLiteralKeys: Environment variables use an index signature.
21213
+ auditDatabasePath: process.env["MOBIUS_AUDIT_DATABASE"] ?? fileURLToPath(new URL("../../../.mobius/daemon.sqlite", import.meta.url)),
21214
21214
  channelId: process.env["MOBIUS_CHANNEL_ID"] ?? "local",
21215
21215
  relayUrl
21216
21216
  },
@@ -21271,6 +21271,23 @@ function sniffRasterMime(bytes) {
21271
21271
  }
21272
21272
  return null;
21273
21273
  }
21274
+ function readInlineRaster(data, name = "Image") {
21275
+ if (data.length === 0 || data.length > Math.ceil(MAX_LOCAL_RASTER_BYTES / 3) * 4 || data.length % 4 !== 0) {
21276
+ return null;
21277
+ }
21278
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data)) {
21279
+ return null;
21280
+ }
21281
+ const bytes = new Uint8Array(Buffer.from(data, "base64"));
21282
+ if (bytes.byteLength < 1 || bytes.byteLength > MAX_LOCAL_RASTER_BYTES) {
21283
+ return null;
21284
+ }
21285
+ const mimeType = sniffRasterMime(bytes);
21286
+ if (mimeType === null) {
21287
+ return null;
21288
+ }
21289
+ return new LocalRasterImage(basename(name) || "Image", mimeType, bytes);
21290
+ }
21274
21291
  function isInsideRoot(root, target) {
21275
21292
  const relation = relative(root, target);
21276
21293
  return relation !== ".." && !relation.startsWith(`..${sep}`) && !isAbsolute(relation);
@@ -21635,7 +21652,7 @@ function normalizeContentBlock(content) {
21635
21652
  mimeType: content.mimeType
21636
21653
  };
21637
21654
  }
21638
- function normalizeToolContent(content) {
21655
+ async function normalizeToolContent(content, prepareContent) {
21639
21656
  const normalized = [];
21640
21657
  for (const item of content) {
21641
21658
  if (item.type === "diff") {
@@ -21654,7 +21671,7 @@ function normalizeToolContent(content) {
21654
21671
  });
21655
21672
  continue;
21656
21673
  }
21657
- normalized.push(normalizeContentBlock(item.content));
21674
+ normalized.push(await prepareContent(normalizeContentBlock(item.content)));
21658
21675
  }
21659
21676
  return normalized;
21660
21677
  }
@@ -21728,10 +21745,12 @@ var AcpActivityTracker = class {
21728
21745
  #activities = /* @__PURE__ */ new Map();
21729
21746
  #identities = /* @__PURE__ */ new Map();
21730
21747
  #onActivity;
21748
+ #prepareContent;
21731
21749
  #activeNarrativeKey = "";
21732
21750
  #nextSequence = 0;
21733
- constructor(onActivity) {
21751
+ constructor(onActivity, prepareContent = (content) => Promise.resolve(content)) {
21734
21752
  this.#onActivity = onActivity;
21753
+ this.#prepareContent = prepareContent;
21735
21754
  }
21736
21755
  recordNarrative = async (activityType, messageId, text) => {
21737
21756
  const key = `${activityType}:${messageId}`;
@@ -21819,7 +21838,7 @@ var AcpActivityTracker = class {
21819
21838
  const output2 = update.rawOutput === void 0 ? readContentOutput(update.content) : formatUnknown(update.rawOutput);
21820
21839
  const detailInput = update.rawInput === void 0 ? "" : formatUnknownDetail(update.rawInput);
21821
21840
  const detailOutput = update.rawOutput === void 0 ? readContentOutputDetail(update.content) : formatUnknownDetail(update.rawOutput);
21822
- const detailContent = normalizeToolContent(update.content ?? []);
21841
+ const detailContent = await normalizeToolContent(update.content ?? [], this.#prepareContent);
21823
21842
  await this.#record(
21824
21843
  key,
21825
21844
  new AcpActivity(
@@ -21845,10 +21864,10 @@ var AcpActivityTracker = class {
21845
21864
  const identity2 = this.#identity(key);
21846
21865
  const previousDetail = recordedDetail(previous);
21847
21866
  const output2 = update.rawOutput !== void 0 ? formatUnknown(update.rawOutput) : update.content !== void 0 && update.content !== null ? readContentOutput(update.content) : previous?.output ?? "";
21848
- const content = update.content === void 0 || update.content === null ? previous?.content ?? [] : normalizeToolContent(update.content);
21867
+ const content = update.content === void 0 || update.content === null ? previous?.content ?? [] : await normalizeToolContent(update.content, this.#prepareContent);
21849
21868
  const detailInput = update.rawInput === void 0 ? previousDetail?.input ?? previous?.input ?? "" : formatUnknownDetail(update.rawInput);
21850
21869
  const detailOutput = update.rawOutput !== void 0 ? formatUnknownDetail(update.rawOutput) : update.content !== void 0 && update.content !== null ? readContentOutputDetail(update.content) : previousDetail?.output ?? previous?.output ?? "";
21851
- const detailContent = update.content === void 0 || update.content === null ? previousDetail?.content ?? previous?.content ?? [] : normalizeToolContent(update.content);
21870
+ const detailContent = update.content === void 0 || update.content === null ? previousDetail?.content ?? previous?.content ?? [] : content;
21852
21871
  await this.#record(
21853
21872
  key,
21854
21873
  new AcpActivity(
@@ -23014,10 +23033,10 @@ var RunningAcpTurn = class extends AcpTurnState {
23014
23033
  running = true;
23015
23034
  activities;
23016
23035
  onText;
23017
- constructor(commandId, onActivity, onText) {
23036
+ constructor(commandId, onActivity, onText, prepareContent) {
23018
23037
  super();
23019
23038
  this.commandId = commandId;
23020
- this.activities = new AcpActivityTracker(onActivity);
23039
+ this.activities = new AcpActivityTracker(onActivity, prepareContent);
23021
23040
  this.onText = onText;
23022
23041
  }
23023
23042
  };
@@ -23301,18 +23320,27 @@ var AcpProcessSession = class _AcpProcessSession {
23301
23320
  await this.#turn.onText(leftover, messageId);
23302
23321
  }
23303
23322
  };
23304
- emitAgentContent = async (content, messageId) => {
23323
+ prepareAgentContent = async (content) => {
23324
+ if (content.type === "image" && content.data.length > 0) {
23325
+ const raster = readInlineRaster(content.data, content.name);
23326
+ if (raster !== null) {
23327
+ return await this.importRaster(raster, AbortSignal.timeout(2e4));
23328
+ }
23329
+ }
23305
23330
  if (content.type === "image" && content.data.length === 0 && content.uri.length > 0) {
23306
23331
  const signal = AbortSignal.timeout(2e4);
23307
23332
  const raster = await readAuthorizedRaster(content.uri, this.imageRoots(), signal);
23308
23333
  if (raster !== null) {
23309
- await this.emitRaster(raster, messageId, signal);
23310
- return;
23334
+ return await this.importRaster(raster, signal);
23311
23335
  }
23312
23336
  }
23337
+ return content;
23338
+ };
23339
+ emitAgentContent = async (content, messageId) => {
23340
+ const prepared = await this.prepareAgentContent(content);
23313
23341
  await this.#getObserver().onContent({
23314
23342
  commandId: this.#turn.commandId,
23315
- content,
23343
+ content: prepared,
23316
23344
  contentId: `content-${crypto.randomUUID()}`,
23317
23345
  kind: AGENT_CONTENT_KIND,
23318
23346
  messageId,
@@ -23320,21 +23348,25 @@ var AcpProcessSession = class _AcpProcessSession {
23320
23348
  });
23321
23349
  };
23322
23350
  emitRaster = async (raster, messageId, signal) => {
23323
- const artifactId = await this.#getObserver().importLocalImage(this.#mobiusSessionId, raster, signal);
23351
+ const content = await this.importRaster(raster, signal);
23324
23352
  await this.#getObserver().onContent({
23325
23353
  commandId: this.#turn.commandId,
23326
- content: imageDisplayContent(
23327
- raster.mimeType,
23328
- raster.name,
23329
- artifactId.length > 0 ? "" : raster.base64(),
23330
- artifactId
23331
- ),
23354
+ content,
23332
23355
  contentId: `content-${crypto.randomUUID()}`,
23333
23356
  kind: AGENT_CONTENT_KIND,
23334
23357
  messageId,
23335
23358
  sessionId: this.#mobiusSessionId
23336
23359
  });
23337
23360
  };
23361
+ importRaster = async (raster, signal) => {
23362
+ const artifactId = await this.#getObserver().importLocalImage(this.#mobiusSessionId, raster, signal);
23363
+ return imageDisplayContent(
23364
+ raster.mimeType,
23365
+ raster.name,
23366
+ artifactId.length > 0 ? "" : raster.base64(),
23367
+ artifactId
23368
+ );
23369
+ };
23338
23370
  async #initializeAgent(signal) {
23339
23371
  const initialized = await this.race(
23340
23372
  this.#connection.agent.request(
@@ -23702,7 +23734,12 @@ var AcpProcessSession = class _AcpProcessSession {
23702
23734
  )
23703
23735
  );
23704
23736
  this.#prompted = true;
23705
- const running = new RunningAcpTurn(options.commandId, options.onActivity, options.onText);
23737
+ const running = new RunningAcpTurn(
23738
+ options.commandId,
23739
+ options.onActivity,
23740
+ options.onText,
23741
+ this.prepareAgentContent
23742
+ );
23706
23743
  this.#turn = running;
23707
23744
  try {
23708
23745
  const response = await this.race(
@@ -25910,6 +25947,7 @@ var MissingAdapter = class extends UnavailableInstalledAgent {
25910
25947
  code = "adapter_not_found";
25911
25948
  message = "ACP adapter not found";
25912
25949
  status = "missing_adapter";
25950
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
25913
25951
  constructor(specification, agent) {
25914
25952
  super(specification, agent);
25915
25953
  }
@@ -25918,6 +25956,7 @@ var MissingAgent = class extends UnavailableAgent {
25918
25956
  code = "agent_not_found";
25919
25957
  message = "Agent executable not found";
25920
25958
  status = "missing_agent";
25959
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
25921
25960
  constructor(specification) {
25922
25961
  super(specification);
25923
25962
  }
@@ -25946,6 +25985,7 @@ var IncompatibleAdapter = class extends UnavailableInstalledAgent {
25946
25985
  code = "acp_incompatible";
25947
25986
  message = "ACP adapter is incompatible";
25948
25987
  status = "incompatible_adapter";
25988
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
25949
25989
  constructor(specification, agent) {
25950
25990
  super(specification, agent);
25951
25991
  }
@@ -25954,6 +25994,7 @@ var AuthenticationRequiredAgent = class extends UnavailableInstalledAgent {
25954
25994
  code = "authentication_required";
25955
25995
  message = "Agent authentication is required";
25956
25996
  status = "authentication_required";
25997
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
25957
25998
  constructor(specification, agent) {
25958
25999
  super(specification, agent);
25959
26000
  }
@@ -28301,6 +28342,7 @@ var AgentInstallation = class {
28301
28342
  };
28302
28343
  var RequestedInstallation = class extends AgentInstallation {
28303
28344
  status = "requested";
28345
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
28304
28346
  constructor(requestId, agentId) {
28305
28347
  super(requestId, agentId);
28306
28348
  }
@@ -28316,6 +28358,7 @@ var RunningInstallation = class extends AgentInstallation {
28316
28358
  var SucceededInstallation = class extends AgentInstallation {
28317
28359
  authenticationMayBeRequired = true;
28318
28360
  status = "succeeded";
28361
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
28319
28362
  constructor(requestId, agentId) {
28320
28363
  super(requestId, agentId);
28321
28364
  }
@@ -28332,6 +28375,7 @@ var FailedInstallation = class extends AgentInstallation {
28332
28375
  };
28333
28376
  var CancelledInstallation = class extends AgentInstallation {
28334
28377
  status = "cancelled";
28378
+ // biome-ignore lint/complexity/noUselessConstructor: Exposes the protected base constructor publicly.
28335
28379
  constructor(requestId, agentId) {
28336
28380
  super(requestId, agentId);
28337
28381
  }
@@ -29338,255 +29382,6 @@ var EmbeddedBotContextApplicator = class {
29338
29382
  // src/native-sessions/service.ts
29339
29383
  import { randomUUID as randomUUID4 } from "node:crypto";
29340
29384
  import { basename as basename4 } from "node:path";
29341
-
29342
- // src/native-sessions/codex-history.ts
29343
- import { spawn as spawn4 } from "node:child_process";
29344
- var responseSchema = external_exports.union([
29345
- external_exports.object({
29346
- id: external_exports.number(),
29347
- error: external_exports.object({ code: external_exports.number(), message: external_exports.string() })
29348
- }),
29349
- external_exports.object({ id: external_exports.number(), result: external_exports.unknown() }),
29350
- external_exports.object({ method: external_exports.string() })
29351
- ]);
29352
- var threadSchema = external_exports.object({
29353
- thread: external_exports.object({
29354
- id: nativeSessionIdSchema,
29355
- cwd: external_exports.string().min(1).max(2048).refine((path2) => !path2.includes("\0"))
29356
- })
29357
- });
29358
- var turnsSchema = external_exports.object({
29359
- data: external_exports.array(external_exports.object({ items: external_exports.array(external_exports.unknown()).max(1e5) })).max(SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT),
29360
- nextCursor: external_exports.string().nullish()
29361
- });
29362
- var itemTypeSchema = external_exports.object({ type: external_exports.string().min(1).max(80) });
29363
- var userMessageSchema = external_exports.object({
29364
- content: external_exports.array(external_exports.unknown()).max(1e4)
29365
- });
29366
- var textSchema = external_exports.object({ text: external_exports.string() });
29367
- var imageSchema = external_exports.object({ url: external_exports.string() });
29368
- var agentMessageSchema = textSchema.extend({ phase: external_exports.string().nullish() });
29369
- function decodeHistory(thread, value) {
29370
- const page = turnsSchema.parse(value);
29371
- const messages = [];
29372
- let characters = 0;
29373
- let truncated = page.nextCursor != null;
29374
- for (const turn of page.data.toReversed()) {
29375
- for (const item of turn.items) {
29376
- const { type } = itemTypeSchema.parse(item);
29377
- let message;
29378
- if (type === "userMessage") {
29379
- const { content } = userMessageSchema.parse(item);
29380
- const text2 = content.map((block) => {
29381
- const { type: type2 } = itemTypeSchema.parse(block);
29382
- if (type2 === "text") {
29383
- return textSchema.parse(block).text;
29384
- }
29385
- if (type2 === "image") {
29386
- return `[@image](${imageSchema.parse(block).url})`;
29387
- }
29388
- return "[Attachment]";
29389
- }).join("");
29390
- message = {
29391
- role: "user",
29392
- text: text2
29393
- };
29394
- } else if (type === "agentMessage") {
29395
- const agent = agentMessageSchema.parse(item);
29396
- if (agent.phase === "commentary") {
29397
- continue;
29398
- }
29399
- message = { role: "assistant", text: agent.text };
29400
- } else {
29401
- continue;
29402
- }
29403
- const text = message.text.slice(-SESSION_HISTORY_CHARACTER_LIMIT);
29404
- if (text.length === 0 && (message.images?.length ?? 0) === 0) {
29405
- continue;
29406
- }
29407
- truncated ||= text.length < message.text.length;
29408
- messages.push({ ...message, text });
29409
- characters += text.length;
29410
- while (messages.length > SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT || characters > SESSION_HISTORY_CHARACTER_LIMIT) {
29411
- const removed = messages.shift();
29412
- if (removed !== void 0) {
29413
- characters -= removed.text.length;
29414
- }
29415
- truncated = true;
29416
- }
29417
- }
29418
- }
29419
- return {
29420
- nativeSessionId: thread.id,
29421
- cwd: thread.cwd,
29422
- history: {
29423
- status: "available",
29424
- messages,
29425
- truncated,
29426
- coveredCommandIds: [],
29427
- coveredMessageIds: []
29428
- }
29429
- };
29430
- }
29431
- async function readCodexHistory(agent, cwd, nativeSessionId, signal) {
29432
- signal.throwIfAborted();
29433
- nativeSessionIdSchema.parse(nativeSessionId);
29434
- if (agent.id !== "codex" || !await revalidateExecutable(agent.agentIdentity)) {
29435
- throw new AcpExecutionError(
29436
- "The local Codex executable changed. Restart Mobius to discover it again.",
29437
- {},
29438
- "agent_executable_changed"
29439
- );
29440
- }
29441
- signal.throwIfAborted();
29442
- const child = spawn4(agent.agentIdentity.realPath, ["app-server"], {
29443
- cwd,
29444
- env: { ...process.env, ...agent.environment },
29445
- detached: process.platform !== "win32",
29446
- shell: false,
29447
- stdio: ["pipe", "pipe", "ignore"]
29448
- });
29449
- const response = Promise.withResolvers();
29450
- let state = { kind: "initializing", requestId: 1 };
29451
- let pending = "";
29452
- let receivedBytes = 0;
29453
- const fail = (cause) => response.reject(
29454
- cause instanceof AcpExecutionError ? cause : new AcpExecutionError(
29455
- "Saved Codex history could not be read.",
29456
- { cause },
29457
- "native_history_unavailable"
29458
- )
29459
- );
29460
- const send2 = (message) => {
29461
- child.stdin.write(`${JSON.stringify(message)}
29462
- `);
29463
- };
29464
- const onData = (chunk) => {
29465
- try {
29466
- receivedBytes += Buffer.byteLength(chunk);
29467
- if (receivedBytes > 32 * 1024 * 1024) {
29468
- throw new AcpExecutionError(
29469
- "This saved conversation is too large to preview.",
29470
- {},
29471
- "native_history_too_large"
29472
- );
29473
- }
29474
- pending += chunk;
29475
- let newline = pending.indexOf("\n");
29476
- while (newline >= 0) {
29477
- const line = pending.slice(0, newline);
29478
- pending = pending.slice(newline + 1);
29479
- newline = pending.indexOf("\n");
29480
- if (line.trim().length === 0) {
29481
- continue;
29482
- }
29483
- const value = JSON.parse(line);
29484
- const message = responseSchema.parse(value);
29485
- if (!("id" in message)) {
29486
- continue;
29487
- }
29488
- if (message.id !== state.requestId) {
29489
- throw new AcpExecutionError(
29490
- "Codex returned an invalid history response.",
29491
- {},
29492
- "invalid_native_session_response"
29493
- );
29494
- }
29495
- if ("error" in message) {
29496
- fail(
29497
- new AcpExecutionError(
29498
- "Saved Codex history could not be read.",
29499
- { cause: message.error },
29500
- "native_history_unavailable"
29501
- )
29502
- );
29503
- return;
29504
- }
29505
- switch (state.kind) {
29506
- case "initializing":
29507
- state = { kind: "metadata", requestId: 2 };
29508
- send2({ method: "initialized", params: {} });
29509
- send2({
29510
- id: state.requestId,
29511
- method: "thread/read",
29512
- params: { threadId: nativeSessionId, includeTurns: false }
29513
- });
29514
- break;
29515
- case "metadata": {
29516
- const { thread } = threadSchema.parse(message.result);
29517
- state = { kind: "turns", requestId: 3, thread };
29518
- send2({
29519
- id: state.requestId,
29520
- method: "thread/turns/list",
29521
- params: {
29522
- threadId: nativeSessionId,
29523
- limit: SESSION_HISTORY_PREVIEW_MESSAGE_LIMIT,
29524
- sortDirection: "desc",
29525
- itemsView: "summary"
29526
- }
29527
- });
29528
- break;
29529
- }
29530
- case "turns":
29531
- response.resolve(decodeHistory(state.thread, message.result));
29532
- return;
29533
- default: {
29534
- const unhandled = state;
29535
- throw new Error(`Unknown history read state: ${String(unhandled)}`);
29536
- }
29537
- }
29538
- }
29539
- } catch (cause) {
29540
- fail(cause);
29541
- }
29542
- };
29543
- const abort = () => response.reject(signal.reason);
29544
- const onExit = () => fail(
29545
- new AcpExecutionError(
29546
- "Codex stopped before its saved history was available.",
29547
- {},
29548
- "native_history_unavailable"
29549
- )
29550
- );
29551
- const timeout = setTimeout(
29552
- () => fail(new AcpExecutionError("Reading saved Codex history timed out.", {}, "native_history_timeout")),
29553
- 15e3
29554
- );
29555
- child.stdout.setEncoding("utf8");
29556
- child.stdout.on("data", onData);
29557
- child.stdin.on("error", fail);
29558
- child.on("error", fail);
29559
- child.on("exit", onExit);
29560
- signal.addEventListener("abort", abort, { once: true });
29561
- try {
29562
- if (signal.aborted) {
29563
- abort();
29564
- } else {
29565
- send2({
29566
- id: 1,
29567
- method: "initialize",
29568
- params: { clientInfo: { name: "mobius_history", version: "1.0.0" } }
29569
- });
29570
- }
29571
- const snapshot = await response.promise;
29572
- return {
29573
- ...snapshot,
29574
- history: await embedLocalHistoryImages(snapshot.history, [cwd, snapshot.cwd], signal)
29575
- };
29576
- } finally {
29577
- clearTimeout(timeout);
29578
- signal.removeEventListener("abort", abort);
29579
- child.stdout.removeListener("data", onData);
29580
- if (child.pid !== void 0) {
29581
- await stopProcessTree(child);
29582
- }
29583
- child.stdin.removeListener("error", fail);
29584
- child.removeListener("error", fail);
29585
- child.removeListener("exit", onExit);
29586
- }
29587
- }
29588
-
29589
- // src/native-sessions/service.ts
29590
29385
  var NativeSessionService = class {
29591
29386
  #entries = /* @__PURE__ */ new Map();
29592
29387
  #cursors = /* @__PURE__ */ new Map();
@@ -29749,7 +29544,7 @@ var NativeSessionService = class {
29749
29544
  "native_session_unsupported"
29750
29545
  );
29751
29546
  }
29752
- const opening = this.#sessions.openNativeSession(
29547
+ const opened = await this.#sessions.openNativeSession(
29753
29548
  {
29754
29549
  ...options,
29755
29550
  cwd: target.cwd,
@@ -29758,35 +29553,6 @@ var NativeSessionService = class {
29758
29553
  },
29759
29554
  { kind: command.action, nativeSessionId: target.info.sessionId }
29760
29555
  );
29761
- const opened = await opening.catch(async (cause) => {
29762
- signal.throwIfAborted();
29763
- if (command.action !== "import" || options.agent.id !== "codex" || !(cause instanceof AcpExecutionError) || cause.code !== "acp_execution_failed") {
29764
- throw cause;
29765
- }
29766
- const snapshot = await readCodexHistory(options.agent, target.cwd, target.info.sessionId, signal);
29767
- const resolved = await this.#directories.identify(command.bindingId, snapshot.cwd, signal);
29768
- if (snapshot.nativeSessionId !== target.info.sessionId || resolved.path !== target.cwd) {
29769
- throw new AcpExecutionError(
29770
- "Codex returned history for a different session or workspace.",
29771
- { cause },
29772
- "invalid_native_session_response"
29773
- );
29774
- }
29775
- signal.throwIfAborted();
29776
- this.record(command, "native_session_previewed");
29777
- return { kind: "preview", history: snapshot.history };
29778
- });
29779
- if ("kind" in opened) {
29780
- return {
29781
- ...envelope2,
29782
- action: "preview",
29783
- entryId: command.entryId,
29784
- reason: "continuation_unavailable",
29785
- targetSessionId: command.targetSessionId,
29786
- title: target.info.title ?? "Untitled session",
29787
- history: opened.history
29788
- };
29789
- }
29790
29556
  signal.throwIfAborted();
29791
29557
  const ready = {
29792
29558
  additionalDirectories: target.additionalEntries,
@@ -30522,11 +30288,9 @@ import { dirname as dirname7 } from "node:path";
30522
30288
  import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
30523
30289
  var DEFAULT_OUTBOX_LIMITS = {
30524
30290
  criticalEventBytes: 136 * 1024 * 1024,
30525
- criticalEventCount: 10128,
30526
30291
  runtimeDeliveryBytes: 64 * 1024 * 1024,
30527
30292
  runtimeDeliveryCount: 64,
30528
- standardEventBytes: 128 * 1024 * 1024,
30529
- standardEventCount: 1e4
30293
+ standardEventBytes: 128 * 1024 * 1024
30530
30294
  };
30531
30295
  var SessionEventOutboxCapacityError = class extends Error {
30532
30296
  code = "session_event_outbox_full";
@@ -30809,21 +30573,9 @@ var SessionEventOutbox = class {
30809
30573
  this.#database.close();
30810
30574
  };
30811
30575
  enqueue = (sessionId, payload, attemptId = null) => {
30812
- return this.#enqueueBounded(
30813
- sessionId,
30814
- payload,
30815
- attemptId ?? "",
30816
- this.#limits.standardEventCount,
30817
- this.#limits.standardEventBytes
30818
- );
30576
+ return this.#enqueueBounded(sessionId, payload, attemptId ?? "", this.#limits.standardEventBytes);
30819
30577
  };
30820
- enqueueCritical = (sessionId, payload, attemptId = "") => this.#enqueueBounded(
30821
- sessionId,
30822
- payload,
30823
- attemptId,
30824
- this.#limits.criticalEventCount,
30825
- this.#limits.criticalEventBytes
30826
- );
30578
+ enqueueCritical = (sessionId, payload, attemptId = "") => this.#enqueueBounded(sessionId, payload, attemptId, this.#limits.criticalEventBytes);
30827
30579
  claimRuntimeDelivery = (delivery) => {
30828
30580
  const payloadBytes = new TextEncoder().encode(delivery.payload).byteLength;
30829
30581
  this.#database.exec("BEGIN IMMEDIATE");
@@ -30930,7 +30682,7 @@ var SessionEventOutbox = class {
30930
30682
  true
30931
30683
  )
30932
30684
  );
30933
- #enqueueBounded = (sessionId, payload, attemptId, maximumEventCount, maximumEventBytes) => {
30685
+ #enqueueBounded = (sessionId, payload, attemptId, maximumEventBytes) => {
30934
30686
  const sourceKey = attemptId.length === 0 ? `runtime:${sessionId}` : `attempt:${sessionId}:${attemptId}`;
30935
30687
  this.#database.exec("BEGIN IMMEDIATE");
30936
30688
  try {
@@ -30957,11 +30709,11 @@ var SessionEventOutbox = class {
30957
30709
  const encoded = encodeSessionEventEnvelope(envelope2);
30958
30710
  const byteLength = new TextEncoder().encode(encoded).byteLength;
30959
30711
  const state = this.#database.prepare(
30960
- `SELECT event_count, total_bytes
30712
+ `SELECT total_bytes
30961
30713
  FROM relay_session_event_outbox_state
30962
30714
  WHERE singleton = 1`
30963
30715
  ).get();
30964
- if (rowNumber(state, "event_count") + 1 > maximumEventCount || rowNumber(state, "total_bytes") + byteLength > maximumEventBytes) {
30716
+ if (rowNumber(state, "total_bytes") + byteLength > maximumEventBytes) {
30965
30717
  throw new SessionEventOutboxCapacityError();
30966
30718
  }
30967
30719
  this.#database.prepare(
@@ -31095,16 +30847,14 @@ var SessionEventOutbox = class {
31095
30847
  function validateLimits(limits) {
31096
30848
  const values = [
31097
30849
  limits.criticalEventBytes,
31098
- limits.criticalEventCount,
31099
30850
  limits.runtimeDeliveryBytes,
31100
30851
  limits.runtimeDeliveryCount,
31101
- limits.standardEventBytes,
31102
- limits.standardEventCount
30852
+ limits.standardEventBytes
31103
30853
  ];
31104
30854
  if (values.some((value) => !Number.isSafeInteger(value) || value < 1)) {
31105
30855
  throw new Error("Session event outbox limits must be positive integers");
31106
30856
  }
31107
- if (limits.criticalEventBytes < limits.standardEventBytes || limits.criticalEventCount < limits.standardEventCount) {
30857
+ if (limits.criticalEventBytes < limits.standardEventBytes) {
31108
30858
  throw new Error("Critical Session event capacity must include the standard capacity");
31109
30859
  }
31110
30860
  }
@@ -31454,10 +31204,13 @@ function sessionEventSourceKey(envelope2) {
31454
31204
  var DaemonSessionEventPublisher = class {
31455
31205
  #outbox;
31456
31206
  #inFlightEvents = /* @__PURE__ */ new Map();
31207
+ #pausedSources = /* @__PURE__ */ new Map();
31208
+ #timer;
31457
31209
  #inFlightBytes = 0;
31458
31210
  #connection = new DisconnectedDaemonSessionEventState();
31459
- constructor(outbox) {
31211
+ constructor(outbox, timer) {
31460
31212
  this.#outbox = outbox;
31213
+ this.#timer = timer;
31461
31214
  }
31462
31215
  connect = (socket) => {
31463
31216
  this.#connection = new ConnectedDaemonSessionEventState(socket);
@@ -31500,6 +31253,30 @@ var DaemonSessionEventPublisher = class {
31500
31253
  this.#publishNextBatch();
31501
31254
  return true;
31502
31255
  };
31256
+ defer = (deferred) => {
31257
+ const inFlight = this.#inFlightEvents.get(deferred.eventId);
31258
+ if (inFlight === void 0) {
31259
+ return;
31260
+ }
31261
+ this.#clearInFlightSource(inFlight.sourceKey);
31262
+ if (!this.#pausedSources.has(inFlight.sourceKey)) {
31263
+ this.#pausedSources.set(
31264
+ inFlight.sourceKey,
31265
+ this.#timer.schedule(() => {
31266
+ this.#pausedSources.delete(inFlight.sourceKey);
31267
+ this.#publishNextBatch();
31268
+ }, deferred.retryAfterMilliseconds)
31269
+ );
31270
+ }
31271
+ this.#publishNextBatch();
31272
+ };
31273
+ close = () => {
31274
+ for (const task of this.#pausedSources.values()) {
31275
+ task.cancel();
31276
+ }
31277
+ this.#pausedSources.clear();
31278
+ this.#clearInFlight();
31279
+ };
31503
31280
  #publishNextBatch = () => {
31504
31281
  while (this.#inFlightEvents.size < SESSION_EVENT_REPLAY_WINDOW) {
31505
31282
  const sourceCounts = /* @__PURE__ */ new Map();
@@ -31510,7 +31287,7 @@ var DaemonSessionEventPublisher = class {
31510
31287
  const batch = this.#outbox.pendingSourceHeads(
31511
31288
  SESSION_EVENT_REPLAY_WINDOW - this.#inFlightEvents.size,
31512
31289
  [...this.#inFlightEvents.keys()],
31513
- saturatedSources
31290
+ [...this.#pausedSources.keys(), ...saturatedSources]
31514
31291
  );
31515
31292
  let sent = 0;
31516
31293
  for (const encoded of batch) {
@@ -31550,6 +31327,15 @@ var DaemonSessionEventPublisher = class {
31550
31327
  this.#inFlightEvents.clear();
31551
31328
  this.#inFlightBytes = 0;
31552
31329
  };
31330
+ #clearInFlightSource = (sourceKey) => {
31331
+ for (const [eventId, inFlight] of this.#inFlightEvents) {
31332
+ if (inFlight.sourceKey !== sourceKey) {
31333
+ continue;
31334
+ }
31335
+ this.#inFlightEvents.delete(eventId);
31336
+ this.#inFlightBytes -= inFlight.byteLength;
31337
+ }
31338
+ };
31553
31339
  };
31554
31340
  var CoalescingDaemonActivityPublisher = class {
31555
31341
  #pending = /* @__PURE__ */ new Map();
@@ -32484,6 +32270,10 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32484
32270
  publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
32485
32271
  return;
32486
32272
  }
32273
+ if (kind === SESSION_EVENT_DEFERRED_KIND) {
32274
+ publisher.defer(decodeSessionEventDeferred(encoded));
32275
+ return;
32276
+ }
32487
32277
  if (kind === SESSION_EVENT_REJECTED_KIND) {
32488
32278
  const rejection = decodeSessionEventRejection(encoded);
32489
32279
  if (publisher.quarantine(rejection)) {
@@ -32977,7 +32767,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
32977
32767
  });
32978
32768
  const sessionStore = new SqliteAcpSessionStore(config2.auditDatabasePath);
32979
32769
  const outbox = new SessionEventOutbox(config2.auditDatabasePath);
32980
- const publisher = new DaemonSessionEventPublisher(outbox);
32770
+ const publisher = new DaemonSessionEventPublisher(outbox, timer);
32981
32771
  const grants = new LocalBotGrantStore(config2.auditDatabasePath);
32982
32772
  const contexts = new BotExecutionContextRegistry();
32983
32773
  const applicator = new EmbeddedBotContextApplicator();
@@ -33079,7 +32869,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
33079
32869
  try {
33080
32870
  grants.close();
33081
32871
  } finally {
33082
- outbox.close();
32872
+ try {
32873
+ publisher.close();
32874
+ } finally {
32875
+ outbox.close();
32876
+ }
33083
32877
  }
33084
32878
  }
33085
32879
  }