@moxt-ai/mobius 0.0.142 → 0.0.143

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 (46) hide show
  1. package/dist/acp/agent-query-processes.js +2 -2
  2. package/dist/acp/agent-query-processes.js.map +1 -1
  3. package/dist/acp/agent-runtime-manager.js +4 -4
  4. package/dist/acp/agent-runtime-manager.js.map +1 -1
  5. package/dist/acp/agent-session.d.ts.map +1 -1
  6. package/dist/acp/agent-session.js +37 -34
  7. package/dist/acp/agent-session.js.map +1 -1
  8. package/dist/acp/runtime-operations.d.ts +2 -2
  9. package/dist/acp/runtime-operations.d.ts.map +1 -1
  10. package/dist/acp/runtime-operations.js +2 -2
  11. package/dist/acp/runtime-operations.js.map +1 -1
  12. package/dist/cli.js +339 -315
  13. package/dist/cli.js.map +4 -4
  14. package/dist/daemon-active-turn.d.ts +2 -0
  15. package/dist/daemon-active-turn.d.ts.map +1 -1
  16. package/dist/daemon-active-turn.js +80 -6
  17. package/dist/daemon-active-turn.js.map +1 -1
  18. package/dist/daemon-prompt-executor.d.ts.map +1 -1
  19. package/dist/daemon-prompt-executor.js +25 -41
  20. package/dist/daemon-prompt-executor.js.map +1 -1
  21. package/dist/daemon-turn-registry.d.ts +48 -0
  22. package/dist/daemon-turn-registry.d.ts.map +1 -1
  23. package/dist/index.js +338 -314
  24. package/dist/index.js.map +4 -4
  25. package/dist/native-sessions/live-history-page.d.ts.map +1 -1
  26. package/dist/native-sessions/live-history-page.js +40 -24
  27. package/dist/native-sessions/live-history-page.js.map +1 -1
  28. package/dist/relay-connection/coalescing-agent-event-publisher.d.ts +32 -0
  29. package/dist/relay-connection/coalescing-agent-event-publisher.d.ts.map +1 -0
  30. package/dist/relay-connection/coalescing-agent-event-publisher.js +116 -0
  31. package/dist/relay-connection/coalescing-agent-event-publisher.js.map +1 -0
  32. package/dist/runtime.js +338 -314
  33. package/dist/runtime.js.map +4 -4
  34. package/package.json +1 -1
  35. package/dist/relay-connection/coalescing-activity-publisher.d.ts +0 -10
  36. package/dist/relay-connection/coalescing-activity-publisher.d.ts.map +0 -1
  37. package/dist/relay-connection/coalescing-activity-publisher.js +0 -54
  38. package/dist/relay-connection/coalescing-activity-publisher.js.map +0 -1
  39. package/dist/relay-connection/coalescing-narration-publisher.d.ts +0 -31
  40. package/dist/relay-connection/coalescing-narration-publisher.d.ts.map +0 -1
  41. package/dist/relay-connection/coalescing-narration-publisher.js +0 -61
  42. package/dist/relay-connection/coalescing-narration-publisher.js.map +0 -1
  43. package/dist/relay-connection/coalescing-text-publisher.d.ts +0 -26
  44. package/dist/relay-connection/coalescing-text-publisher.d.ts.map +0 -1
  45. package/dist/relay-connection/coalescing-text-publisher.js +0 -57
  46. package/dist/relay-connection/coalescing-text-publisher.js.map +0 -1
package/dist/index.js CHANGED
@@ -20955,7 +20955,7 @@ var userHistoryMessage = external_exports.strictObject({
20955
20955
  text: external_exports.string().max(SESSION_HISTORY_CHARACTER_LIMIT),
20956
20956
  images: external_exports.array(historyImage).max(8).readonly().optional()
20957
20957
  }).readonly();
20958
- var assistantHistoryMessage = external_exports.strictObject({
20958
+ var assistantHistoryMessageSchema = external_exports.strictObject({
20959
20959
  activities: external_exports.array(nativeMessageActivitySchema).max(100).readonly().default([]),
20960
20960
  content: external_exports.array(displayContentSchema).max(100).readonly().default([]),
20961
20961
  role: external_exports.literal("assistant"),
@@ -20964,7 +20964,7 @@ var assistantHistoryMessage = external_exports.strictObject({
20964
20964
  text: external_exports.string().max(SESSION_HISTORY_CHARACTER_LIMIT),
20965
20965
  images: external_exports.array(historyImage).max(8).readonly().optional()
20966
20966
  }).readonly();
20967
- var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessage]);
20967
+ var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessageSchema]);
20968
20968
  var availableSessionHistoryObjectSchema = external_exports.strictObject({
20969
20969
  status: external_exports.literal("available"),
20970
20970
  messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
@@ -21035,6 +21035,7 @@ var activeTurnSnapshotSchema = external_exports.strictObject({
21035
21035
  truncated: external_exports.boolean(),
21036
21036
  epoch: identifierStringSchema,
21037
21037
  interaction: external_exports.discriminatedUnion("kind", [sessionPermissionRequestedEventSchema, sessionElicitationRequestedEventSchema]).nullable(),
21038
+ messages: external_exports.array(assistantHistoryMessageSchema).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
21038
21039
  sequence: external_exports.number().int().nonnegative(),
21039
21040
  state: external_exports.literal("running")
21040
21041
  }).readonly();
@@ -23729,6 +23730,48 @@ var AgentUpdateStream = class {
23729
23730
  };
23730
23731
  };
23731
23732
 
23733
+ // src/acp/runtime-operations.ts
23734
+ var RuntimeOperations = class {
23735
+ #shutdown = new AbortController();
23736
+ #pending = /* @__PURE__ */ new Set();
23737
+ #closing = null;
23738
+ run = async (options, operation) => {
23739
+ const signal = AbortSignal.any([options.signal, this.#shutdown.signal]);
23740
+ signal.throwIfAborted();
23741
+ const pending = Promise.resolve().then(() => operation({ ...options, signal }));
23742
+ this.#pending.add(pending);
23743
+ try {
23744
+ const result = await pending;
23745
+ signal.throwIfAborted();
23746
+ return result;
23747
+ } finally {
23748
+ this.#pending.delete(pending);
23749
+ }
23750
+ };
23751
+ close = (release) => {
23752
+ if (this.#closing !== null) {
23753
+ return this.#closing;
23754
+ }
23755
+ this.#shutdown.abort();
23756
+ this.#closing = (async () => {
23757
+ await Promise.allSettled([...this.#pending]);
23758
+ await release();
23759
+ })();
23760
+ return this.#closing;
23761
+ };
23762
+ };
23763
+ async function waitForSharedOperation(pending, signal) {
23764
+ signal.throwIfAborted();
23765
+ const cancelled = Promise.withResolvers();
23766
+ const abort = () => cancelled.reject(signal.reason);
23767
+ signal.addEventListener("abort", abort, { once: true });
23768
+ try {
23769
+ return await Promise.race([pending, cancelled.promise]);
23770
+ } finally {
23771
+ signal.removeEventListener("abort", abort);
23772
+ }
23773
+ }
23774
+
23732
23775
  // src/acp/session-config/session-config-protocol.ts
23733
23776
  import { methods as methods2 } from "@agentclientprotocol/sdk";
23734
23777
  var MAXIMUM_CONFIG_OPTIONS = 100;
@@ -30425,9 +30468,7 @@ var AgentSession = class _AgentSession {
30425
30468
  #configOptions = [];
30426
30469
  #prompted = false;
30427
30470
  #nativePrompt = null;
30428
- #replay = {
30429
- kind: "idle"
30430
- };
30471
+ #sessionLoad = { kind: "not-requested" };
30431
30472
  #restored = false;
30432
30473
  #sessionTitle = null;
30433
30474
  #sessionUpdatedAt = "";
@@ -30540,30 +30581,46 @@ var AgentSession = class _AgentSession {
30540
30581
  }
30541
30582
  };
30542
30583
  }
30543
- const history = new AcpHistoryReplay();
30544
- this.#replay = { kind: "capture", history };
30545
- try {
30546
- await this.race(
30547
- this.#connection.connection.agent.request(
30548
- methods3.agent.session.load,
30549
- {
30550
- additionalDirectories: [...this.#additionalDirectories],
30551
- cwd: this.#cwd,
30552
- mcpServers: [],
30553
- sessionId: this.#updates.sessionId()
30554
- },
30555
- { cancellationSignal: signal }
30556
- ),
30557
- "ACP agent exited while loading history",
30558
- signal
30559
- );
30560
- await this.#updates.drained();
30561
- signal.throwIfAborted();
30562
- return await embedLocalHistoryImages(await history.snapshot(), signal);
30563
- } finally {
30564
- this.#replay = { kind: "idle" };
30565
- }
30584
+ const loaded = await this.#loadSession(signal, "ACP agent exited while loading history");
30585
+ return await embedLocalHistoryImages(await loaded.history.snapshot(), signal);
30566
30586
  };
30587
+ async #loadSession(signal, exitMessage) {
30588
+ if (this.#sessionLoad.kind === "loaded") {
30589
+ return this.#sessionLoad;
30590
+ }
30591
+ if (this.#sessionLoad.kind === "capturing") {
30592
+ return await waitForSharedOperation(this.#sessionLoad.completion, signal);
30593
+ }
30594
+ const history = new AcpHistoryReplay();
30595
+ const completion = Promise.resolve().then(async () => {
30596
+ try {
30597
+ const response = await this.race(
30598
+ this.#connection.connection.agent.request(
30599
+ methods3.agent.session.load,
30600
+ {
30601
+ additionalDirectories: [...this.#additionalDirectories],
30602
+ cwd: this.#cwd,
30603
+ mcpServers: [],
30604
+ sessionId: this.#updates.sessionId()
30605
+ },
30606
+ { cancellationSignal: signal }
30607
+ ),
30608
+ exitMessage,
30609
+ signal
30610
+ );
30611
+ await this.#updates.drained();
30612
+ signal.throwIfAborted();
30613
+ const loaded = { history, kind: "loaded", response };
30614
+ this.#sessionLoad = loaded;
30615
+ return loaded;
30616
+ } catch (cause) {
30617
+ this.#sessionLoad = { kind: "not-requested" };
30618
+ throw cause;
30619
+ }
30620
+ });
30621
+ this.#sessionLoad = { completion, history, kind: "capturing" };
30622
+ return await completion;
30623
+ }
30567
30624
  prepareAgentContent = async (content) => {
30568
30625
  if (content.type === "image" && content.uri.length > 0) {
30569
30626
  const signal = AbortSignal.timeout(2e4);
@@ -30766,29 +30823,10 @@ var AgentSession = class _AgentSession {
30766
30823
  )
30767
30824
  );
30768
30825
  } else if (this.#capabilities.loadSession) {
30769
- this.#replay = { kind: "discard" };
30770
- try {
30771
- lifecycleResponse = await this.#metrics.measure(
30772
- "session-load",
30773
- async () => await this.race(
30774
- this.#connection.connection.agent.request(
30775
- methods3.agent.session.load,
30776
- {
30777
- additionalDirectories: [...this.#additionalDirectories],
30778
- cwd: this.#cwd,
30779
- mcpServers: [],
30780
- sessionId: this.#updates.sessionId()
30781
- },
30782
- { cancellationSignal: signal }
30783
- ),
30784
- "ACP agent exited while loading a session",
30785
- signal
30786
- )
30787
- );
30788
- } finally {
30789
- await this.#updates.drained();
30790
- this.#replay = { kind: "idle" };
30791
- }
30826
+ lifecycleResponse = await this.#metrics.measure(
30827
+ "session-load",
30828
+ async () => (await this.#loadSession(signal, "ACP agent exited while loading a session")).response
30829
+ );
30792
30830
  } else {
30793
30831
  throw new AcpExecutionError(
30794
30832
  "The ACP agent cannot restore its persisted session",
@@ -31173,10 +31211,10 @@ var AgentSession = class _AgentSession {
31173
31211
  #processUpdate = async (notification) => {
31174
31212
  this.assertNativeSession(notification.sessionId);
31175
31213
  const update = notification.update;
31176
- if (this.#replay.kind === "capture") {
31177
- await this.#replay.history.record(update);
31214
+ if (this.#sessionLoad.kind !== "not-requested") {
31215
+ await this.#sessionLoad.history.record(update);
31178
31216
  }
31179
- if (this.#replay.kind !== "idle" && (update.sessionUpdate === "user_message_chunk" || update.sessionUpdate === "agent_message_chunk" || update.sessionUpdate === "agent_thought_chunk" || update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update" || update.sessionUpdate === "plan" || update.sessionUpdate === "plan_update" || update.sessionUpdate === "plan_removed")) {
31217
+ if (this.#sessionLoad.kind === "capturing" && (update.sessionUpdate === "user_message_chunk" || update.sessionUpdate === "agent_message_chunk" || update.sessionUpdate === "agent_thought_chunk" || update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update" || update.sessionUpdate === "plan" || update.sessionUpdate === "plan_update" || update.sessionUpdate === "plan_removed")) {
31180
31218
  return;
31181
31219
  }
31182
31220
  if (update.sessionUpdate === "agent_message_chunk") {
@@ -31446,48 +31484,6 @@ var RetainedAgentProcesses = class {
31446
31484
  };
31447
31485
  };
31448
31486
 
31449
- // src/acp/runtime-operations.ts
31450
- var RuntimeOperations = class {
31451
- #shutdown = new AbortController();
31452
- #pending = /* @__PURE__ */ new Set();
31453
- #closing = null;
31454
- run = async (options, operation) => {
31455
- const signal = AbortSignal.any([options.signal, this.#shutdown.signal]);
31456
- signal.throwIfAborted();
31457
- const pending = Promise.resolve().then(() => operation({ ...options, signal }));
31458
- this.#pending.add(pending);
31459
- try {
31460
- const result = await pending;
31461
- signal.throwIfAborted();
31462
- return result;
31463
- } finally {
31464
- this.#pending.delete(pending);
31465
- }
31466
- };
31467
- close = (release) => {
31468
- if (this.#closing !== null) {
31469
- return this.#closing;
31470
- }
31471
- this.#shutdown.abort();
31472
- this.#closing = (async () => {
31473
- await Promise.allSettled([...this.#pending]);
31474
- await release();
31475
- })();
31476
- return this.#closing;
31477
- };
31478
- };
31479
- async function waitForPreparation(pending, signal) {
31480
- signal.throwIfAborted();
31481
- const cancelled = Promise.withResolvers();
31482
- const abort = () => cancelled.reject(signal.reason);
31483
- signal.addEventListener("abort", abort, { once: true });
31484
- try {
31485
- return await Promise.race([pending, cancelled.promise]);
31486
- } finally {
31487
- signal.removeEventListener("abort", abort);
31488
- }
31489
- }
31490
-
31491
31487
  // src/acp/agent-query-processes.ts
31492
31488
  var AgentQueryProcesses = class {
31493
31489
  #processesByQuery;
@@ -31534,7 +31530,7 @@ var AgentQueryProcesses = class {
31534
31530
  prepare = async (options) => {
31535
31531
  const pending = this.#processesByQuery.pending(options.sessionId);
31536
31532
  if (pending !== void 0) {
31537
- const runtime2 = await waitForPreparation(pending, options.signal);
31533
+ const runtime2 = await waitForSharedOperation(pending, options.signal);
31538
31534
  if (runtime2.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
31539
31535
  this.#processesByQuery.touch(options.sessionId, runtime2);
31540
31536
  return runtime2.prepared();
@@ -31952,7 +31948,7 @@ var AgentRuntimeManager = class {
31952
31948
  if (pending !== void 0) {
31953
31949
  const runtime2 = await metrics.measure(
31954
31950
  "session-preparation-wait",
31955
- async () => await waitForPreparation(pending, options2.signal)
31951
+ async () => await waitForSharedOperation(pending, options2.signal)
31956
31952
  );
31957
31953
  if (runtime2.belongsTo(options2.agent.id, options2.cwd, options2.additionalDirectories)) {
31958
31954
  this.#executionProcesses.touch(options2.sessionId, runtime2);
@@ -32042,7 +32038,7 @@ var AgentRuntimeManager = class {
32042
32038
  prepareExecutionSession = async (options) => this.#operations.run(options, async (options2) => {
32043
32039
  const pending = this.#executionProcesses.pending(options2.sessionId);
32044
32040
  if (pending !== void 0) {
32045
- await waitForPreparation(pending, options2.signal);
32041
+ await waitForSharedOperation(pending, options2.signal);
32046
32042
  return await this.prepareExecutionSession(options2);
32047
32043
  }
32048
32044
  const existing = this.#executionProcesses.get(options2.sessionId);
@@ -32096,7 +32092,7 @@ var AgentRuntimeManager = class {
32096
32092
  }
32097
32093
  const pending = this.#executionProcesses.pending(options2.sessionId);
32098
32094
  if (pending !== void 0) {
32099
- await waitForPreparation(pending, options2.signal);
32095
+ await waitForSharedOperation(pending, options2.signal);
32100
32096
  }
32101
32097
  if (this.#executions.command(options2.sessionId) !== options2.targetCommandId) {
32102
32098
  throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
@@ -38662,6 +38658,7 @@ var ActiveDaemonTurn = class {
38662
38658
  #text = "";
38663
38659
  #activities = /* @__PURE__ */ new Map();
38664
38660
  #content = /* @__PURE__ */ new Map();
38661
+ #timeline = [];
38665
38662
  #sequence = 0;
38666
38663
  #interaction = null;
38667
38664
  #epoch = decodeIdentifier(randomUUID2());
@@ -38705,12 +38702,16 @@ var ActiveDaemonTurn = class {
38705
38702
  recordEvent = (event) => {
38706
38703
  if (event.kind === AGENT_MESSAGE_DELTA_KIND) {
38707
38704
  this.#text += event.text;
38705
+ this.appendMessageText(event.messageId, event.text);
38708
38706
  return this.updated();
38709
38707
  }
38710
38708
  if (event.sessionId !== this.command.sessionId || "commandId" in event && event.commandId !== this.command.commandId) {
38711
38709
  return event;
38712
38710
  }
38713
38711
  if (event.kind === AGENT_ACTIVITY_KIND) {
38712
+ if (!this.#activities.has(event.activityId)) {
38713
+ this.#timeline = [...this.#timeline, { activityId: event.activityId, kind: "activity" }];
38714
+ }
38714
38715
  this.#activities.set(event.activityId, {
38715
38716
  activityId: event.activityId,
38716
38717
  activityType: event.activityType,
@@ -38726,6 +38727,9 @@ var ActiveDaemonTurn = class {
38726
38727
  });
38727
38728
  } else if (event.kind === AGENT_ACTIVITY_NARRATION_KIND) {
38728
38729
  const previous = this.#activities.get(event.activityId);
38730
+ if (previous === void 0) {
38731
+ this.#timeline = [...this.#timeline, { activityId: event.activityId, kind: "activity" }];
38732
+ }
38729
38733
  this.#activities.set(event.activityId, {
38730
38734
  activityId: event.activityId,
38731
38735
  activityType: event.activityType,
@@ -38741,6 +38745,24 @@ var ActiveDaemonTurn = class {
38741
38745
  });
38742
38746
  } else if (event.kind === AGENT_CONTENT_KIND) {
38743
38747
  this.#content.set(event.contentId, event.content);
38748
+ const target = this.#timeline.findLastIndex(
38749
+ (entry) => entry.kind === "message" && entry.messageId === event.messageId
38750
+ );
38751
+ if (target < 0) {
38752
+ this.#timeline = [
38753
+ ...this.#timeline,
38754
+ {
38755
+ content: /* @__PURE__ */ new Map([[event.contentId, event.content]]),
38756
+ kind: "message",
38757
+ messageId: event.messageId,
38758
+ text: ""
38759
+ }
38760
+ ];
38761
+ } else {
38762
+ this.#timeline = this.#timeline.map(
38763
+ (entry, index) => index === target && entry.kind === "message" ? { ...entry, content: new Map([...entry.content, [event.contentId, event.content]]) } : entry
38764
+ );
38765
+ }
38744
38766
  } else if (event.kind === SESSION_PERMISSION_REQUESTED_KIND || event.kind === SESSION_ELICITATION_REQUESTED_KIND) {
38745
38767
  this.#interaction = event;
38746
38768
  } else if ((event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) && this.#interaction?.interactionId === event.interactionId) {
@@ -38751,6 +38773,49 @@ var ActiveDaemonTurn = class {
38751
38773
  }
38752
38774
  return event;
38753
38775
  };
38776
+ appendMessageText(messageId, text2) {
38777
+ let remaining = text2;
38778
+ while (remaining.length > 0) {
38779
+ const latest = this.#timeline.at(-1);
38780
+ if (latest?.kind === "message" && latest.messageId === messageId) {
38781
+ const available = SESSION_HISTORY_CHARACTER_LIMIT - latest.text.length;
38782
+ if (available > 0) {
38783
+ this.#timeline = [
38784
+ ...this.#timeline.slice(0, -1),
38785
+ { ...latest, text: `${latest.text}${remaining.slice(0, available)}` }
38786
+ ];
38787
+ remaining = remaining.slice(available);
38788
+ continue;
38789
+ }
38790
+ }
38791
+ this.#timeline = [
38792
+ ...this.#timeline,
38793
+ {
38794
+ content: /* @__PURE__ */ new Map(),
38795
+ kind: "message",
38796
+ messageId,
38797
+ text: remaining.slice(0, SESSION_HISTORY_CHARACTER_LIMIT)
38798
+ }
38799
+ ];
38800
+ remaining = remaining.slice(SESSION_HISTORY_CHARACTER_LIMIT);
38801
+ }
38802
+ }
38803
+ timelineMessages() {
38804
+ return this.#timeline.flatMap((entry) => {
38805
+ if (entry.kind === "message") {
38806
+ return [
38807
+ {
38808
+ activities: [],
38809
+ content: [...entry.content.values()],
38810
+ role: "assistant",
38811
+ text: entry.text
38812
+ }
38813
+ ];
38814
+ }
38815
+ const activity2 = this.#activities.get(entry.activityId);
38816
+ return activity2 === void 0 ? [] : [{ activities: [activity2], content: [], role: "assistant", text: "" }];
38817
+ });
38818
+ }
38754
38819
  updated = () => {
38755
38820
  this.#sequence += 1;
38756
38821
  const turn = this.snapshot();
@@ -38759,18 +38824,26 @@ var ActiveDaemonTurn = class {
38759
38824
  }
38760
38825
  return { kind: "session.turn.updated", sessionId: this.command.sessionId, turn };
38761
38826
  };
38762
- snapshot = () => this.#dispatch.kind === "running" ? {
38763
- activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
38764
- commandId: decodeIdentifier(this.command.commandId),
38765
- content: [...this.#content.values()],
38766
- prompt: this.command.prompt,
38767
- text: this.#text,
38768
- truncated: false,
38769
- epoch: this.#epoch,
38770
- interaction: this.#interaction,
38771
- sequence: this.#sequence,
38772
- state: "running"
38773
- } : null;
38827
+ snapshot = () => {
38828
+ if (this.#dispatch.kind !== "running") {
38829
+ return null;
38830
+ }
38831
+ const completeMessages = this.timelineMessages();
38832
+ const messages = completeMessages.slice(-SESSION_HISTORY_MESSAGE_LIMIT);
38833
+ return {
38834
+ activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
38835
+ commandId: decodeIdentifier(this.command.commandId),
38836
+ content: [...this.#content.values()],
38837
+ prompt: this.command.prompt,
38838
+ text: this.#text,
38839
+ truncated: messages.length !== completeMessages.length,
38840
+ epoch: this.#epoch,
38841
+ interaction: this.#interaction,
38842
+ messages,
38843
+ sequence: this.#sequence,
38844
+ state: "running"
38845
+ };
38846
+ };
38774
38847
  matches = (cancellation) => cancellation.commandId === this.command.commandId && cancellation.sessionId === this.command.sessionId;
38775
38848
  matchesInteraction = (interactionId, sessionId) => this.command.sessionId === sessionId && this.#interaction?.interactionId === interactionId;
38776
38849
  requestCancellation = () => {
@@ -41080,6 +41153,34 @@ function liveHistoryCoveredTurnRanges(history) {
41080
41153
  function historyMessageCharacters(message) {
41081
41154
  return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
41082
41155
  }
41156
+ function boundedActiveMessages(messages, messageLimit, characterLimit) {
41157
+ const selected = [];
41158
+ let remainingCharacters = characterLimit;
41159
+ for (let index = messages.length - 1; index >= 0 && selected.length < messageLimit; index -= 1) {
41160
+ const message = messages[index];
41161
+ if (message === void 0) {
41162
+ break;
41163
+ }
41164
+ const characters = historyMessageCharacters(message);
41165
+ if (characters <= remainingCharacters) {
41166
+ selected.unshift(message);
41167
+ remainingCharacters -= characters;
41168
+ continue;
41169
+ }
41170
+ const structuredCharacters = characters - message.text.length;
41171
+ if (selected.length === 0 && structuredCharacters <= remainingCharacters) {
41172
+ const textCharacters = remainingCharacters - structuredCharacters;
41173
+ selected.unshift({ ...message, text: textCharacters === 0 ? "" : message.text.slice(-textCharacters) });
41174
+ remainingCharacters = 0;
41175
+ }
41176
+ break;
41177
+ }
41178
+ return {
41179
+ characters: characterLimit - remainingCharacters,
41180
+ messages: selected,
41181
+ truncated: selected.length !== messages.length
41182
+ };
41183
+ }
41083
41184
  function paginateAcpSessionHistory(history, sessionId, cursor, limit) {
41084
41185
  if (history.status === "unavailable") {
41085
41186
  if (cursor !== null) {
@@ -41110,30 +41211,19 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
41110
41211
  if (activeTurn === null || cursor !== null) {
41111
41212
  return paginateAcpSessionHistory(history, sessionId, cursor, limit);
41112
41213
  }
41113
- const activeMessageOverhead = 4;
41114
- const activeStructuredCharacters = JSON.stringify(activeTurn.activities).length + JSON.stringify(activeTurn.content).length;
41115
- const maximumActiveTextLength = Math.max(
41116
- 0,
41117
- SESSION_HISTORY_CHARACTER_LIMIT - activeTurn.prompt.length - activeStructuredCharacters - activeMessageOverhead
41118
- );
41119
- const activeText = activeTurn.text.slice(-maximumActiveTextLength);
41120
- const completeActiveMessages = [
41121
- { role: "user", text: activeTurn.prompt },
41122
- {
41123
- activities: activeTurn.activities,
41124
- content: activeTurn.content,
41125
- role: "assistant",
41126
- text: activeText
41127
- }
41128
- ];
41129
41214
  if (history.status === "unavailable") {
41215
+ const bounded3 = boundedActiveMessages(
41216
+ activeTurn.messages,
41217
+ Math.max(0, limit - 1),
41218
+ Math.max(0, SESSION_HISTORY_CHARACTER_LIMIT - activeTurn.prompt.length)
41219
+ );
41130
41220
  return {
41131
41221
  coveredCommandIds: [],
41132
41222
  coveredMessageIds: [],
41133
- messages: completeActiveMessages,
41223
+ messages: [{ role: "user", text: activeTurn.prompt }, ...bounded3.messages],
41134
41224
  nextCursor: null,
41135
41225
  status: "available",
41136
- truncated: activeTurn.truncated || activeText.length !== activeTurn.text.length
41226
+ truncated: activeTurn.truncated || bounded3.truncated
41137
41227
  };
41138
41228
  }
41139
41229
  const activeUserIndex = history.coveredCommandIds.includes(activeTurn.commandId) ? history.messages.findLastIndex((message) => message.role === "user") : -1;
@@ -41148,9 +41238,18 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
41148
41238
  };
41149
41239
  }
41150
41240
  const latestNativeMessage = history.messages.at(-1);
41151
- const activeMessages = activeUserIndex >= 0 || latestNativeMessage?.role === "user" && latestNativeMessage.text === activeTurn.prompt ? completeActiveMessages.slice(1) : completeActiveMessages;
41241
+ const includeActivePrompt = activeUserIndex < 0 && !(latestNativeMessage?.role === "user" && latestNativeMessage.text === activeTurn.prompt);
41242
+ const bounded2 = boundedActiveMessages(
41243
+ activeTurn.messages,
41244
+ Math.max(0, limit - (includeActivePrompt ? 1 : 0)),
41245
+ Math.max(0, SESSION_HISTORY_CHARACTER_LIMIT - (includeActivePrompt ? activeTurn.prompt.length : 0))
41246
+ );
41247
+ const activeMessages = [
41248
+ ...includeActivePrompt ? [{ role: "user", text: activeTurn.prompt }] : [],
41249
+ ...bounded2.messages
41250
+ ];
41152
41251
  let historicalMessageCount = 0;
41153
- let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - activeText.length - activeMessageOverhead - activeStructuredCharacters - (activeMessages.length === completeActiveMessages.length ? activeTurn.prompt.length : 0);
41252
+ let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - bounded2.characters - (includeActivePrompt ? activeTurn.prompt.length : 0);
41154
41253
  const historicalMessageLimit = Math.max(0, limit - activeMessages.length);
41155
41254
  for (let index = history.messages.length - 1; index >= 0 && historicalMessageCount < historicalMessageLimit; index -= 1) {
41156
41255
  const message = history.messages[index];
@@ -41175,7 +41274,7 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
41175
41274
  return {
41176
41275
  ...page,
41177
41276
  messages: [...page.messages, ...activeMessages],
41178
- truncated: page.truncated || activeTurn.truncated || activeText.length !== activeTurn.text.length
41277
+ truncated: page.truncated || activeTurn.truncated || bounded2.truncated
41179
41278
  };
41180
41279
  }
41181
41280
  function mergeLiveSessionHistory(history, activeTurn, sessionId) {
@@ -41485,167 +41584,113 @@ function warnIgnoredSetting(audit, event, commandId, sessionId, agentId, selecti
41485
41584
  `);
41486
41585
  }
41487
41586
 
41488
- // src/relay-connection/coalescing-activity-publisher.ts
41489
- var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
41490
- var CoalescingDaemonActivityPublisher = class {
41491
- #pending = /* @__PURE__ */ new Map();
41587
+ // src/relay-connection/coalescing-agent-event-publisher.ts
41588
+ var AGENT_EVENT_COALESCING_MILLISECONDS = 100;
41589
+ var AGENT_EVENT_TEXT_MAXIMUM_LENGTH = 1e5;
41590
+ var CoalescingAgentEventPublisher = class {
41591
+ #entries = [];
41492
41592
  #publish;
41493
41593
  #publishedActivityIds = /* @__PURE__ */ new Set();
41494
- #scheduled = /* @__PURE__ */ new Map();
41594
+ #scheduled = null;
41495
41595
  #timer;
41496
41596
  constructor(timer, publish) {
41497
41597
  this.#publish = publish;
41498
41598
  this.#timer = timer;
41499
41599
  }
41500
- flush = () => {
41501
- const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
41502
- for (const task of this.#scheduled.values()) {
41503
- task.cancel();
41504
- }
41505
- this.#pending.clear();
41506
- this.#scheduled.clear();
41507
- for (const activity2 of activities) {
41508
- this.#publish(activity2);
41509
- }
41510
- };
41511
- record = (activity2) => {
41512
- if (activity2.status !== "pending" && activity2.status !== "running") {
41513
- this.#discardPending(activity2.activityId);
41514
- this.#publishedActivityIds.add(activity2.activityId);
41515
- this.#publish(activity2);
41516
- return Promise.resolve();
41517
- }
41518
- if (!this.#publishedActivityIds.has(activity2.activityId)) {
41519
- this.#publishedActivityIds.add(activity2.activityId);
41520
- this.#publish(activity2);
41521
- return Promise.resolve();
41522
- }
41523
- this.#pending.set(activity2.activityId, activity2);
41524
- if (!this.#scheduled.has(activity2.activityId)) {
41525
- this.#scheduled.set(
41526
- activity2.activityId,
41527
- this.#timer.schedule(
41528
- () => this.#flushActivity(activity2.activityId),
41529
- AGENT_ACTIVITY_COALESCING_MILLISECONDS
41530
- )
41531
- );
41532
- }
41533
- return Promise.resolve();
41534
- };
41535
- #discardPending = (activityId) => {
41536
- this.#scheduled.get(activityId)?.cancel();
41537
- this.#scheduled.delete(activityId);
41538
- this.#pending.delete(activityId);
41539
- };
41540
- #flushActivity = (activityId) => {
41541
- const activity2 = this.#pending.get(activityId);
41542
- this.#discardPending(activityId);
41543
- if (activity2 !== void 0) {
41544
- this.#publish(activity2);
41545
- }
41546
- };
41547
- };
41548
-
41549
- // src/relay-connection/coalescing-narration-publisher.ts
41550
- var CoalescingNarrationPublisher = class {
41551
- #coalescingMilliseconds;
41552
- #maximumTextLength;
41553
- #publish;
41554
- #timer;
41555
- #pending = /* @__PURE__ */ new Map();
41556
- #scheduled = null;
41557
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
41558
- this.#publish = publish;
41559
- this.#timer = timer;
41560
- this.#coalescingMilliseconds = coalescingMilliseconds;
41561
- this.#maximumTextLength = maximumTextLength;
41562
- }
41563
- /** Emit every buffered increment now, cancelling the pending flush. */
41564
41600
  flush = () => {
41565
41601
  this.#scheduled?.cancel();
41566
41602
  this.#scheduled = null;
41567
- const increments = [...this.#pending.entries()].map(
41568
- ([activityId, pending]) => ({
41569
- activityId,
41570
- activityType: pending.activityType,
41571
- sequence: pending.sequence,
41572
- text: pending.text
41573
- })
41574
- );
41575
- this.#pending.clear();
41576
- for (const increment of increments) {
41577
- this.#publish(increment);
41603
+ const entries = this.#entries;
41604
+ this.#entries = [];
41605
+ for (const entry of entries) {
41606
+ this.#publish(entry);
41578
41607
  }
41579
41608
  };
41580
- /**
41581
- * Buffer an increment, emitting the buffered text when it would exceed the
41582
- * protocol's per-event text limit so no event is rejected by the codec.
41583
- */
41584
- record = (increment) => {
41585
- if (increment.text.length === 0) {
41586
- return;
41587
- }
41588
- const previous = this.#pending.get(increment.activityId);
41589
- if (previous !== void 0 && previous.text.length + increment.text.length > this.#maximumTextLength) {
41609
+ recordActivity = (activity2) => {
41610
+ const latest = this.#entries.at(-1);
41611
+ this.#entries = latest?.kind === "activity" && latest.activity.activityId === activity2.activityId ? [...this.#entries.slice(0, -1), { activity: activity2, kind: "activity" }] : [...this.#entries, { activity: activity2, kind: "activity" }];
41612
+ const first = !this.#publishedActivityIds.has(activity2.activityId);
41613
+ this.#publishedActivityIds.add(activity2.activityId);
41614
+ if (first || activity2.status !== "pending" && activity2.status !== "running") {
41590
41615
  this.flush();
41616
+ } else {
41617
+ this.schedule();
41591
41618
  }
41592
- const current = this.#pending.get(increment.activityId);
41593
- this.#pending.set(increment.activityId, {
41594
- activityType: increment.activityType,
41595
- sequence: increment.sequence,
41596
- text: `${current?.text ?? ""}${increment.text}`
41597
- });
41598
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
41619
+ return Promise.resolve();
41599
41620
  };
41600
- };
41601
-
41602
- // src/relay-connection/coalescing-text-publisher.ts
41603
- var CoalescingTextPublisher = class {
41604
- #coalescingMilliseconds;
41605
- #maximumTextLength;
41606
- #publish;
41607
- #timer;
41608
- #messageId = "";
41609
- #scheduled = null;
41610
- #text = "";
41611
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
41612
- this.#publish = publish;
41613
- this.#timer = timer;
41614
- this.#coalescingMilliseconds = coalescingMilliseconds;
41615
- this.#maximumTextLength = maximumTextLength;
41616
- }
41617
- /** Emit any buffered text now, cancelling the pending flush. */
41618
- flush = () => {
41619
- this.#scheduled?.cancel();
41620
- this.#scheduled = null;
41621
- const messageId = this.#messageId;
41622
- const text2 = this.#text;
41623
- this.#messageId = "";
41624
- this.#text = "";
41625
- if (text2.length > 0) {
41626
- this.#publish(messageId, text2);
41621
+ recordNarration = (increment) => {
41622
+ if (increment.text.length === 0) {
41623
+ return Promise.resolve();
41624
+ }
41625
+ let remaining = increment.text;
41626
+ while (remaining.length > 0) {
41627
+ const latest = this.#entries.at(-1);
41628
+ if (latest?.kind === "narration" && latest.increment.activityId === increment.activityId) {
41629
+ const available = AGENT_EVENT_TEXT_MAXIMUM_LENGTH - latest.increment.text.length;
41630
+ if (available > 0) {
41631
+ this.#entries = [
41632
+ ...this.#entries.slice(0, -1),
41633
+ {
41634
+ increment: {
41635
+ ...increment,
41636
+ text: `${latest.increment.text}${remaining.slice(0, available)}`
41637
+ },
41638
+ kind: "narration"
41639
+ }
41640
+ ];
41641
+ remaining = remaining.slice(available);
41642
+ continue;
41643
+ }
41644
+ }
41645
+ this.#entries = [
41646
+ ...this.#entries,
41647
+ {
41648
+ increment: { ...increment, text: remaining.slice(0, AGENT_EVENT_TEXT_MAXIMUM_LENGTH) },
41649
+ kind: "narration"
41650
+ }
41651
+ ];
41652
+ remaining = remaining.slice(AGENT_EVENT_TEXT_MAXIMUM_LENGTH);
41627
41653
  }
41654
+ this.schedule();
41655
+ return Promise.resolve();
41628
41656
  };
41629
- /**
41630
- * Buffer a chunk, emitting the buffered text when the message changes or the
41631
- * buffer would exceed the protocol's per-event text limit.
41632
- */
41633
- record = (messageId, text2) => {
41657
+ recordText = (text2, messageId) => {
41634
41658
  if (text2.length === 0) {
41635
- return;
41659
+ return Promise.resolve();
41636
41660
  }
41637
- if (this.#text.length > 0 && (this.#messageId !== messageId || this.#text.length + text2.length > this.#maximumTextLength)) {
41638
- this.flush();
41661
+ let remaining = text2;
41662
+ while (remaining.length > 0) {
41663
+ const latest = this.#entries.at(-1);
41664
+ if (latest?.kind === "text" && latest.messageId === messageId) {
41665
+ const available = AGENT_EVENT_TEXT_MAXIMUM_LENGTH - latest.text.length;
41666
+ if (available > 0) {
41667
+ this.#entries = [
41668
+ ...this.#entries.slice(0, -1),
41669
+ { ...latest, text: `${latest.text}${remaining.slice(0, available)}` }
41670
+ ];
41671
+ remaining = remaining.slice(available);
41672
+ continue;
41673
+ }
41674
+ }
41675
+ this.#entries = [
41676
+ ...this.#entries,
41677
+ {
41678
+ kind: "text",
41679
+ messageId,
41680
+ text: remaining.slice(0, AGENT_EVENT_TEXT_MAXIMUM_LENGTH)
41681
+ }
41682
+ ];
41683
+ remaining = remaining.slice(AGENT_EVENT_TEXT_MAXIMUM_LENGTH);
41639
41684
  }
41640
- this.#messageId = messageId;
41641
- this.#text = `${this.#text}${text2}`;
41642
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
41685
+ this.schedule();
41686
+ return Promise.resolve();
41643
41687
  };
41688
+ schedule() {
41689
+ this.#scheduled ??= this.#timer.schedule(this.flush, AGENT_EVENT_COALESCING_MILLISECONDS);
41690
+ }
41644
41691
  };
41645
41692
 
41646
41693
  // src/daemon-prompt-executor.ts
41647
- var AGENT_TEXT_COALESCING_MILLISECONDS = 100;
41648
- var AGENT_TEXT_MAXIMUM_LENGTH = 1e5;
41649
41694
  function daemonActivity(command, activity2) {
41650
41695
  return {
41651
41696
  activityId: activity2.activityId,
@@ -41681,41 +41726,30 @@ async function executePrompt({
41681
41726
  recordLiveEvent,
41682
41727
  cancellationRequested
41683
41728
  }) {
41684
- const narration = new CoalescingNarrationPublisher(
41685
- timer,
41686
- (increment) => {
41687
- const event = {
41688
- activityId: increment.activityId,
41689
- activityType: increment.activityType,
41729
+ const events = new CoalescingAgentEventPublisher(timer, (event) => {
41730
+ if (event.kind === "text") {
41731
+ publisher.publish(
41732
+ command.sessionId,
41733
+ recordLiveEvent({ kind: AGENT_MESSAGE_DELTA_KIND, messageId: event.messageId, text: event.text })
41734
+ );
41735
+ return;
41736
+ }
41737
+ if (event.kind === "narration") {
41738
+ const recorded = {
41739
+ activityId: event.increment.activityId,
41740
+ activityType: event.increment.activityType,
41690
41741
  commandId: command.commandId,
41691
41742
  kind: AGENT_ACTIVITY_NARRATION_KIND,
41692
- sequence: increment.sequence,
41743
+ sequence: event.increment.sequence,
41693
41744
  sessionId: command.sessionId,
41694
- text: increment.text
41745
+ text: event.increment.text
41695
41746
  };
41696
- publisher.publish(command.sessionId, recordLiveEvent(event));
41697
- },
41698
- AGENT_ACTIVITY_COALESCING_MILLISECONDS,
41699
- AGENT_TEXT_MAXIMUM_LENGTH
41700
- );
41701
- const activities = new CoalescingDaemonActivityPublisher(timer, (activity2) => {
41702
- if (activity2.status !== "pending" && activity2.status !== "running") {
41703
- narration.flush();
41747
+ publisher.publish(command.sessionId, recordLiveEvent(recorded));
41748
+ return;
41704
41749
  }
41705
- const event = daemonActivity(command, activity2);
41706
- publisher.publish(command.sessionId, recordLiveEvent(event));
41750
+ const activity2 = daemonActivity(command, event.activity);
41751
+ publisher.publish(command.sessionId, recordLiveEvent(activity2));
41707
41752
  });
41708
- const text2 = new CoalescingTextPublisher(
41709
- timer,
41710
- (messageId, delta) => {
41711
- publisher.publish(
41712
- command.sessionId,
41713
- recordLiveEvent({ kind: AGENT_MESSAGE_DELTA_KIND, messageId, text: delta })
41714
- );
41715
- },
41716
- AGENT_TEXT_COALESCING_MILLISECONDS,
41717
- AGENT_TEXT_MAXIMUM_LENGTH
41718
- );
41719
41753
  try {
41720
41754
  const resolved = await resolveSessionDirectories(
41721
41755
  directories,
@@ -41804,15 +41838,9 @@ async function executePrompt({
41804
41838
  cwd: resolved.cwd,
41805
41839
  disposition: availability.disposition,
41806
41840
  images: attachments.images,
41807
- onActivity: activities.record,
41808
- onNarration: (increment) => {
41809
- narration.record(increment);
41810
- return Promise.resolve();
41811
- },
41812
- onText: (delta, messageId) => {
41813
- text2.record(messageId, delta);
41814
- return Promise.resolve();
41815
- },
41841
+ onActivity: events.recordActivity,
41842
+ onNarration: events.recordNarration,
41843
+ onText: events.recordText,
41816
41844
  prompt: command.prompt,
41817
41845
  resources: attachments.resources,
41818
41846
  sessionId: command.sessionId,
@@ -41821,9 +41849,7 @@ async function executePrompt({
41821
41849
  });
41822
41850
  stopReason = result.stopReason;
41823
41851
  }
41824
- text2.flush();
41825
- narration.flush();
41826
- activities.flush();
41852
+ events.flush();
41827
41853
  publisher.publish(command.sessionId, {
41828
41854
  commandId: command.commandId,
41829
41855
  kind: SESSION_COMPLETED_KIND,
@@ -41837,9 +41863,7 @@ async function executePrompt({
41837
41863
  sessionId: command.sessionId
41838
41864
  });
41839
41865
  } catch (error62) {
41840
- text2.flush();
41841
- narration.flush();
41842
- activities.flush();
41866
+ events.flush();
41843
41867
  if (signal.aborted && cancellationRequested()) {
41844
41868
  publisher.publish(command.sessionId, {
41845
41869
  commandId: command.commandId,