@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/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.142",
3384
+ version: "0.0.143",
3385
3385
  description: "Mobius CLI for pairing and running local AI agent services.",
3386
3386
  type: "module",
3387
3387
  bin: {
@@ -29441,7 +29441,7 @@ var userHistoryMessage = external_exports.strictObject({
29441
29441
  text: external_exports.string().max(SESSION_HISTORY_CHARACTER_LIMIT),
29442
29442
  images: external_exports.array(historyImage).max(8).readonly().optional()
29443
29443
  }).readonly();
29444
- var assistantHistoryMessage = external_exports.strictObject({
29444
+ var assistantHistoryMessageSchema = external_exports.strictObject({
29445
29445
  activities: external_exports.array(nativeMessageActivitySchema).max(100).readonly().default([]),
29446
29446
  content: external_exports.array(displayContentSchema).max(100).readonly().default([]),
29447
29447
  role: external_exports.literal("assistant"),
@@ -29450,7 +29450,7 @@ var assistantHistoryMessage = external_exports.strictObject({
29450
29450
  text: external_exports.string().max(SESSION_HISTORY_CHARACTER_LIMIT),
29451
29451
  images: external_exports.array(historyImage).max(8).readonly().optional()
29452
29452
  }).readonly();
29453
- var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessage]);
29453
+ var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessageSchema]);
29454
29454
  var availableSessionHistoryObjectSchema = external_exports.strictObject({
29455
29455
  status: external_exports.literal("available"),
29456
29456
  messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
@@ -29521,6 +29521,7 @@ var activeTurnSnapshotSchema = external_exports.strictObject({
29521
29521
  truncated: external_exports.boolean(),
29522
29522
  epoch: identifierStringSchema,
29523
29523
  interaction: external_exports.discriminatedUnion("kind", [sessionPermissionRequestedEventSchema, sessionElicitationRequestedEventSchema]).nullable(),
29524
+ messages: external_exports.array(assistantHistoryMessageSchema).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
29524
29525
  sequence: external_exports.number().int().nonnegative(),
29525
29526
  state: external_exports.literal("running")
29526
29527
  }).readonly();
@@ -32240,6 +32241,48 @@ var AgentUpdateStream = class {
32240
32241
  };
32241
32242
  };
32242
32243
 
32244
+ // src/acp/runtime-operations.ts
32245
+ var RuntimeOperations = class {
32246
+ #shutdown = new AbortController();
32247
+ #pending = /* @__PURE__ */ new Set();
32248
+ #closing = null;
32249
+ run = async (options, operation) => {
32250
+ const signal = AbortSignal.any([options.signal, this.#shutdown.signal]);
32251
+ signal.throwIfAborted();
32252
+ const pending = Promise.resolve().then(() => operation({ ...options, signal }));
32253
+ this.#pending.add(pending);
32254
+ try {
32255
+ const result = await pending;
32256
+ signal.throwIfAborted();
32257
+ return result;
32258
+ } finally {
32259
+ this.#pending.delete(pending);
32260
+ }
32261
+ };
32262
+ close = (release) => {
32263
+ if (this.#closing !== null) {
32264
+ return this.#closing;
32265
+ }
32266
+ this.#shutdown.abort();
32267
+ this.#closing = (async () => {
32268
+ await Promise.allSettled([...this.#pending]);
32269
+ await release();
32270
+ })();
32271
+ return this.#closing;
32272
+ };
32273
+ };
32274
+ async function waitForSharedOperation(pending, signal) {
32275
+ signal.throwIfAborted();
32276
+ const cancelled = Promise.withResolvers();
32277
+ const abort = () => cancelled.reject(signal.reason);
32278
+ signal.addEventListener("abort", abort, { once: true });
32279
+ try {
32280
+ return await Promise.race([pending, cancelled.promise]);
32281
+ } finally {
32282
+ signal.removeEventListener("abort", abort);
32283
+ }
32284
+ }
32285
+
32243
32286
  // src/acp/session-config/session-config-protocol.ts
32244
32287
  import { methods as methods7 } from "@agentclientprotocol/sdk";
32245
32288
  var MAXIMUM_CONFIG_OPTIONS = 100;
@@ -38879,9 +38922,7 @@ var AgentSession = class _AgentSession {
38879
38922
  #configOptions = [];
38880
38923
  #prompted = false;
38881
38924
  #nativePrompt = null;
38882
- #replay = {
38883
- kind: "idle"
38884
- };
38925
+ #sessionLoad = { kind: "not-requested" };
38885
38926
  #restored = false;
38886
38927
  #sessionTitle = null;
38887
38928
  #sessionUpdatedAt = "";
@@ -38994,30 +39035,46 @@ var AgentSession = class _AgentSession {
38994
39035
  }
38995
39036
  };
38996
39037
  }
38997
- const history = new AcpHistoryReplay();
38998
- this.#replay = { kind: "capture", history };
38999
- try {
39000
- await this.race(
39001
- this.#connection.connection.agent.request(
39002
- methods8.agent.session.load,
39003
- {
39004
- additionalDirectories: [...this.#additionalDirectories],
39005
- cwd: this.#cwd,
39006
- mcpServers: [],
39007
- sessionId: this.#updates.sessionId()
39008
- },
39009
- { cancellationSignal: signal }
39010
- ),
39011
- "ACP agent exited while loading history",
39012
- signal
39013
- );
39014
- await this.#updates.drained();
39015
- signal.throwIfAborted();
39016
- return await embedLocalHistoryImages(await history.snapshot(), signal);
39017
- } finally {
39018
- this.#replay = { kind: "idle" };
39019
- }
39038
+ const loaded = await this.#loadSession(signal, "ACP agent exited while loading history");
39039
+ return await embedLocalHistoryImages(await loaded.history.snapshot(), signal);
39020
39040
  };
39041
+ async #loadSession(signal, exitMessage) {
39042
+ if (this.#sessionLoad.kind === "loaded") {
39043
+ return this.#sessionLoad;
39044
+ }
39045
+ if (this.#sessionLoad.kind === "capturing") {
39046
+ return await waitForSharedOperation(this.#sessionLoad.completion, signal);
39047
+ }
39048
+ const history = new AcpHistoryReplay();
39049
+ const completion = Promise.resolve().then(async () => {
39050
+ try {
39051
+ const response = await this.race(
39052
+ this.#connection.connection.agent.request(
39053
+ methods8.agent.session.load,
39054
+ {
39055
+ additionalDirectories: [...this.#additionalDirectories],
39056
+ cwd: this.#cwd,
39057
+ mcpServers: [],
39058
+ sessionId: this.#updates.sessionId()
39059
+ },
39060
+ { cancellationSignal: signal }
39061
+ ),
39062
+ exitMessage,
39063
+ signal
39064
+ );
39065
+ await this.#updates.drained();
39066
+ signal.throwIfAborted();
39067
+ const loaded = { history, kind: "loaded", response };
39068
+ this.#sessionLoad = loaded;
39069
+ return loaded;
39070
+ } catch (cause) {
39071
+ this.#sessionLoad = { kind: "not-requested" };
39072
+ throw cause;
39073
+ }
39074
+ });
39075
+ this.#sessionLoad = { completion, history, kind: "capturing" };
39076
+ return await completion;
39077
+ }
39021
39078
  prepareAgentContent = async (content) => {
39022
39079
  if (content.type === "image" && content.uri.length > 0) {
39023
39080
  const signal = AbortSignal.timeout(2e4);
@@ -39220,29 +39277,10 @@ var AgentSession = class _AgentSession {
39220
39277
  )
39221
39278
  );
39222
39279
  } else if (this.#capabilities.loadSession) {
39223
- this.#replay = { kind: "discard" };
39224
- try {
39225
- lifecycleResponse = await this.#metrics.measure(
39226
- "session-load",
39227
- async () => await this.race(
39228
- this.#connection.connection.agent.request(
39229
- methods8.agent.session.load,
39230
- {
39231
- additionalDirectories: [...this.#additionalDirectories],
39232
- cwd: this.#cwd,
39233
- mcpServers: [],
39234
- sessionId: this.#updates.sessionId()
39235
- },
39236
- { cancellationSignal: signal }
39237
- ),
39238
- "ACP agent exited while loading a session",
39239
- signal
39240
- )
39241
- );
39242
- } finally {
39243
- await this.#updates.drained();
39244
- this.#replay = { kind: "idle" };
39245
- }
39280
+ lifecycleResponse = await this.#metrics.measure(
39281
+ "session-load",
39282
+ async () => (await this.#loadSession(signal, "ACP agent exited while loading a session")).response
39283
+ );
39246
39284
  } else {
39247
39285
  throw new AcpExecutionError(
39248
39286
  "The ACP agent cannot restore its persisted session",
@@ -39627,10 +39665,10 @@ var AgentSession = class _AgentSession {
39627
39665
  #processUpdate = async (notification) => {
39628
39666
  this.assertNativeSession(notification.sessionId);
39629
39667
  const update = notification.update;
39630
- if (this.#replay.kind === "capture") {
39631
- await this.#replay.history.record(update);
39668
+ if (this.#sessionLoad.kind !== "not-requested") {
39669
+ await this.#sessionLoad.history.record(update);
39632
39670
  }
39633
- 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")) {
39671
+ 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")) {
39634
39672
  return;
39635
39673
  }
39636
39674
  if (update.sessionUpdate === "agent_message_chunk") {
@@ -39900,48 +39938,6 @@ var RetainedAgentProcesses = class {
39900
39938
  };
39901
39939
  };
39902
39940
 
39903
- // src/acp/runtime-operations.ts
39904
- var RuntimeOperations = class {
39905
- #shutdown = new AbortController();
39906
- #pending = /* @__PURE__ */ new Set();
39907
- #closing = null;
39908
- run = async (options, operation) => {
39909
- const signal = AbortSignal.any([options.signal, this.#shutdown.signal]);
39910
- signal.throwIfAborted();
39911
- const pending = Promise.resolve().then(() => operation({ ...options, signal }));
39912
- this.#pending.add(pending);
39913
- try {
39914
- const result = await pending;
39915
- signal.throwIfAborted();
39916
- return result;
39917
- } finally {
39918
- this.#pending.delete(pending);
39919
- }
39920
- };
39921
- close = (release) => {
39922
- if (this.#closing !== null) {
39923
- return this.#closing;
39924
- }
39925
- this.#shutdown.abort();
39926
- this.#closing = (async () => {
39927
- await Promise.allSettled([...this.#pending]);
39928
- await release();
39929
- })();
39930
- return this.#closing;
39931
- };
39932
- };
39933
- async function waitForPreparation(pending, signal) {
39934
- signal.throwIfAborted();
39935
- const cancelled = Promise.withResolvers();
39936
- const abort = () => cancelled.reject(signal.reason);
39937
- signal.addEventListener("abort", abort, { once: true });
39938
- try {
39939
- return await Promise.race([pending, cancelled.promise]);
39940
- } finally {
39941
- signal.removeEventListener("abort", abort);
39942
- }
39943
- }
39944
-
39945
39941
  // src/acp/agent-query-processes.ts
39946
39942
  var AgentQueryProcesses = class {
39947
39943
  #processesByQuery;
@@ -39988,7 +39984,7 @@ var AgentQueryProcesses = class {
39988
39984
  prepare = async (options) => {
39989
39985
  const pending = this.#processesByQuery.pending(options.sessionId);
39990
39986
  if (pending !== void 0) {
39991
- const runtime2 = await waitForPreparation(pending, options.signal);
39987
+ const runtime2 = await waitForSharedOperation(pending, options.signal);
39992
39988
  if (runtime2.belongsTo(options.agent.id, options.cwd, options.additionalDirectories)) {
39993
39989
  this.#processesByQuery.touch(options.sessionId, runtime2);
39994
39990
  return runtime2.prepared();
@@ -40406,7 +40402,7 @@ var AgentRuntimeManager = class {
40406
40402
  if (pending !== void 0) {
40407
40403
  const runtime2 = await metrics.measure(
40408
40404
  "session-preparation-wait",
40409
- async () => await waitForPreparation(pending, options2.signal)
40405
+ async () => await waitForSharedOperation(pending, options2.signal)
40410
40406
  );
40411
40407
  if (runtime2.belongsTo(options2.agent.id, options2.cwd, options2.additionalDirectories)) {
40412
40408
  this.#executionProcesses.touch(options2.sessionId, runtime2);
@@ -40496,7 +40492,7 @@ var AgentRuntimeManager = class {
40496
40492
  prepareExecutionSession = async (options) => this.#operations.run(options, async (options2) => {
40497
40493
  const pending = this.#executionProcesses.pending(options2.sessionId);
40498
40494
  if (pending !== void 0) {
40499
- await waitForPreparation(pending, options2.signal);
40495
+ await waitForSharedOperation(pending, options2.signal);
40500
40496
  return await this.prepareExecutionSession(options2);
40501
40497
  }
40502
40498
  const existing = this.#executionProcesses.get(options2.sessionId);
@@ -40550,7 +40546,7 @@ var AgentRuntimeManager = class {
40550
40546
  }
40551
40547
  const pending = this.#executionProcesses.pending(options2.sessionId);
40552
40548
  if (pending !== void 0) {
40553
- await waitForPreparation(pending, options2.signal);
40549
+ await waitForSharedOperation(pending, options2.signal);
40554
40550
  }
40555
40551
  if (this.#executions.command(options2.sessionId) !== options2.targetCommandId) {
40556
40552
  throw new AcpExecutionError("The target turn is no longer active", {}, "steering_target_unavailable");
@@ -42103,6 +42099,7 @@ var ActiveDaemonTurn = class {
42103
42099
  #text = "";
42104
42100
  #activities = /* @__PURE__ */ new Map();
42105
42101
  #content = /* @__PURE__ */ new Map();
42102
+ #timeline = [];
42106
42103
  #sequence = 0;
42107
42104
  #interaction = null;
42108
42105
  #epoch = decodeIdentifier(randomUUID2());
@@ -42146,12 +42143,16 @@ var ActiveDaemonTurn = class {
42146
42143
  recordEvent = (event) => {
42147
42144
  if (event.kind === AGENT_MESSAGE_DELTA_KIND) {
42148
42145
  this.#text += event.text;
42146
+ this.appendMessageText(event.messageId, event.text);
42149
42147
  return this.updated();
42150
42148
  }
42151
42149
  if (event.sessionId !== this.command.sessionId || "commandId" in event && event.commandId !== this.command.commandId) {
42152
42150
  return event;
42153
42151
  }
42154
42152
  if (event.kind === AGENT_ACTIVITY_KIND) {
42153
+ if (!this.#activities.has(event.activityId)) {
42154
+ this.#timeline = [...this.#timeline, { activityId: event.activityId, kind: "activity" }];
42155
+ }
42155
42156
  this.#activities.set(event.activityId, {
42156
42157
  activityId: event.activityId,
42157
42158
  activityType: event.activityType,
@@ -42167,6 +42168,9 @@ var ActiveDaemonTurn = class {
42167
42168
  });
42168
42169
  } else if (event.kind === AGENT_ACTIVITY_NARRATION_KIND) {
42169
42170
  const previous = this.#activities.get(event.activityId);
42171
+ if (previous === void 0) {
42172
+ this.#timeline = [...this.#timeline, { activityId: event.activityId, kind: "activity" }];
42173
+ }
42170
42174
  this.#activities.set(event.activityId, {
42171
42175
  activityId: event.activityId,
42172
42176
  activityType: event.activityType,
@@ -42182,6 +42186,24 @@ var ActiveDaemonTurn = class {
42182
42186
  });
42183
42187
  } else if (event.kind === AGENT_CONTENT_KIND) {
42184
42188
  this.#content.set(event.contentId, event.content);
42189
+ const target = this.#timeline.findLastIndex(
42190
+ (entry) => entry.kind === "message" && entry.messageId === event.messageId
42191
+ );
42192
+ if (target < 0) {
42193
+ this.#timeline = [
42194
+ ...this.#timeline,
42195
+ {
42196
+ content: /* @__PURE__ */ new Map([[event.contentId, event.content]]),
42197
+ kind: "message",
42198
+ messageId: event.messageId,
42199
+ text: ""
42200
+ }
42201
+ ];
42202
+ } else {
42203
+ this.#timeline = this.#timeline.map(
42204
+ (entry, index) => index === target && entry.kind === "message" ? { ...entry, content: new Map([...entry.content, [event.contentId, event.content]]) } : entry
42205
+ );
42206
+ }
42185
42207
  } else if (event.kind === SESSION_PERMISSION_REQUESTED_KIND || event.kind === SESSION_ELICITATION_REQUESTED_KIND) {
42186
42208
  this.#interaction = event;
42187
42209
  } else if ((event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) && this.#interaction?.interactionId === event.interactionId) {
@@ -42192,6 +42214,49 @@ var ActiveDaemonTurn = class {
42192
42214
  }
42193
42215
  return event;
42194
42216
  };
42217
+ appendMessageText(messageId, text2) {
42218
+ let remaining = text2;
42219
+ while (remaining.length > 0) {
42220
+ const latest = this.#timeline.at(-1);
42221
+ if (latest?.kind === "message" && latest.messageId === messageId) {
42222
+ const available = SESSION_HISTORY_CHARACTER_LIMIT - latest.text.length;
42223
+ if (available > 0) {
42224
+ this.#timeline = [
42225
+ ...this.#timeline.slice(0, -1),
42226
+ { ...latest, text: `${latest.text}${remaining.slice(0, available)}` }
42227
+ ];
42228
+ remaining = remaining.slice(available);
42229
+ continue;
42230
+ }
42231
+ }
42232
+ this.#timeline = [
42233
+ ...this.#timeline,
42234
+ {
42235
+ content: /* @__PURE__ */ new Map(),
42236
+ kind: "message",
42237
+ messageId,
42238
+ text: remaining.slice(0, SESSION_HISTORY_CHARACTER_LIMIT)
42239
+ }
42240
+ ];
42241
+ remaining = remaining.slice(SESSION_HISTORY_CHARACTER_LIMIT);
42242
+ }
42243
+ }
42244
+ timelineMessages() {
42245
+ return this.#timeline.flatMap((entry) => {
42246
+ if (entry.kind === "message") {
42247
+ return [
42248
+ {
42249
+ activities: [],
42250
+ content: [...entry.content.values()],
42251
+ role: "assistant",
42252
+ text: entry.text
42253
+ }
42254
+ ];
42255
+ }
42256
+ const activity2 = this.#activities.get(entry.activityId);
42257
+ return activity2 === void 0 ? [] : [{ activities: [activity2], content: [], role: "assistant", text: "" }];
42258
+ });
42259
+ }
42195
42260
  updated = () => {
42196
42261
  this.#sequence += 1;
42197
42262
  const turn = this.snapshot();
@@ -42200,18 +42265,26 @@ var ActiveDaemonTurn = class {
42200
42265
  }
42201
42266
  return { kind: "session.turn.updated", sessionId: this.command.sessionId, turn };
42202
42267
  };
42203
- snapshot = () => this.#dispatch.kind === "running" ? {
42204
- activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
42205
- commandId: decodeIdentifier(this.command.commandId),
42206
- content: [...this.#content.values()],
42207
- prompt: this.command.prompt,
42208
- text: this.#text,
42209
- truncated: false,
42210
- epoch: this.#epoch,
42211
- interaction: this.#interaction,
42212
- sequence: this.#sequence,
42213
- state: "running"
42214
- } : null;
42268
+ snapshot = () => {
42269
+ if (this.#dispatch.kind !== "running") {
42270
+ return null;
42271
+ }
42272
+ const completeMessages = this.timelineMessages();
42273
+ const messages = completeMessages.slice(-SESSION_HISTORY_MESSAGE_LIMIT);
42274
+ return {
42275
+ activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
42276
+ commandId: decodeIdentifier(this.command.commandId),
42277
+ content: [...this.#content.values()],
42278
+ prompt: this.command.prompt,
42279
+ text: this.#text,
42280
+ truncated: messages.length !== completeMessages.length,
42281
+ epoch: this.#epoch,
42282
+ interaction: this.#interaction,
42283
+ messages,
42284
+ sequence: this.#sequence,
42285
+ state: "running"
42286
+ };
42287
+ };
42215
42288
  matches = (cancellation) => cancellation.commandId === this.command.commandId && cancellation.sessionId === this.command.sessionId;
42216
42289
  matchesInteraction = (interactionId, sessionId) => this.command.sessionId === sessionId && this.#interaction?.interactionId === interactionId;
42217
42290
  requestCancellation = () => {
@@ -44521,6 +44594,34 @@ function liveHistoryCoveredTurnRanges(history) {
44521
44594
  function historyMessageCharacters(message) {
44522
44595
  return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
44523
44596
  }
44597
+ function boundedActiveMessages(messages, messageLimit, characterLimit) {
44598
+ const selected = [];
44599
+ let remainingCharacters = characterLimit;
44600
+ for (let index = messages.length - 1; index >= 0 && selected.length < messageLimit; index -= 1) {
44601
+ const message = messages[index];
44602
+ if (message === void 0) {
44603
+ break;
44604
+ }
44605
+ const characters = historyMessageCharacters(message);
44606
+ if (characters <= remainingCharacters) {
44607
+ selected.unshift(message);
44608
+ remainingCharacters -= characters;
44609
+ continue;
44610
+ }
44611
+ const structuredCharacters = characters - message.text.length;
44612
+ if (selected.length === 0 && structuredCharacters <= remainingCharacters) {
44613
+ const textCharacters = remainingCharacters - structuredCharacters;
44614
+ selected.unshift({ ...message, text: textCharacters === 0 ? "" : message.text.slice(-textCharacters) });
44615
+ remainingCharacters = 0;
44616
+ }
44617
+ break;
44618
+ }
44619
+ return {
44620
+ characters: characterLimit - remainingCharacters,
44621
+ messages: selected,
44622
+ truncated: selected.length !== messages.length
44623
+ };
44624
+ }
44524
44625
  function paginateAcpSessionHistory(history, sessionId, cursor, limit) {
44525
44626
  if (history.status === "unavailable") {
44526
44627
  if (cursor !== null) {
@@ -44551,30 +44652,19 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44551
44652
  if (activeTurn === null || cursor !== null) {
44552
44653
  return paginateAcpSessionHistory(history, sessionId, cursor, limit);
44553
44654
  }
44554
- const activeMessageOverhead = 4;
44555
- const activeStructuredCharacters = JSON.stringify(activeTurn.activities).length + JSON.stringify(activeTurn.content).length;
44556
- const maximumActiveTextLength = Math.max(
44557
- 0,
44558
- SESSION_HISTORY_CHARACTER_LIMIT - activeTurn.prompt.length - activeStructuredCharacters - activeMessageOverhead
44559
- );
44560
- const activeText = activeTurn.text.slice(-maximumActiveTextLength);
44561
- const completeActiveMessages = [
44562
- { role: "user", text: activeTurn.prompt },
44563
- {
44564
- activities: activeTurn.activities,
44565
- content: activeTurn.content,
44566
- role: "assistant",
44567
- text: activeText
44568
- }
44569
- ];
44570
44655
  if (history.status === "unavailable") {
44656
+ const bounded3 = boundedActiveMessages(
44657
+ activeTurn.messages,
44658
+ Math.max(0, limit - 1),
44659
+ Math.max(0, SESSION_HISTORY_CHARACTER_LIMIT - activeTurn.prompt.length)
44660
+ );
44571
44661
  return {
44572
44662
  coveredCommandIds: [],
44573
44663
  coveredMessageIds: [],
44574
- messages: completeActiveMessages,
44664
+ messages: [{ role: "user", text: activeTurn.prompt }, ...bounded3.messages],
44575
44665
  nextCursor: null,
44576
44666
  status: "available",
44577
- truncated: activeTurn.truncated || activeText.length !== activeTurn.text.length
44667
+ truncated: activeTurn.truncated || bounded3.truncated
44578
44668
  };
44579
44669
  }
44580
44670
  const activeUserIndex = history.coveredCommandIds.includes(activeTurn.commandId) ? history.messages.findLastIndex((message) => message.role === "user") : -1;
@@ -44589,9 +44679,18 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44589
44679
  };
44590
44680
  }
44591
44681
  const latestNativeMessage = history.messages.at(-1);
44592
- const activeMessages = activeUserIndex >= 0 || latestNativeMessage?.role === "user" && latestNativeMessage.text === activeTurn.prompt ? completeActiveMessages.slice(1) : completeActiveMessages;
44682
+ const includeActivePrompt = activeUserIndex < 0 && !(latestNativeMessage?.role === "user" && latestNativeMessage.text === activeTurn.prompt);
44683
+ const bounded2 = boundedActiveMessages(
44684
+ activeTurn.messages,
44685
+ Math.max(0, limit - (includeActivePrompt ? 1 : 0)),
44686
+ Math.max(0, SESSION_HISTORY_CHARACTER_LIMIT - (includeActivePrompt ? activeTurn.prompt.length : 0))
44687
+ );
44688
+ const activeMessages = [
44689
+ ...includeActivePrompt ? [{ role: "user", text: activeTurn.prompt }] : [],
44690
+ ...bounded2.messages
44691
+ ];
44593
44692
  let historicalMessageCount = 0;
44594
- let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - activeText.length - activeMessageOverhead - activeStructuredCharacters - (activeMessages.length === completeActiveMessages.length ? activeTurn.prompt.length : 0);
44693
+ let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - bounded2.characters - (includeActivePrompt ? activeTurn.prompt.length : 0);
44595
44694
  const historicalMessageLimit = Math.max(0, limit - activeMessages.length);
44596
44695
  for (let index = history.messages.length - 1; index >= 0 && historicalMessageCount < historicalMessageLimit; index -= 1) {
44597
44696
  const message = history.messages[index];
@@ -44616,7 +44715,7 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44616
44715
  return {
44617
44716
  ...page,
44618
44717
  messages: [...page.messages, ...activeMessages],
44619
- truncated: page.truncated || activeTurn.truncated || activeText.length !== activeTurn.text.length
44718
+ truncated: page.truncated || activeTurn.truncated || bounded2.truncated
44620
44719
  };
44621
44720
  }
44622
44721
  function mergeLiveSessionHistory(history, activeTurn, sessionId) {
@@ -44926,167 +45025,113 @@ function warnIgnoredSetting(audit, event, commandId, sessionId, agentId, selecti
44926
45025
  `);
44927
45026
  }
44928
45027
 
44929
- // src/relay-connection/coalescing-activity-publisher.ts
44930
- var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
44931
- var CoalescingDaemonActivityPublisher = class {
44932
- #pending = /* @__PURE__ */ new Map();
45028
+ // src/relay-connection/coalescing-agent-event-publisher.ts
45029
+ var AGENT_EVENT_COALESCING_MILLISECONDS = 100;
45030
+ var AGENT_EVENT_TEXT_MAXIMUM_LENGTH = 1e5;
45031
+ var CoalescingAgentEventPublisher = class {
45032
+ #entries = [];
44933
45033
  #publish;
44934
45034
  #publishedActivityIds = /* @__PURE__ */ new Set();
44935
- #scheduled = /* @__PURE__ */ new Map();
45035
+ #scheduled = null;
44936
45036
  #timer;
44937
45037
  constructor(timer, publish) {
44938
45038
  this.#publish = publish;
44939
45039
  this.#timer = timer;
44940
45040
  }
44941
- flush = () => {
44942
- const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
44943
- for (const task of this.#scheduled.values()) {
44944
- task.cancel();
44945
- }
44946
- this.#pending.clear();
44947
- this.#scheduled.clear();
44948
- for (const activity2 of activities) {
44949
- this.#publish(activity2);
44950
- }
44951
- };
44952
- record = (activity2) => {
44953
- if (activity2.status !== "pending" && activity2.status !== "running") {
44954
- this.#discardPending(activity2.activityId);
44955
- this.#publishedActivityIds.add(activity2.activityId);
44956
- this.#publish(activity2);
44957
- return Promise.resolve();
44958
- }
44959
- if (!this.#publishedActivityIds.has(activity2.activityId)) {
44960
- this.#publishedActivityIds.add(activity2.activityId);
44961
- this.#publish(activity2);
44962
- return Promise.resolve();
44963
- }
44964
- this.#pending.set(activity2.activityId, activity2);
44965
- if (!this.#scheduled.has(activity2.activityId)) {
44966
- this.#scheduled.set(
44967
- activity2.activityId,
44968
- this.#timer.schedule(
44969
- () => this.#flushActivity(activity2.activityId),
44970
- AGENT_ACTIVITY_COALESCING_MILLISECONDS
44971
- )
44972
- );
44973
- }
44974
- return Promise.resolve();
44975
- };
44976
- #discardPending = (activityId) => {
44977
- this.#scheduled.get(activityId)?.cancel();
44978
- this.#scheduled.delete(activityId);
44979
- this.#pending.delete(activityId);
44980
- };
44981
- #flushActivity = (activityId) => {
44982
- const activity2 = this.#pending.get(activityId);
44983
- this.#discardPending(activityId);
44984
- if (activity2 !== void 0) {
44985
- this.#publish(activity2);
44986
- }
44987
- };
44988
- };
44989
-
44990
- // src/relay-connection/coalescing-narration-publisher.ts
44991
- var CoalescingNarrationPublisher = class {
44992
- #coalescingMilliseconds;
44993
- #maximumTextLength;
44994
- #publish;
44995
- #timer;
44996
- #pending = /* @__PURE__ */ new Map();
44997
- #scheduled = null;
44998
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
44999
- this.#publish = publish;
45000
- this.#timer = timer;
45001
- this.#coalescingMilliseconds = coalescingMilliseconds;
45002
- this.#maximumTextLength = maximumTextLength;
45003
- }
45004
- /** Emit every buffered increment now, cancelling the pending flush. */
45005
45041
  flush = () => {
45006
45042
  this.#scheduled?.cancel();
45007
45043
  this.#scheduled = null;
45008
- const increments = [...this.#pending.entries()].map(
45009
- ([activityId, pending]) => ({
45010
- activityId,
45011
- activityType: pending.activityType,
45012
- sequence: pending.sequence,
45013
- text: pending.text
45014
- })
45015
- );
45016
- this.#pending.clear();
45017
- for (const increment of increments) {
45018
- this.#publish(increment);
45044
+ const entries = this.#entries;
45045
+ this.#entries = [];
45046
+ for (const entry of entries) {
45047
+ this.#publish(entry);
45019
45048
  }
45020
45049
  };
45021
- /**
45022
- * Buffer an increment, emitting the buffered text when it would exceed the
45023
- * protocol's per-event text limit so no event is rejected by the codec.
45024
- */
45025
- record = (increment) => {
45026
- if (increment.text.length === 0) {
45027
- return;
45028
- }
45029
- const previous = this.#pending.get(increment.activityId);
45030
- if (previous !== void 0 && previous.text.length + increment.text.length > this.#maximumTextLength) {
45050
+ recordActivity = (activity2) => {
45051
+ const latest = this.#entries.at(-1);
45052
+ 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" }];
45053
+ const first = !this.#publishedActivityIds.has(activity2.activityId);
45054
+ this.#publishedActivityIds.add(activity2.activityId);
45055
+ if (first || activity2.status !== "pending" && activity2.status !== "running") {
45031
45056
  this.flush();
45057
+ } else {
45058
+ this.schedule();
45032
45059
  }
45033
- const current = this.#pending.get(increment.activityId);
45034
- this.#pending.set(increment.activityId, {
45035
- activityType: increment.activityType,
45036
- sequence: increment.sequence,
45037
- text: `${current?.text ?? ""}${increment.text}`
45038
- });
45039
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
45060
+ return Promise.resolve();
45040
45061
  };
45041
- };
45042
-
45043
- // src/relay-connection/coalescing-text-publisher.ts
45044
- var CoalescingTextPublisher = class {
45045
- #coalescingMilliseconds;
45046
- #maximumTextLength;
45047
- #publish;
45048
- #timer;
45049
- #messageId = "";
45050
- #scheduled = null;
45051
- #text = "";
45052
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
45053
- this.#publish = publish;
45054
- this.#timer = timer;
45055
- this.#coalescingMilliseconds = coalescingMilliseconds;
45056
- this.#maximumTextLength = maximumTextLength;
45057
- }
45058
- /** Emit any buffered text now, cancelling the pending flush. */
45059
- flush = () => {
45060
- this.#scheduled?.cancel();
45061
- this.#scheduled = null;
45062
- const messageId = this.#messageId;
45063
- const text2 = this.#text;
45064
- this.#messageId = "";
45065
- this.#text = "";
45066
- if (text2.length > 0) {
45067
- this.#publish(messageId, text2);
45062
+ recordNarration = (increment) => {
45063
+ if (increment.text.length === 0) {
45064
+ return Promise.resolve();
45065
+ }
45066
+ let remaining = increment.text;
45067
+ while (remaining.length > 0) {
45068
+ const latest = this.#entries.at(-1);
45069
+ if (latest?.kind === "narration" && latest.increment.activityId === increment.activityId) {
45070
+ const available = AGENT_EVENT_TEXT_MAXIMUM_LENGTH - latest.increment.text.length;
45071
+ if (available > 0) {
45072
+ this.#entries = [
45073
+ ...this.#entries.slice(0, -1),
45074
+ {
45075
+ increment: {
45076
+ ...increment,
45077
+ text: `${latest.increment.text}${remaining.slice(0, available)}`
45078
+ },
45079
+ kind: "narration"
45080
+ }
45081
+ ];
45082
+ remaining = remaining.slice(available);
45083
+ continue;
45084
+ }
45085
+ }
45086
+ this.#entries = [
45087
+ ...this.#entries,
45088
+ {
45089
+ increment: { ...increment, text: remaining.slice(0, AGENT_EVENT_TEXT_MAXIMUM_LENGTH) },
45090
+ kind: "narration"
45091
+ }
45092
+ ];
45093
+ remaining = remaining.slice(AGENT_EVENT_TEXT_MAXIMUM_LENGTH);
45068
45094
  }
45095
+ this.schedule();
45096
+ return Promise.resolve();
45069
45097
  };
45070
- /**
45071
- * Buffer a chunk, emitting the buffered text when the message changes or the
45072
- * buffer would exceed the protocol's per-event text limit.
45073
- */
45074
- record = (messageId, text2) => {
45098
+ recordText = (text2, messageId) => {
45075
45099
  if (text2.length === 0) {
45076
- return;
45100
+ return Promise.resolve();
45077
45101
  }
45078
- if (this.#text.length > 0 && (this.#messageId !== messageId || this.#text.length + text2.length > this.#maximumTextLength)) {
45079
- this.flush();
45102
+ let remaining = text2;
45103
+ while (remaining.length > 0) {
45104
+ const latest = this.#entries.at(-1);
45105
+ if (latest?.kind === "text" && latest.messageId === messageId) {
45106
+ const available = AGENT_EVENT_TEXT_MAXIMUM_LENGTH - latest.text.length;
45107
+ if (available > 0) {
45108
+ this.#entries = [
45109
+ ...this.#entries.slice(0, -1),
45110
+ { ...latest, text: `${latest.text}${remaining.slice(0, available)}` }
45111
+ ];
45112
+ remaining = remaining.slice(available);
45113
+ continue;
45114
+ }
45115
+ }
45116
+ this.#entries = [
45117
+ ...this.#entries,
45118
+ {
45119
+ kind: "text",
45120
+ messageId,
45121
+ text: remaining.slice(0, AGENT_EVENT_TEXT_MAXIMUM_LENGTH)
45122
+ }
45123
+ ];
45124
+ remaining = remaining.slice(AGENT_EVENT_TEXT_MAXIMUM_LENGTH);
45080
45125
  }
45081
- this.#messageId = messageId;
45082
- this.#text = `${this.#text}${text2}`;
45083
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
45126
+ this.schedule();
45127
+ return Promise.resolve();
45084
45128
  };
45129
+ schedule() {
45130
+ this.#scheduled ??= this.#timer.schedule(this.flush, AGENT_EVENT_COALESCING_MILLISECONDS);
45131
+ }
45085
45132
  };
45086
45133
 
45087
45134
  // src/daemon-prompt-executor.ts
45088
- var AGENT_TEXT_COALESCING_MILLISECONDS = 100;
45089
- var AGENT_TEXT_MAXIMUM_LENGTH = 1e5;
45090
45135
  function daemonActivity(command, activity2) {
45091
45136
  return {
45092
45137
  activityId: activity2.activityId,
@@ -45122,41 +45167,30 @@ async function executePrompt({
45122
45167
  recordLiveEvent,
45123
45168
  cancellationRequested
45124
45169
  }) {
45125
- const narration = new CoalescingNarrationPublisher(
45126
- timer,
45127
- (increment) => {
45128
- const event = {
45129
- activityId: increment.activityId,
45130
- activityType: increment.activityType,
45170
+ const events = new CoalescingAgentEventPublisher(timer, (event) => {
45171
+ if (event.kind === "text") {
45172
+ publisher.publish(
45173
+ command.sessionId,
45174
+ recordLiveEvent({ kind: AGENT_MESSAGE_DELTA_KIND, messageId: event.messageId, text: event.text })
45175
+ );
45176
+ return;
45177
+ }
45178
+ if (event.kind === "narration") {
45179
+ const recorded = {
45180
+ activityId: event.increment.activityId,
45181
+ activityType: event.increment.activityType,
45131
45182
  commandId: command.commandId,
45132
45183
  kind: AGENT_ACTIVITY_NARRATION_KIND,
45133
- sequence: increment.sequence,
45184
+ sequence: event.increment.sequence,
45134
45185
  sessionId: command.sessionId,
45135
- text: increment.text
45186
+ text: event.increment.text
45136
45187
  };
45137
- publisher.publish(command.sessionId, recordLiveEvent(event));
45138
- },
45139
- AGENT_ACTIVITY_COALESCING_MILLISECONDS,
45140
- AGENT_TEXT_MAXIMUM_LENGTH
45141
- );
45142
- const activities = new CoalescingDaemonActivityPublisher(timer, (activity2) => {
45143
- if (activity2.status !== "pending" && activity2.status !== "running") {
45144
- narration.flush();
45188
+ publisher.publish(command.sessionId, recordLiveEvent(recorded));
45189
+ return;
45145
45190
  }
45146
- const event = daemonActivity(command, activity2);
45147
- publisher.publish(command.sessionId, recordLiveEvent(event));
45191
+ const activity2 = daemonActivity(command, event.activity);
45192
+ publisher.publish(command.sessionId, recordLiveEvent(activity2));
45148
45193
  });
45149
- const text2 = new CoalescingTextPublisher(
45150
- timer,
45151
- (messageId, delta) => {
45152
- publisher.publish(
45153
- command.sessionId,
45154
- recordLiveEvent({ kind: AGENT_MESSAGE_DELTA_KIND, messageId, text: delta })
45155
- );
45156
- },
45157
- AGENT_TEXT_COALESCING_MILLISECONDS,
45158
- AGENT_TEXT_MAXIMUM_LENGTH
45159
- );
45160
45194
  try {
45161
45195
  const resolved = await resolveSessionDirectories(
45162
45196
  directories,
@@ -45245,15 +45279,9 @@ async function executePrompt({
45245
45279
  cwd: resolved.cwd,
45246
45280
  disposition: availability.disposition,
45247
45281
  images: attachments.images,
45248
- onActivity: activities.record,
45249
- onNarration: (increment) => {
45250
- narration.record(increment);
45251
- return Promise.resolve();
45252
- },
45253
- onText: (delta, messageId) => {
45254
- text2.record(messageId, delta);
45255
- return Promise.resolve();
45256
- },
45282
+ onActivity: events.recordActivity,
45283
+ onNarration: events.recordNarration,
45284
+ onText: events.recordText,
45257
45285
  prompt: command.prompt,
45258
45286
  resources: attachments.resources,
45259
45287
  sessionId: command.sessionId,
@@ -45262,9 +45290,7 @@ async function executePrompt({
45262
45290
  });
45263
45291
  stopReason = result.stopReason;
45264
45292
  }
45265
- text2.flush();
45266
- narration.flush();
45267
- activities.flush();
45293
+ events.flush();
45268
45294
  publisher.publish(command.sessionId, {
45269
45295
  commandId: command.commandId,
45270
45296
  kind: SESSION_COMPLETED_KIND,
@@ -45278,9 +45304,7 @@ async function executePrompt({
45278
45304
  sessionId: command.sessionId
45279
45305
  });
45280
45306
  } catch (error62) {
45281
- text2.flush();
45282
- narration.flush();
45283
- activities.flush();
45307
+ events.flush();
45284
45308
  if (signal.aborted && cancellationRequested()) {
45285
45309
  publisher.publish(command.sessionId, {
45286
45310
  commandId: command.commandId,