@moxt-ai/mobius 0.0.141 → 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 (48) 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 +347 -315
  13. package/dist/cli.js.map +4 -4
  14. package/dist/daemon-active-turn.d.ts +3 -0
  15. package/dist/daemon-active-turn.d.ts.map +1 -1
  16. package/dist/daemon-active-turn.js +81 -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/daemon-turn-registry.js +8 -1
  24. package/dist/daemon-turn-registry.js.map +1 -1
  25. package/dist/index.js +346 -314
  26. package/dist/index.js.map +4 -4
  27. package/dist/native-sessions/live-history-page.d.ts.map +1 -1
  28. package/dist/native-sessions/live-history-page.js +40 -24
  29. package/dist/native-sessions/live-history-page.js.map +1 -1
  30. package/dist/relay-connection/coalescing-agent-event-publisher.d.ts +32 -0
  31. package/dist/relay-connection/coalescing-agent-event-publisher.d.ts.map +1 -0
  32. package/dist/relay-connection/coalescing-agent-event-publisher.js +116 -0
  33. package/dist/relay-connection/coalescing-agent-event-publisher.js.map +1 -0
  34. package/dist/runtime.js +346 -314
  35. package/dist/runtime.js.map +4 -4
  36. package/package.json +1 -1
  37. package/dist/relay-connection/coalescing-activity-publisher.d.ts +0 -10
  38. package/dist/relay-connection/coalescing-activity-publisher.d.ts.map +0 -1
  39. package/dist/relay-connection/coalescing-activity-publisher.js +0 -54
  40. package/dist/relay-connection/coalescing-activity-publisher.js.map +0 -1
  41. package/dist/relay-connection/coalescing-narration-publisher.d.ts +0 -31
  42. package/dist/relay-connection/coalescing-narration-publisher.d.ts.map +0 -1
  43. package/dist/relay-connection/coalescing-narration-publisher.js +0 -61
  44. package/dist/relay-connection/coalescing-narration-publisher.js.map +0 -1
  45. package/dist/relay-connection/coalescing-text-publisher.d.ts +0 -26
  46. package/dist/relay-connection/coalescing-text-publisher.d.ts.map +0 -1
  47. package/dist/relay-connection/coalescing-text-publisher.js +0 -57
  48. 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.141",
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,19 +42265,28 @@ 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;
42289
+ matchesInteraction = (interactionId, sessionId) => this.command.sessionId === sessionId && this.#interaction?.interactionId === interactionId;
42216
42290
  requestCancellation = () => {
42217
42291
  if (this.#cancellationRequested) {
42218
42292
  return "already-requested";
@@ -42281,6 +42355,13 @@ var DaemonTurnRegistry = class {
42281
42355
  return turn.recordEvent(event);
42282
42356
  }
42283
42357
  }
42358
+ if (event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) {
42359
+ for (const turn of this.#turns.values()) {
42360
+ if (turn.matchesInteraction(event.interactionId, event.sessionId)) {
42361
+ return turn.recordEvent(event);
42362
+ }
42363
+ }
42364
+ }
42284
42365
  return event;
42285
42366
  };
42286
42367
  executionSnapshot = async (matches) => {
@@ -44513,6 +44594,34 @@ function liveHistoryCoveredTurnRanges(history) {
44513
44594
  function historyMessageCharacters(message) {
44514
44595
  return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
44515
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
+ }
44516
44625
  function paginateAcpSessionHistory(history, sessionId, cursor, limit) {
44517
44626
  if (history.status === "unavailable") {
44518
44627
  if (cursor !== null) {
@@ -44543,30 +44652,19 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44543
44652
  if (activeTurn === null || cursor !== null) {
44544
44653
  return paginateAcpSessionHistory(history, sessionId, cursor, limit);
44545
44654
  }
44546
- const activeMessageOverhead = 4;
44547
- const activeStructuredCharacters = JSON.stringify(activeTurn.activities).length + JSON.stringify(activeTurn.content).length;
44548
- const maximumActiveTextLength = Math.max(
44549
- 0,
44550
- SESSION_HISTORY_CHARACTER_LIMIT - activeTurn.prompt.length - activeStructuredCharacters - activeMessageOverhead
44551
- );
44552
- const activeText = activeTurn.text.slice(-maximumActiveTextLength);
44553
- const completeActiveMessages = [
44554
- { role: "user", text: activeTurn.prompt },
44555
- {
44556
- activities: activeTurn.activities,
44557
- content: activeTurn.content,
44558
- role: "assistant",
44559
- text: activeText
44560
- }
44561
- ];
44562
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
+ );
44563
44661
  return {
44564
44662
  coveredCommandIds: [],
44565
44663
  coveredMessageIds: [],
44566
- messages: completeActiveMessages,
44664
+ messages: [{ role: "user", text: activeTurn.prompt }, ...bounded3.messages],
44567
44665
  nextCursor: null,
44568
44666
  status: "available",
44569
- truncated: activeTurn.truncated || activeText.length !== activeTurn.text.length
44667
+ truncated: activeTurn.truncated || bounded3.truncated
44570
44668
  };
44571
44669
  }
44572
44670
  const activeUserIndex = history.coveredCommandIds.includes(activeTurn.commandId) ? history.messages.findLastIndex((message) => message.role === "user") : -1;
@@ -44581,9 +44679,18 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44581
44679
  };
44582
44680
  }
44583
44681
  const latestNativeMessage = history.messages.at(-1);
44584
- 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
+ ];
44585
44692
  let historicalMessageCount = 0;
44586
- 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);
44587
44694
  const historicalMessageLimit = Math.max(0, limit - activeMessages.length);
44588
44695
  for (let index = history.messages.length - 1; index >= 0 && historicalMessageCount < historicalMessageLimit; index -= 1) {
44589
44696
  const message = history.messages[index];
@@ -44608,7 +44715,7 @@ function paginateLiveSessionHistory(history, activeTurn, sessionId, cursor, limi
44608
44715
  return {
44609
44716
  ...page,
44610
44717
  messages: [...page.messages, ...activeMessages],
44611
- truncated: page.truncated || activeTurn.truncated || activeText.length !== activeTurn.text.length
44718
+ truncated: page.truncated || activeTurn.truncated || bounded2.truncated
44612
44719
  };
44613
44720
  }
44614
44721
  function mergeLiveSessionHistory(history, activeTurn, sessionId) {
@@ -44918,167 +45025,113 @@ function warnIgnoredSetting(audit, event, commandId, sessionId, agentId, selecti
44918
45025
  `);
44919
45026
  }
44920
45027
 
44921
- // src/relay-connection/coalescing-activity-publisher.ts
44922
- var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
44923
- var CoalescingDaemonActivityPublisher = class {
44924
- #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 = [];
44925
45033
  #publish;
44926
45034
  #publishedActivityIds = /* @__PURE__ */ new Set();
44927
- #scheduled = /* @__PURE__ */ new Map();
45035
+ #scheduled = null;
44928
45036
  #timer;
44929
45037
  constructor(timer, publish) {
44930
45038
  this.#publish = publish;
44931
45039
  this.#timer = timer;
44932
45040
  }
44933
- flush = () => {
44934
- const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
44935
- for (const task of this.#scheduled.values()) {
44936
- task.cancel();
44937
- }
44938
- this.#pending.clear();
44939
- this.#scheduled.clear();
44940
- for (const activity2 of activities) {
44941
- this.#publish(activity2);
44942
- }
44943
- };
44944
- record = (activity2) => {
44945
- if (activity2.status !== "pending" && activity2.status !== "running") {
44946
- this.#discardPending(activity2.activityId);
44947
- this.#publishedActivityIds.add(activity2.activityId);
44948
- this.#publish(activity2);
44949
- return Promise.resolve();
44950
- }
44951
- if (!this.#publishedActivityIds.has(activity2.activityId)) {
44952
- this.#publishedActivityIds.add(activity2.activityId);
44953
- this.#publish(activity2);
44954
- return Promise.resolve();
44955
- }
44956
- this.#pending.set(activity2.activityId, activity2);
44957
- if (!this.#scheduled.has(activity2.activityId)) {
44958
- this.#scheduled.set(
44959
- activity2.activityId,
44960
- this.#timer.schedule(
44961
- () => this.#flushActivity(activity2.activityId),
44962
- AGENT_ACTIVITY_COALESCING_MILLISECONDS
44963
- )
44964
- );
44965
- }
44966
- return Promise.resolve();
44967
- };
44968
- #discardPending = (activityId) => {
44969
- this.#scheduled.get(activityId)?.cancel();
44970
- this.#scheduled.delete(activityId);
44971
- this.#pending.delete(activityId);
44972
- };
44973
- #flushActivity = (activityId) => {
44974
- const activity2 = this.#pending.get(activityId);
44975
- this.#discardPending(activityId);
44976
- if (activity2 !== void 0) {
44977
- this.#publish(activity2);
44978
- }
44979
- };
44980
- };
44981
-
44982
- // src/relay-connection/coalescing-narration-publisher.ts
44983
- var CoalescingNarrationPublisher = class {
44984
- #coalescingMilliseconds;
44985
- #maximumTextLength;
44986
- #publish;
44987
- #timer;
44988
- #pending = /* @__PURE__ */ new Map();
44989
- #scheduled = null;
44990
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
44991
- this.#publish = publish;
44992
- this.#timer = timer;
44993
- this.#coalescingMilliseconds = coalescingMilliseconds;
44994
- this.#maximumTextLength = maximumTextLength;
44995
- }
44996
- /** Emit every buffered increment now, cancelling the pending flush. */
44997
45041
  flush = () => {
44998
45042
  this.#scheduled?.cancel();
44999
45043
  this.#scheduled = null;
45000
- const increments = [...this.#pending.entries()].map(
45001
- ([activityId, pending]) => ({
45002
- activityId,
45003
- activityType: pending.activityType,
45004
- sequence: pending.sequence,
45005
- text: pending.text
45006
- })
45007
- );
45008
- this.#pending.clear();
45009
- for (const increment of increments) {
45010
- this.#publish(increment);
45044
+ const entries = this.#entries;
45045
+ this.#entries = [];
45046
+ for (const entry of entries) {
45047
+ this.#publish(entry);
45011
45048
  }
45012
45049
  };
45013
- /**
45014
- * Buffer an increment, emitting the buffered text when it would exceed the
45015
- * protocol's per-event text limit so no event is rejected by the codec.
45016
- */
45017
- record = (increment) => {
45018
- if (increment.text.length === 0) {
45019
- return;
45020
- }
45021
- const previous = this.#pending.get(increment.activityId);
45022
- 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") {
45023
45056
  this.flush();
45057
+ } else {
45058
+ this.schedule();
45024
45059
  }
45025
- const current = this.#pending.get(increment.activityId);
45026
- this.#pending.set(increment.activityId, {
45027
- activityType: increment.activityType,
45028
- sequence: increment.sequence,
45029
- text: `${current?.text ?? ""}${increment.text}`
45030
- });
45031
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
45060
+ return Promise.resolve();
45032
45061
  };
45033
- };
45034
-
45035
- // src/relay-connection/coalescing-text-publisher.ts
45036
- var CoalescingTextPublisher = class {
45037
- #coalescingMilliseconds;
45038
- #maximumTextLength;
45039
- #publish;
45040
- #timer;
45041
- #messageId = "";
45042
- #scheduled = null;
45043
- #text = "";
45044
- constructor(timer, publish, coalescingMilliseconds, maximumTextLength) {
45045
- this.#publish = publish;
45046
- this.#timer = timer;
45047
- this.#coalescingMilliseconds = coalescingMilliseconds;
45048
- this.#maximumTextLength = maximumTextLength;
45049
- }
45050
- /** Emit any buffered text now, cancelling the pending flush. */
45051
- flush = () => {
45052
- this.#scheduled?.cancel();
45053
- this.#scheduled = null;
45054
- const messageId = this.#messageId;
45055
- const text2 = this.#text;
45056
- this.#messageId = "";
45057
- this.#text = "";
45058
- if (text2.length > 0) {
45059
- 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);
45060
45094
  }
45095
+ this.schedule();
45096
+ return Promise.resolve();
45061
45097
  };
45062
- /**
45063
- * Buffer a chunk, emitting the buffered text when the message changes or the
45064
- * buffer would exceed the protocol's per-event text limit.
45065
- */
45066
- record = (messageId, text2) => {
45098
+ recordText = (text2, messageId) => {
45067
45099
  if (text2.length === 0) {
45068
- return;
45100
+ return Promise.resolve();
45069
45101
  }
45070
- if (this.#text.length > 0 && (this.#messageId !== messageId || this.#text.length + text2.length > this.#maximumTextLength)) {
45071
- 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);
45072
45125
  }
45073
- this.#messageId = messageId;
45074
- this.#text = `${this.#text}${text2}`;
45075
- this.#scheduled ??= this.#timer.schedule(this.flush, this.#coalescingMilliseconds);
45126
+ this.schedule();
45127
+ return Promise.resolve();
45076
45128
  };
45129
+ schedule() {
45130
+ this.#scheduled ??= this.#timer.schedule(this.flush, AGENT_EVENT_COALESCING_MILLISECONDS);
45131
+ }
45077
45132
  };
45078
45133
 
45079
45134
  // src/daemon-prompt-executor.ts
45080
- var AGENT_TEXT_COALESCING_MILLISECONDS = 100;
45081
- var AGENT_TEXT_MAXIMUM_LENGTH = 1e5;
45082
45135
  function daemonActivity(command, activity2) {
45083
45136
  return {
45084
45137
  activityId: activity2.activityId,
@@ -45114,41 +45167,30 @@ async function executePrompt({
45114
45167
  recordLiveEvent,
45115
45168
  cancellationRequested
45116
45169
  }) {
45117
- const narration = new CoalescingNarrationPublisher(
45118
- timer,
45119
- (increment) => {
45120
- const event = {
45121
- activityId: increment.activityId,
45122
- 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,
45123
45182
  commandId: command.commandId,
45124
45183
  kind: AGENT_ACTIVITY_NARRATION_KIND,
45125
- sequence: increment.sequence,
45184
+ sequence: event.increment.sequence,
45126
45185
  sessionId: command.sessionId,
45127
- text: increment.text
45186
+ text: event.increment.text
45128
45187
  };
45129
- publisher.publish(command.sessionId, recordLiveEvent(event));
45130
- },
45131
- AGENT_ACTIVITY_COALESCING_MILLISECONDS,
45132
- AGENT_TEXT_MAXIMUM_LENGTH
45133
- );
45134
- const activities = new CoalescingDaemonActivityPublisher(timer, (activity2) => {
45135
- if (activity2.status !== "pending" && activity2.status !== "running") {
45136
- narration.flush();
45188
+ publisher.publish(command.sessionId, recordLiveEvent(recorded));
45189
+ return;
45137
45190
  }
45138
- const event = daemonActivity(command, activity2);
45139
- publisher.publish(command.sessionId, recordLiveEvent(event));
45191
+ const activity2 = daemonActivity(command, event.activity);
45192
+ publisher.publish(command.sessionId, recordLiveEvent(activity2));
45140
45193
  });
45141
- const text2 = new CoalescingTextPublisher(
45142
- timer,
45143
- (messageId, delta) => {
45144
- publisher.publish(
45145
- command.sessionId,
45146
- recordLiveEvent({ kind: AGENT_MESSAGE_DELTA_KIND, messageId, text: delta })
45147
- );
45148
- },
45149
- AGENT_TEXT_COALESCING_MILLISECONDS,
45150
- AGENT_TEXT_MAXIMUM_LENGTH
45151
- );
45152
45194
  try {
45153
45195
  const resolved = await resolveSessionDirectories(
45154
45196
  directories,
@@ -45237,15 +45279,9 @@ async function executePrompt({
45237
45279
  cwd: resolved.cwd,
45238
45280
  disposition: availability.disposition,
45239
45281
  images: attachments.images,
45240
- onActivity: activities.record,
45241
- onNarration: (increment) => {
45242
- narration.record(increment);
45243
- return Promise.resolve();
45244
- },
45245
- onText: (delta, messageId) => {
45246
- text2.record(messageId, delta);
45247
- return Promise.resolve();
45248
- },
45282
+ onActivity: events.recordActivity,
45283
+ onNarration: events.recordNarration,
45284
+ onText: events.recordText,
45249
45285
  prompt: command.prompt,
45250
45286
  resources: attachments.resources,
45251
45287
  sessionId: command.sessionId,
@@ -45254,9 +45290,7 @@ async function executePrompt({
45254
45290
  });
45255
45291
  stopReason = result.stopReason;
45256
45292
  }
45257
- text2.flush();
45258
- narration.flush();
45259
- activities.flush();
45293
+ events.flush();
45260
45294
  publisher.publish(command.sessionId, {
45261
45295
  commandId: command.commandId,
45262
45296
  kind: SESSION_COMPLETED_KIND,
@@ -45270,9 +45304,7 @@ async function executePrompt({
45270
45304
  sessionId: command.sessionId
45271
45305
  });
45272
45306
  } catch (error62) {
45273
- text2.flush();
45274
- narration.flush();
45275
- activities.flush();
45307
+ events.flush();
45276
45308
  if (signal.aborted && cancellationRequested()) {
45277
45309
  publisher.publish(command.sessionId, {
45278
45310
  commandId: command.commandId,