@moxt-ai/mobius 0.0.147 → 0.0.149

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.
package/dist/cli.js CHANGED
@@ -3381,7 +3381,7 @@ var program = new Command();
3381
3381
  // package.json
3382
3382
  var package_default = {
3383
3383
  name: "@moxt-ai/mobius",
3384
- version: "0.0.147",
3384
+ version: "0.0.149",
3385
3385
  description: "Mobius CLI for pairing and running local AI agent services.",
3386
3386
  type: "module",
3387
3387
  bin: {
@@ -29624,18 +29624,21 @@ var activeTurnMessageSchema = external_exports.discriminatedUnion("role", [
29624
29624
  activeTurnAssistantMessageSchema
29625
29625
  ]);
29626
29626
  var activeTurnSnapshotSchema = external_exports.strictObject({
29627
- activities: external_exports.array(activeTurnActivitySchema).readonly(),
29628
29627
  commandId: identifierStringSchema,
29629
- content: external_exports.array(displayContentSchema).readonly(),
29630
- prompt: external_exports.string().max(1e5),
29631
- text: external_exports.string(),
29632
- truncated: external_exports.boolean(),
29633
29628
  epoch: identifierStringSchema,
29629
+ hasOlder: external_exports.boolean(),
29634
29630
  interaction: external_exports.discriminatedUnion("kind", [sessionPermissionRequestedEventSchema, sessionElicitationRequestedEventSchema]).nullable(),
29635
29631
  messages: external_exports.array(activeTurnMessageSchema).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
29636
29632
  sequence: external_exports.number().int().nonnegative(),
29637
- state: external_exports.literal("running")
29638
- }).readonly();
29633
+ state: external_exports.literal("running"),
29634
+ truncated: external_exports.boolean()
29635
+ }).readonly().refine(
29636
+ (snapshot) => snapshot.messages.reduce(
29637
+ (total, message) => total + message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0),
29638
+ 0
29639
+ ) <= SESSION_HISTORY_CHARACTER_LIMIT,
29640
+ "Active Turn snapshot content is too large"
29641
+ );
29639
29642
  var identity = {
29640
29643
  ...envelope,
29641
29644
  kind: external_exports.literal(NATIVE_SESSION_REQUEST_KIND),
@@ -29779,27 +29782,14 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
29779
29782
 
29780
29783
  // ../protocol/src/session-events.ts
29781
29784
  var SESSION_EVENT_KIND = "session.event";
29782
- var SESSION_EVENT_COMMITTED_KIND = "session.event.committed";
29783
29785
  var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
29784
29786
  var eventEnvelopeSchema = external_exports.strictObject({
29785
29787
  kind: external_exports.literal(SESSION_EVENT_KIND),
29786
29788
  payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
29787
29789
  sessionId: identifierStringSchema
29788
29790
  }).readonly();
29789
- var committedEventSchema = external_exports.strictObject({
29790
- kind: external_exports.literal(SESSION_EVENT_COMMITTED_KIND),
29791
- payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
29792
- sessionId: identifierStringSchema
29793
- }).readonly();
29794
29791
  var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
29795
29792
  var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
29796
- var SESSION_TURN_UPDATED_KIND = "session.turn.updated";
29797
- var sessionTurnUpdatedSchema = external_exports.strictObject({
29798
- kind: external_exports.literal(SESSION_TURN_UPDATED_KIND),
29799
- sessionId: identifierStringSchema,
29800
- turn: activeTurnSnapshotSchema
29801
- }).readonly();
29802
- var decodeSessionTurnUpdatedEvent = (encoded) => decode5(encoded, sessionTurnUpdatedSchema);
29803
29793
  function decode5(encoded, schema) {
29804
29794
  let value;
29805
29795
  try {
@@ -29829,8 +29819,6 @@ var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSche
29829
29819
  function decodeRecordedSessionEvent(encoded) {
29830
29820
  const kind = decodeMessageKind(encoded);
29831
29821
  switch (kind) {
29832
- case SESSION_TURN_UPDATED_KIND:
29833
- return decodeSessionTurnUpdatedEvent(encoded);
29834
29822
  case SESSION_QUEUE_CHANGED_KIND:
29835
29823
  return decode5(encoded, sessionQueueChangedSchema);
29836
29824
  case AGENT_ACTIVITY_KIND:
@@ -42207,6 +42195,28 @@ var DaemonInstallationObserver = class {
42207
42195
 
42208
42196
  // src/daemon-active-turn.ts
42209
42197
  import { randomUUID as randomUUID2 } from "node:crypto";
42198
+ function activeMessageCharacters(message) {
42199
+ return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
42200
+ }
42201
+ var ACTIVE_TURN_TEXT_CHUNK_LIMIT = Math.floor(SESSION_HISTORY_CHARACTER_LIMIT / 2);
42202
+ function boundedRecoveryMessages(messages) {
42203
+ const selected = [];
42204
+ let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT;
42205
+ for (let index = messages.length - 1; index >= 0 && selected.length < SESSION_HISTORY_MESSAGE_LIMIT; index -= 1) {
42206
+ const message = messages[index];
42207
+ if (message === void 0) {
42208
+ break;
42209
+ }
42210
+ const characters = activeMessageCharacters(message);
42211
+ if (characters <= remainingCharacters) {
42212
+ selected.unshift(message);
42213
+ remainingCharacters -= characters;
42214
+ continue;
42215
+ }
42216
+ break;
42217
+ }
42218
+ return { hasOlder: selected.length !== messages.length, messages: selected };
42219
+ }
42210
42220
  var ActiveDaemonTurn = class {
42211
42221
  attemptId;
42212
42222
  command;
@@ -42214,9 +42224,7 @@ var ActiveDaemonTurn = class {
42214
42224
  #finished = Promise.withResolvers();
42215
42225
  #cancellationRequested = false;
42216
42226
  #dispatch;
42217
- #text = "";
42218
42227
  #activities = /* @__PURE__ */ new Map();
42219
- #content = /* @__PURE__ */ new Map();
42220
42228
  #timeline;
42221
42229
  #sequence = 0;
42222
42230
  #interaction = null;
@@ -42263,9 +42271,17 @@ var ActiveDaemonTurn = class {
42263
42271
  effectiveDisposition = () => this.#dispatch.kind === "promoted" ? SessionPromptDisposition.Steer : this.command.disposition;
42264
42272
  recordEvent = (event) => {
42265
42273
  if (event.kind === AGENT_MESSAGE_DELTA_KIND) {
42266
- this.#text += event.text;
42274
+ const sequence = this.advance();
42267
42275
  this.appendMessageText(event.messageId, event.text);
42268
- return this.updated();
42276
+ return {
42277
+ commandId: this.command.commandId,
42278
+ epoch: this.#epoch,
42279
+ kind: AGENT_MESSAGE_DELTA_KIND,
42280
+ messageId: event.messageId,
42281
+ sequence,
42282
+ sessionId: this.command.sessionId,
42283
+ text: event.text
42284
+ };
42269
42285
  }
42270
42286
  if (event.sessionId !== this.command.sessionId || "commandId" in event && event.commandId !== this.command.commandId) {
42271
42287
  return event;
@@ -42312,7 +42328,6 @@ var ActiveDaemonTurn = class {
42312
42328
  toolKind: previous?.toolKind ?? ""
42313
42329
  });
42314
42330
  } else if (event.kind === AGENT_CONTENT_KIND) {
42315
- this.#content.set(event.contentId, event.content);
42316
42331
  const target = this.#timeline.findLastIndex(
42317
42332
  (entry) => entry.kind === "message" && entry.sourceMessageId === event.messageId
42318
42333
  );
@@ -42338,7 +42353,7 @@ var ActiveDaemonTurn = class {
42338
42353
  this.#interaction = null;
42339
42354
  }
42340
42355
  if (event.kind === AGENT_ACTIVITY_KIND || event.kind === AGENT_ACTIVITY_NARRATION_KIND || event.kind === AGENT_CONTENT_KIND) {
42341
- return this.updated();
42356
+ this.advance();
42342
42357
  }
42343
42358
  return event;
42344
42359
  };
@@ -42347,7 +42362,7 @@ var ActiveDaemonTurn = class {
42347
42362
  while (remaining.length > 0) {
42348
42363
  const latest = this.#timeline.at(-1);
42349
42364
  if (latest?.kind === "message" && latest.sourceMessageId === messageId) {
42350
- const available = SESSION_HISTORY_CHARACTER_LIMIT - latest.text.length;
42365
+ const available = ACTIVE_TURN_TEXT_CHUNK_LIMIT - latest.text.length;
42351
42366
  if (available > 0) {
42352
42367
  this.#timeline = [
42353
42368
  ...this.#timeline.slice(0, -1),
@@ -42364,21 +42379,22 @@ var ActiveDaemonTurn = class {
42364
42379
  id: decodeIdentifier(randomUUID2()),
42365
42380
  kind: "message",
42366
42381
  sourceMessageId: messageId,
42367
- text: remaining.slice(0, SESSION_HISTORY_CHARACTER_LIMIT)
42382
+ text: remaining.slice(0, ACTIVE_TURN_TEXT_CHUNK_LIMIT)
42368
42383
  }
42369
42384
  ];
42370
- remaining = remaining.slice(SESSION_HISTORY_CHARACTER_LIMIT);
42385
+ remaining = remaining.slice(ACTIVE_TURN_TEXT_CHUNK_LIMIT);
42371
42386
  }
42372
42387
  }
42373
42388
  recordSteeringPrompt = (command) => {
42374
42389
  if (this.#dispatch.kind !== "running" || command.sessionId !== this.command.sessionId || command.targetCommandId !== this.command.commandId) {
42375
- return null;
42390
+ return false;
42376
42391
  }
42377
42392
  this.#timeline = [
42378
42393
  ...this.#timeline,
42379
42394
  { id: decodeIdentifier(command.commandId), images: command.images, kind: "user", text: command.prompt }
42380
42395
  ];
42381
- return this.updated();
42396
+ this.advance();
42397
+ return true;
42382
42398
  };
42383
42399
  timelineMessages() {
42384
42400
  return this.#timeline.flatMap((entry) => {
@@ -42400,32 +42416,39 @@ var ActiveDaemonTurn = class {
42400
42416
  return activity2 === void 0 ? [] : [{ activities: [activity2], content: [], id: entry.id, role: "assistant", text: "" }];
42401
42417
  });
42402
42418
  }
42403
- updated = () => {
42404
- this.#sequence += 1;
42405
- const turn = this.snapshot();
42406
- if (turn === null) {
42407
- throw new Error("Only a running turn can publish a snapshot");
42419
+ advance = () => {
42420
+ if (this.#dispatch.kind !== "running") {
42421
+ throw new Error("Only a running turn can advance its snapshot");
42408
42422
  }
42409
- return { kind: "session.turn.updated", sessionId: this.command.sessionId, turn };
42423
+ this.#sequence += 1;
42424
+ return this.#sequence;
42410
42425
  };
42411
42426
  snapshot = () => {
42412
42427
  if (this.#dispatch.kind !== "running") {
42413
42428
  return null;
42414
42429
  }
42415
- const completeMessages = this.timelineMessages();
42416
- const messages = completeMessages.slice(-SESSION_HISTORY_MESSAGE_LIMIT);
42430
+ const bounded2 = boundedRecoveryMessages(this.timelineMessages());
42417
42431
  return {
42418
- activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
42419
42432
  commandId: decodeIdentifier(this.command.commandId),
42420
- content: [...this.#content.values()],
42421
- prompt: this.command.prompt,
42422
- text: this.#text,
42423
- truncated: messages.length !== completeMessages.length,
42424
42433
  epoch: this.#epoch,
42434
+ hasOlder: bounded2.hasOlder,
42425
42435
  interaction: this.#interaction,
42426
- messages,
42436
+ messages: bounded2.messages,
42427
42437
  sequence: this.#sequence,
42428
- state: "running"
42438
+ state: "running",
42439
+ truncated: false
42440
+ };
42441
+ };
42442
+ recovery = () => {
42443
+ if (this.#dispatch.kind !== "running") {
42444
+ return null;
42445
+ }
42446
+ return {
42447
+ commandId: decodeIdentifier(this.command.commandId),
42448
+ epoch: this.#epoch,
42449
+ messages: this.timelineMessages(),
42450
+ state: "running",
42451
+ truncated: false
42429
42452
  };
42430
42453
  };
42431
42454
  matches = (cancellation) => cancellation.commandId === this.command.commandId && cancellation.sessionId === this.command.sessionId;
@@ -42451,6 +42474,7 @@ var ActiveDaemonTurn = class {
42451
42474
  };
42452
42475
 
42453
42476
  // src/daemon-turn-registry.ts
42477
+ var COMPLETED_TURN_RECOVERY_LIMIT = 32;
42454
42478
  var waitForTurnCompletion = (turn, waitSignal) => {
42455
42479
  waitSignal.throwIfAborted();
42456
42480
  return new Promise((resolve9, reject) => {
@@ -42477,6 +42501,7 @@ var waitForTurnCompletion = (turn, waitSignal) => {
42477
42501
  };
42478
42502
  var DaemonTurnRegistry = class {
42479
42503
  #turns = /* @__PURE__ */ new Map();
42504
+ #completed = /* @__PURE__ */ new Map();
42480
42505
  #operations;
42481
42506
  constructor(operations) {
42482
42507
  this.#operations = operations;
@@ -42488,7 +42513,22 @@ var DaemonTurnRegistry = class {
42488
42513
  };
42489
42514
  get = (commandId) => this.#turns.get(commandId);
42490
42515
  finish = (turn) => {
42516
+ const recovery = turn.recovery();
42491
42517
  this.#turns.delete(turn.command.commandId);
42518
+ if (recovery !== null) {
42519
+ this.#completed.delete(turn.command.sessionId);
42520
+ this.#completed.set(turn.command.sessionId, {
42521
+ command: turn.command,
42522
+ recovery: { ...recovery, state: "completed" }
42523
+ });
42524
+ while (this.#completed.size > COMPLETED_TURN_RECOVERY_LIMIT) {
42525
+ const oldestSessionId = this.#completed.keys().next().value;
42526
+ if (oldestSessionId === void 0) {
42527
+ break;
42528
+ }
42529
+ this.#completed.delete(oldestSessionId);
42530
+ }
42531
+ }
42492
42532
  turn.finish();
42493
42533
  };
42494
42534
  recordEvent = (event) => {
@@ -42509,7 +42549,7 @@ var DaemonTurnRegistry = class {
42509
42549
  };
42510
42550
  recordSteeringPrompt = (command) => {
42511
42551
  const target = this.#turns.get(command.targetCommandId);
42512
- return target?.recordSteeringPrompt(command) ?? null;
42552
+ return target?.recordSteeringPrompt(command) ?? false;
42513
42553
  };
42514
42554
  executionSnapshot = async (matches) => {
42515
42555
  const matching = [];
@@ -42540,12 +42580,30 @@ var DaemonTurnRegistry = class {
42540
42580
  }
42541
42581
  return null;
42542
42582
  };
42583
+ recovery = (matches) => {
42584
+ for (const turn of this.#turns.values()) {
42585
+ if (!matches(turn.command)) {
42586
+ continue;
42587
+ }
42588
+ const recovery = turn.recovery();
42589
+ if (recovery !== null) {
42590
+ return recovery;
42591
+ }
42592
+ }
42593
+ for (const completed of this.#completed.values()) {
42594
+ if (matches(completed.command)) {
42595
+ return completed.recovery;
42596
+ }
42597
+ }
42598
+ return null;
42599
+ };
42543
42600
  close = async () => {
42544
42601
  const turns = [...this.#turns.values()];
42545
42602
  for (const turn of turns) {
42546
42603
  turn.abortDaemon();
42547
42604
  }
42548
42605
  await Promise.all(turns.map((turn) => turn.completion));
42606
+ this.#completed.clear();
42549
42607
  };
42550
42608
  admitAndWait = async (turn, waitSignal, onQueued) => {
42551
42609
  const command = turn.command;
@@ -44853,10 +44911,10 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
44853
44911
  }
44854
44912
 
44855
44913
  // src/native-sessions/live-history-page.ts
44856
- function liveHistoryCursor(sessionId, endIndex) {
44857
- return Buffer.from(JSON.stringify({ endIndex, sessionId }), "utf8").toString("base64url");
44914
+ function encodeLiveHistoryCursor(cursor) {
44915
+ return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
44858
44916
  }
44859
- function liveHistoryEndIndex(cursor, sessionId) {
44917
+ function decodeLiveHistoryCursor(cursor, sessionId) {
44860
44918
  try {
44861
44919
  if (!/^[A-Za-z0-9_-]{1,512}$/u.test(cursor)) {
44862
44920
  throw new Error("Invalid cursor alphabet");
@@ -44866,14 +44924,32 @@ function liveHistoryEndIndex(cursor, sessionId) {
44866
44924
  throw new Error("Invalid cursor shape");
44867
44925
  }
44868
44926
  const record2 = Object.fromEntries(Object.entries(value));
44869
- if (Object.keys(record2).sort().join(",") !== "endIndex,sessionId" || record2["sessionId"] !== sessionId || typeof record2["endIndex"] !== "number" || !Number.isSafeInteger(record2["endIndex"]) || record2["endIndex"] < 0) {
44927
+ const keys = Object.keys(record2).sort().join(",");
44928
+ const legacyNative = keys === "endIndex,sessionId";
44929
+ const kind = legacyNative ? "native" : record2["kind"];
44930
+ if (keys !== "endIndex,kind,sessionId" && keys !== "endIndex,epoch,kind,sessionId" && !legacyNative || record2["sessionId"] !== sessionId || kind !== "active" && kind !== "native" || typeof record2["endIndex"] !== "number" || !Number.isSafeInteger(record2["endIndex"]) || record2["endIndex"] < 0) {
44870
44931
  throw new Error("Invalid cursor target");
44871
44932
  }
44872
- return record2["endIndex"];
44933
+ if (kind === "active") {
44934
+ if (keys !== "endIndex,epoch,kind,sessionId" || typeof record2["epoch"] !== "string") {
44935
+ throw new Error("Invalid active cursor target");
44936
+ }
44937
+ return { endIndex: record2["endIndex"], epoch: record2["epoch"], kind, sessionId };
44938
+ }
44939
+ if (keys !== "endIndex,kind,sessionId" && !legacyNative) {
44940
+ throw new Error("Invalid native cursor target");
44941
+ }
44942
+ return { endIndex: record2["endIndex"], kind, sessionId };
44873
44943
  } catch (cause) {
44874
44944
  throw new AcpExecutionError("The native Session history cursor is invalid", { cause }, "invalid_cursor");
44875
44945
  }
44876
44946
  }
44947
+ function activeHistoryCursor(sessionId, epoch, endIndex) {
44948
+ return encodeLiveHistoryCursor({ endIndex, epoch, kind: "active", sessionId });
44949
+ }
44950
+ function nativeHistoryCursor(sessionId, endIndex) {
44951
+ return encodeLiveHistoryCursor({ endIndex, kind: "native", sessionId });
44952
+ }
44877
44953
  function liveHistoryCoveredTurnRanges(history) {
44878
44954
  const ranges = [];
44879
44955
  let historyIndex = history.messages.length;
@@ -44894,139 +44970,180 @@ function liveHistoryCoveredTurnRanges(history) {
44894
44970
  function historyMessageCharacters(message) {
44895
44971
  return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
44896
44972
  }
44897
- function boundedActiveMessages(messages, messageLimit, characterLimit) {
44973
+ function isNativeHistoryImageMimeType(mimeType) {
44974
+ return mimeType === "image/png" || mimeType === "image/jpeg" || mimeType === "image/gif" || mimeType === "image/webp";
44975
+ }
44976
+ function selectMessagesBefore(messages, endIndex, messageLimit, characterLimit) {
44898
44977
  const selected = [];
44899
44978
  let remainingCharacters = characterLimit;
44900
- for (let index = messages.length - 1; index >= 0 && selected.length < messageLimit; index -= 1) {
44979
+ let startIndex = endIndex;
44980
+ for (let index = endIndex - 1; index >= 0 && selected.length < messageLimit; index -= 1) {
44901
44981
  const message = messages[index];
44902
44982
  if (message === void 0) {
44903
44983
  break;
44904
44984
  }
44905
44985
  const characters = historyMessageCharacters(message);
44906
- if (characters <= remainingCharacters) {
44907
- selected.unshift(message);
44908
- remainingCharacters -= characters;
44909
- continue;
44910
- }
44911
- const structuredCharacters = characters - message.text.length;
44912
- if (selected.length === 0 && structuredCharacters <= remainingCharacters) {
44913
- const textCharacters = remainingCharacters - structuredCharacters;
44914
- selected.unshift({ ...message, text: textCharacters === 0 ? "" : message.text.slice(-textCharacters) });
44915
- remainingCharacters = 0;
44986
+ if (characters > remainingCharacters) {
44987
+ break;
44916
44988
  }
44917
- break;
44989
+ selected.unshift(message);
44990
+ remainingCharacters -= characters;
44991
+ startIndex = index;
44918
44992
  }
44919
- return {
44920
- characters: characterLimit - remainingCharacters,
44921
- messages: selected,
44922
- truncated: selected.length !== messages.length
44923
- };
44993
+ if (selected.length === 0 && endIndex > 0) {
44994
+ throw new AcpExecutionError(
44995
+ "A native Session history item exceeds the page budget",
44996
+ {},
44997
+ "history_item_too_large"
44998
+ );
44999
+ }
45000
+ return { characters: characterLimit - remainingCharacters, messages: selected, startIndex };
44924
45001
  }
44925
45002
  function nativeActiveMessage(message) {
44926
- return message.role === "user" ? { role: "user", text: message.text } : {
44927
- activities: message.activities,
44928
- content: message.content,
44929
- role: "assistant",
44930
- text: message.text
44931
- };
45003
+ if (message.role === "assistant") {
45004
+ return {
45005
+ activities: message.activities,
45006
+ content: message.content,
45007
+ role: "assistant",
45008
+ text: message.text
45009
+ };
45010
+ }
45011
+ const images = (message.images ?? []).flatMap((image) => {
45012
+ if (image.data.length < 4 || image.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(image.data) || !isNativeHistoryImageMimeType(image.mimeType)) {
45013
+ return [];
45014
+ }
45015
+ return [{ data: image.data, mimeType: image.mimeType, name: image.name }];
45016
+ });
45017
+ return { ...images.length === 0 ? {} : { images }, role: "user", text: message.text };
45018
+ }
45019
+ function nativeHistoryCoversCompletedRecovery(history, recovery) {
45020
+ const root = recovery.messages.find((message) => message.role === "user" && message.id === recovery.commandId);
45021
+ if (root === void 0) {
45022
+ return false;
45023
+ }
45024
+ const rootIndex = history.messages.findLastIndex(
45025
+ (message) => message.role === "user" && message.text === root.text
45026
+ );
45027
+ if (rootIndex < 0) {
45028
+ return false;
45029
+ }
45030
+ const nativeAssistant = history.messages.slice(rootIndex + 1).filter((message) => message.role === "assistant");
45031
+ if (nativeAssistant.length === 0) {
45032
+ return false;
45033
+ }
45034
+ const recoveryAssistant = recovery.messages.filter((message) => message.role === "assistant");
45035
+ const recoveryText = recoveryAssistant.map((message) => message.text).join("");
45036
+ const nativeText = nativeAssistant.map((message) => message.text).join("");
45037
+ if (recoveryText.length > 0 && !nativeText.includes(recoveryText)) {
45038
+ return false;
45039
+ }
45040
+ const nativeContent = new Set(
45041
+ nativeAssistant.flatMap((message) => message.content).map((content) => JSON.stringify(content))
45042
+ );
45043
+ return recoveryAssistant.flatMap((message) => message.content).every((content) => nativeContent.has(JSON.stringify(content)));
44932
45044
  }
44933
- function paginateAcpSessionHistory(history, sessionId, cursor, limit) {
45045
+ function historyBeforeActiveTurn(history, activeTurn) {
44934
45046
  if (history.status === "unavailable") {
44935
- if (cursor !== null) {
44936
- throw new AcpExecutionError("Unavailable native Session history has no cursor", {}, "invalid_cursor");
44937
- }
44938
- return { cause: history.cause, nextCursor: null, status: "unavailable" };
45047
+ return history;
44939
45048
  }
44940
- const endIndex = cursor === null ? history.messages.length : liveHistoryEndIndex(cursor, sessionId);
45049
+ const activeRoot = activeTurn.messages.find(
45050
+ (message) => message.role === "user" && message.id === activeTurn.commandId
45051
+ );
45052
+ const matchingActiveUserIndex = activeRoot === void 0 ? -1 : activeTurn.state === "completed" || history.messages.at(-1)?.role === "user" && history.messages.at(-1)?.text === activeRoot.text || history.coveredCommandIds.includes(activeTurn.commandId) ? history.messages.findLastIndex((message) => message.role === "user" && message.text === activeRoot.text) : -1;
45053
+ const activeUserIndex = matchingActiveUserIndex >= 0 ? matchingActiveUserIndex : liveHistoryCoveredTurnRanges(history).find((range) => range.commandId === activeTurn.commandId)?.userIndex ?? -1;
45054
+ if (activeUserIndex < 0) {
45055
+ return history;
45056
+ }
45057
+ return {
45058
+ ...history,
45059
+ coveredCommandIds: history.coveredCommandIds.filter((id) => id !== activeTurn.commandId),
45060
+ coveredMessageIds: history.coveredMessageIds.filter(
45061
+ (id) => id !== `${activeTurn.commandId}:user` && id !== `${activeTurn.commandId}:agent`
45062
+ ),
45063
+ messages: history.messages.slice(0, activeUserIndex)
45064
+ };
45065
+ }
45066
+ function paginateAvailableNativeHistory(history, sessionId, endIndex, limit, characterLimit) {
44941
45067
  if (endIndex > history.messages.length) {
44942
45068
  throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
44943
45069
  }
44944
- const startIndex = Math.max(0, endIndex - limit);
45070
+ const selected = selectMessagesBefore(history.messages, endIndex, limit, characterLimit);
44945
45071
  const coveredMessageIds = new Set(history.coveredMessageIds);
44946
45072
  const ranges = liveHistoryCoveredTurnRanges(history);
44947
45073
  return {
44948
- coveredCommandIds: ranges.filter((range) => range.userIndex >= startIndex && range.assistantEndIndex <= endIndex).map((range) => range.commandId),
45074
+ coveredCommandIds: ranges.filter((range) => range.userIndex >= selected.startIndex && range.assistantEndIndex <= endIndex).map((range) => range.commandId),
44949
45075
  coveredMessageIds: ranges.flatMap((range) => [
44950
- ...range.userIndex >= startIndex && range.userIndex < endIndex && coveredMessageIds.has(`${range.commandId}:user`) ? [`${range.commandId}:user`] : [],
44951
- ...range.assistantStartIndex >= startIndex && range.assistantEndIndex <= endIndex && coveredMessageIds.has(`${range.commandId}:agent`) ? [`${range.commandId}:agent`] : []
45076
+ ...range.userIndex >= selected.startIndex && range.userIndex < endIndex && coveredMessageIds.has(`${range.commandId}:user`) ? [`${range.commandId}:user`] : [],
45077
+ ...range.assistantStartIndex >= selected.startIndex && range.assistantEndIndex <= endIndex && coveredMessageIds.has(`${range.commandId}:agent`) ? [`${range.commandId}:agent`] : []
44952
45078
  ]),
44953
- messages: history.messages.slice(startIndex, endIndex),
44954
- nextCursor: startIndex > 0 ? liveHistoryCursor(sessionId, startIndex) : null,
45079
+ messages: selected.messages,
45080
+ nextCursor: selected.startIndex > 0 ? nativeHistoryCursor(sessionId, selected.startIndex) : null,
44955
45081
  status: "available",
44956
45082
  truncated: history.truncated
44957
45083
  };
44958
45084
  }
44959
- function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limit) {
44960
- if (activeTurn === null || cursor !== null) {
44961
- return paginateAcpSessionHistory(history, sessionId, cursor, limit);
45085
+ function paginateNativeHistory(history, sessionId, cursor, limit) {
45086
+ if (cursor?.kind === "active") {
45087
+ throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
44962
45088
  }
44963
45089
  if (history.status === "unavailable") {
44964
- const bounded3 = boundedActiveMessages(
44965
- activeTurn.messages.map(nativeActiveMessage),
44966
- limit,
44967
- SESSION_HISTORY_CHARACTER_LIMIT
44968
- );
44969
- return {
44970
- coveredCommandIds: [],
44971
- coveredMessageIds: [],
44972
- messages: bounded3.messages,
44973
- nextCursor: null,
44974
- status: "available",
44975
- truncated: activeTurn.truncated || bounded3.truncated
44976
- };
45090
+ if (cursor !== null) {
45091
+ throw new AcpExecutionError("Unavailable native Session history has no cursor", {}, "invalid_cursor");
45092
+ }
45093
+ return { cause: history.cause, nextCursor: null, status: "unavailable" };
44977
45094
  }
44978
- const activeRoot = activeTurn.messages.find(
44979
- (message) => message.role === "user" && message.id === activeTurn.commandId
45095
+ return paginateAvailableNativeHistory(
45096
+ history,
45097
+ sessionId,
45098
+ cursor?.endIndex ?? history.messages.length,
45099
+ limit,
45100
+ SESSION_HISTORY_CHARACTER_LIMIT
44980
45101
  );
44981
- const coveredActiveRoot = history.coveredCommandIds.includes(activeTurn.commandId);
44982
- const matchingActiveUserIndex = coveredActiveRoot ? history.messages.findLastIndex(
44983
- (message) => message.role === "user" && message.text === (activeRoot?.text ?? activeTurn.prompt)
44984
- ) : -1;
44985
- const activeUserIndex = matchingActiveUserIndex >= 0 ? matchingActiveUserIndex : liveHistoryCoveredTurnRanges(history).find((range) => range.commandId === activeTurn.commandId)?.userIndex ?? -1;
44986
- if (activeUserIndex >= 0) {
44987
- history = {
44988
- ...history,
44989
- coveredCommandIds: history.coveredCommandIds.filter((id) => id !== activeTurn.commandId),
44990
- coveredMessageIds: history.coveredMessageIds.filter(
44991
- (id) => id !== `${activeTurn.commandId}:user` && id !== `${activeTurn.commandId}:agent`
44992
- ),
44993
- messages: history.messages.slice(0, activeUserIndex + 1)
44994
- };
44995
- }
44996
- const latestNativeMessage = history.messages.at(-1);
44997
- const firstActiveMessage = activeTurn.messages[0];
44998
- const preserveNativeRoot = activeUserIndex >= 0 || firstActiveMessage?.role === "user" && latestNativeMessage?.role === "user" && latestNativeMessage.text === firstActiveMessage.text;
44999
- const activeMessages = activeTurn.messages.slice(preserveNativeRoot ? 1 : 0).map(nativeActiveMessage);
45000
- const bounded2 = boundedActiveMessages(activeMessages, limit, SESSION_HISTORY_CHARACTER_LIMIT);
45001
- let historicalMessageCount = 0;
45002
- let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - bounded2.characters;
45003
- const historicalMessageLimit = Math.max(0, limit - bounded2.messages.length);
45004
- for (let index = history.messages.length - 1; index >= 0 && historicalMessageCount < historicalMessageLimit; index -= 1) {
45005
- const message = history.messages[index];
45006
- if (message === void 0) {
45007
- break;
45008
- }
45009
- const characters = historyMessageCharacters(message);
45010
- if (characters > remainingCharacters) {
45011
- break;
45012
- }
45013
- historicalMessageCount += 1;
45014
- remainingCharacters -= characters;
45102
+ }
45103
+ function paginateActiveTurn(history, activeTurn, sessionId, endIndex, limit) {
45104
+ if (endIndex > activeTurn.messages.length) {
45105
+ throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
45015
45106
  }
45016
- const page = historicalMessageCount === 0 ? {
45017
- coveredCommandIds: [],
45018
- coveredMessageIds: [],
45019
- messages: [],
45020
- nextCursor: history.messages.length === 0 ? null : liveHistoryCursor(sessionId, history.messages.length),
45021
- status: "available",
45022
- truncated: history.truncated
45023
- } : paginateAcpSessionHistory(history, sessionId, null, historicalMessageCount);
45107
+ const activeMessages = activeTurn.messages.map(nativeActiveMessage);
45108
+ const active = selectMessagesBefore(activeMessages, endIndex, limit, SESSION_HISTORY_CHARACTER_LIMIT);
45109
+ const historicalLimit = limit - active.messages.length;
45110
+ const historicalCharacterLimit = SESSION_HISTORY_CHARACTER_LIMIT - active.characters;
45111
+ const pendingHistoricalCursor = history.status === "available" && history.messages.length > 0 ? nativeHistoryCursor(sessionId, history.messages.length) : null;
45112
+ const latestHistoricalMessage = history.status === "available" ? history.messages.at(-1) : void 0;
45113
+ const historicalPageFits = latestHistoricalMessage === void 0 || historyMessageCharacters(latestHistoricalMessage) <= historicalCharacterLimit;
45114
+ const historical = active.startIndex === 0 && history.status === "available" && historicalLimit > 0 && historicalPageFits ? paginateAvailableNativeHistory(
45115
+ history,
45116
+ sessionId,
45117
+ history.messages.length,
45118
+ historicalLimit,
45119
+ historicalCharacterLimit
45120
+ ) : null;
45024
45121
  return {
45025
- ...page,
45026
- messages: [...page.messages, ...bounded2.messages],
45027
- truncated: page.truncated || activeTurn.truncated || bounded2.truncated
45122
+ coveredCommandIds: historical?.coveredCommandIds ?? [],
45123
+ coveredMessageIds: [
45124
+ ...historical?.coveredMessageIds ?? [],
45125
+ ...active.startIndex === 0 ? [`${activeTurn.commandId}:user`, `${activeTurn.commandId}:agent`] : []
45126
+ ],
45127
+ messages: [...historical?.messages ?? [], ...active.messages],
45128
+ nextCursor: active.startIndex > 0 ? activeHistoryCursor(sessionId, activeTurn.epoch, active.startIndex) : historical === null ? pendingHistoricalCursor : historical.nextCursor,
45129
+ status: "available",
45130
+ truncated: activeTurn.truncated || (historical?.truncated ?? false)
45028
45131
  };
45029
45132
  }
45133
+ function paginateLiveSessionHistory(history, activeTurn, sessionId, encodedCursor, limit) {
45134
+ const cursor = encodedCursor === null ? null : decodeLiveHistoryCursor(encodedCursor, sessionId);
45135
+ if (activeTurn === null || activeTurn.state === "completed" && history.status === "available" && nativeHistoryCoversCompletedRecovery(history, activeTurn)) {
45136
+ return paginateNativeHistory(history, sessionId, cursor, limit);
45137
+ }
45138
+ const historical = historyBeforeActiveTurn(history, activeTurn);
45139
+ if (cursor?.kind === "native") {
45140
+ return paginateNativeHistory(historical, sessionId, cursor, limit);
45141
+ }
45142
+ if (cursor !== null && cursor.epoch !== activeTurn.epoch) {
45143
+ throw new AcpExecutionError("The active Turn history cursor is stale", {}, "stale_cursor");
45144
+ }
45145
+ return paginateActiveTurn(historical, activeTurn, sessionId, cursor?.endIndex ?? activeTurn.messages.length, limit);
45146
+ }
45030
45147
  function mergeLiveSessionHistory(history, activeTurn, sessionId) {
45031
45148
  const page = paginateLiveSessionHistory(history, activeTurn, sessionId, null, 1e3);
45032
45149
  if (page.status === "unavailable") {
@@ -45037,7 +45154,7 @@ function mergeLiveSessionHistory(history, activeTurn, sessionId) {
45037
45154
  coveredMessageIds: page.coveredMessageIds,
45038
45155
  messages: page.messages,
45039
45156
  status: "available",
45040
- truncated: page.truncated
45157
+ truncated: page.truncated || page.nextCursor !== null
45041
45158
  };
45042
45159
  }
45043
45160
 
@@ -45556,15 +45673,13 @@ async function executePrompt({
45556
45673
  });
45557
45674
  operations.transitionExecution(command.commandId, "running");
45558
45675
  if (availability.disposition === SessionPromptDisposition.Steer) {
45559
- const updated = recordSteeringPrompt();
45560
- if (updated === null) {
45676
+ if (!recordSteeringPrompt()) {
45561
45677
  throw new AcpExecutionError(
45562
45678
  "The steering target is unavailable",
45563
45679
  {},
45564
45680
  "prompt_queue_target_unavailable"
45565
45681
  );
45566
45682
  }
45567
- publisher.publish(command.sessionId, updated);
45568
45683
  }
45569
45684
  const stream = activateAttempt();
45570
45685
  publisher.publish(command.sessionId, {
@@ -45952,7 +46067,7 @@ function createDaemonCommandHandler({
45952
46067
  resolveEnvironment: resolveAgentCatalogEnvironment
45953
46068
  });
45954
46069
  const queueChanged = (sessionId) => publisher.publish(sessionId, { kind: "session.queue.changed", sessionId });
45955
- const activeTurnSnapshot = (sessionId) => activeTurns.snapshot((command) => command.sessionId === sessionId);
46070
+ const activeTurnRecovery = (sessionId) => activeTurns.recovery((command) => command.sessionId === sessionId);
45956
46071
  const nativeRequestHandler = createNativeSessionRequestHandler({
45957
46072
  audit,
45958
46073
  diagnostics,
@@ -45973,7 +46088,7 @@ function createDaemonCommandHandler({
45973
46088
  directories,
45974
46089
  audit,
45975
46090
  sessions,
45976
- activeTurnSnapshot,
46091
+ activeTurnRecovery,
45977
46092
  signal
45978
46093
  );
45979
46094
  return;
@@ -45987,7 +46102,7 @@ function createDaemonCommandHandler({
45987
46102
  directories,
45988
46103
  audit,
45989
46104
  sessions,
45990
- activeTurnSnapshot,
46105
+ activeTurnRecovery,
45991
46106
  signal
45992
46107
  );
45993
46108
  return;