@moxt-ai/mobius 0.0.150 → 0.0.153

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 (61) hide show
  1. package/dist/acp/agent-runtime-manager.d.ts.map +1 -1
  2. package/dist/acp/agent-runtime-manager.js +8 -1
  3. package/dist/acp/agent-runtime-manager.js.map +1 -1
  4. package/dist/acp/agent-session.d.ts.map +1 -1
  5. package/dist/acp/agent-session.js +8 -4
  6. package/dist/acp/agent-session.js.map +1 -1
  7. package/dist/acp/session-contracts.d.ts +2 -1
  8. package/dist/acp/session-contracts.d.ts.map +1 -1
  9. package/dist/acp/session-contracts.js +3 -1
  10. package/dist/acp/session-contracts.js.map +1 -1
  11. package/dist/acp/session-store.d.ts +2 -1
  12. package/dist/acp/session-store.d.ts.map +1 -1
  13. package/dist/acp/session-store.js +6 -2
  14. package/dist/acp/session-store.js.map +1 -1
  15. package/dist/cli.js +274 -76
  16. package/dist/cli.js.map +3 -3
  17. package/dist/daemon-active-turn.d.ts +1 -0
  18. package/dist/daemon-active-turn.d.ts.map +1 -1
  19. package/dist/daemon-active-turn.js +1 -0
  20. package/dist/daemon-active-turn.js.map +1 -1
  21. package/dist/daemon-command-handler.d.ts.map +1 -1
  22. package/dist/daemon-command-handler.js +1 -0
  23. package/dist/daemon-command-handler.js.map +1 -1
  24. package/dist/daemon-prompt-executor.d.ts.map +1 -1
  25. package/dist/daemon-prompt-executor.js +28 -8
  26. package/dist/daemon-prompt-executor.js.map +1 -1
  27. package/dist/daemon-turn-registry.d.ts +15 -14
  28. package/dist/daemon-turn-registry.d.ts.map +1 -1
  29. package/dist/daemon-turn-registry.js +32 -34
  30. package/dist/daemon-turn-registry.js.map +1 -1
  31. package/dist/generated/acp-session-store-migrations.d.ts +3 -0
  32. package/dist/generated/acp-session-store-migrations.d.ts.map +1 -1
  33. package/dist/generated/acp-session-store-migrations.js +5 -1
  34. package/dist/generated/acp-session-store-migrations.js.map +1 -1
  35. package/dist/index.js +264 -73
  36. package/dist/index.js.map +3 -3
  37. package/dist/native-sessions/handle-native-requests.d.ts +2 -0
  38. package/dist/native-sessions/handle-native-requests.d.ts.map +1 -1
  39. package/dist/native-sessions/handle-native-requests.js +12 -1
  40. package/dist/native-sessions/handle-native-requests.js.map +1 -1
  41. package/dist/native-sessions/live-history-page.d.ts.map +1 -1
  42. package/dist/native-sessions/live-history-page.js +2 -0
  43. package/dist/native-sessions/live-history-page.js.map +1 -1
  44. package/dist/native-sessions/service.d.ts.map +1 -1
  45. package/dist/native-sessions/service.js +47 -0
  46. package/dist/native-sessions/service.js.map +1 -1
  47. package/dist/relay-connection/session-event-outbox.d.ts +1 -0
  48. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
  49. package/dist/relay-connection/session-event-outbox.js +37 -22
  50. package/dist/relay-connection/session-event-outbox.js.map +1 -1
  51. package/dist/relay-connection/session-event-publisher.d.ts +1 -0
  52. package/dist/relay-connection/session-event-publisher.d.ts.map +1 -1
  53. package/dist/relay-connection/session-event-publisher.js +10 -2
  54. package/dist/relay-connection/session-event-publisher.js.map +1 -1
  55. package/dist/runtime.js +266 -75
  56. package/dist/runtime.js.map +3 -3
  57. package/dist/schema/acp-session-store/acp-session.d.ts +19 -0
  58. package/dist/schema/acp-session-store/acp-session.d.ts.map +1 -1
  59. package/dist/schema/acp-session-store/acp-session.js +1 -0
  60. package/dist/schema/acp-session-store/acp-session.js.map +1 -1
  61. package/package.json +10 -3
package/dist/index.js CHANGED
@@ -20990,6 +20990,11 @@ var assistantHistoryMessageSchema = external_exports.strictObject({
20990
20990
  }).readonly();
20991
20991
  var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessageSchema]);
20992
20992
  var availableSessionHistoryObjectSchema = external_exports.strictObject({
20993
+ streamPosition: external_exports.strictObject({
20994
+ commandId: identifierStringSchema,
20995
+ epoch: identifierStringSchema,
20996
+ sequence: external_exports.number().int().nonnegative()
20997
+ }).readonly().optional(),
20993
20998
  status: external_exports.literal("available"),
20994
20999
  messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
20995
21000
  truncated: external_exports.boolean(),
@@ -21108,6 +21113,12 @@ var nativeSessionRequestSchema = external_exports.discriminatedUnion("action", [
21108
21113
  entryId: identifierStringSchema,
21109
21114
  targetSessionId: identifierStringSchema
21110
21115
  }).readonly(),
21116
+ external_exports.strictObject({
21117
+ ...identity,
21118
+ action: external_exports.literal("fork-recorded"),
21119
+ targetSessionId: identifierStringSchema,
21120
+ throughCommandId: identifierStringSchema
21121
+ }).readonly(),
21111
21122
  external_exports.strictObject({
21112
21123
  ...identity,
21113
21124
  action: external_exports.literal("delete"),
@@ -21131,6 +21142,12 @@ var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
21131
21142
  entryId: identifierStringSchema,
21132
21143
  targetSessionId: identifierStringSchema
21133
21144
  }).readonly(),
21145
+ external_exports.strictObject({
21146
+ ...identity,
21147
+ action: external_exports.literal("fork-recorded"),
21148
+ targetSessionId: identifierStringSchema,
21149
+ throughCommandId: identifierStringSchema
21150
+ }).readonly(),
21134
21151
  external_exports.strictObject({
21135
21152
  ...identity,
21136
21153
  action: external_exports.literal("delete"),
@@ -21180,7 +21197,7 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
21180
21197
  }).readonly(),
21181
21198
  external_exports.strictObject({
21182
21199
  ...resultEnvelope,
21183
- action: external_exports.enum(["import", "fork"]),
21200
+ action: external_exports.enum(["import", "fork", "fork-recorded"]),
21184
21201
  targetSessionId: identifierStringSchema,
21185
21202
  ready: sessionReadyEventSchema,
21186
21203
  title: external_exports.string().max(500)
@@ -21229,6 +21246,7 @@ var SESSION_EVENT_KIND = "session.event";
21229
21246
  var SESSION_EVENT_ACK_KIND = "session.event.ack";
21230
21247
  var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
21231
21248
  var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
21249
+ var SESSION_FORKED_KIND = "session.forked";
21232
21250
  var SessionEventRejectionCode = {
21233
21251
  IdempotencyConflict: "idempotency_conflict",
21234
21252
  InvalidEvent: "invalid_event",
@@ -21246,13 +21264,13 @@ var reliableDeliverySchema = external_exports.strictObject({
21246
21264
  sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
21247
21265
  streamId: identifierStringSchema
21248
21266
  }).readonly();
21249
- var reliableEventEnvelopeSchema = external_exports.strictObject({
21267
+ var reliableSessionEventEnvelopeSchema = external_exports.strictObject({
21250
21268
  delivery: reliableDeliverySchema,
21251
21269
  kind: external_exports.literal(SESSION_EVENT_KIND),
21252
21270
  payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
21253
21271
  sessionId: identifierStringSchema
21254
21272
  }).readonly();
21255
- var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableEventEnvelopeSchema]).readonly();
21273
+ var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableSessionEventEnvelopeSchema]).readonly();
21256
21274
  var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
21257
21275
  external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
21258
21276
  external_exports.strictObject({
@@ -21281,6 +21299,12 @@ var eventRejectedSchema = external_exports.strictObject({
21281
21299
  }).readonly();
21282
21300
  var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
21283
21301
  var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
21302
+ var sessionForkedSchema = external_exports.strictObject({
21303
+ kind: external_exports.literal(SESSION_FORKED_KIND),
21304
+ sessionId: identifierStringSchema,
21305
+ sourceSessionId: identifierStringSchema,
21306
+ throughCommandId: identifierStringSchema
21307
+ }).readonly();
21284
21308
  function decode5(encoded, schema) {
21285
21309
  let value;
21286
21310
  try {
@@ -21318,6 +21342,8 @@ function decodeRecordedSessionEvent(encoded) {
21318
21342
  switch (kind) {
21319
21343
  case SESSION_QUEUE_CHANGED_KIND:
21320
21344
  return decode5(encoded, sessionQueueChangedSchema);
21345
+ case SESSION_FORKED_KIND:
21346
+ return decode5(encoded, sessionForkedSchema);
21321
21347
  case AGENT_ACTIVITY_KIND:
21322
21348
  return decodeAgentActivityEvent(encoded);
21323
21349
  case AGENT_ACTIVITY_NARRATION_KIND:
@@ -24097,13 +24123,15 @@ var AcpPreparedSession = class {
24097
24123
  capabilities;
24098
24124
  commands;
24099
24125
  configOptions;
24126
+ prompted;
24100
24127
  restored;
24101
- constructor(agentName, agentVersion, capabilities, commands, configOptions, restored) {
24128
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
24102
24129
  this.agentName = agentName;
24103
24130
  this.agentVersion = agentVersion;
24104
24131
  this.capabilities = capabilities;
24105
24132
  this.commands = commands;
24106
24133
  this.configOptions = configOptions;
24134
+ this.prompted = prompted;
24107
24135
  this.restored = restored;
24108
24136
  }
24109
24137
  };
@@ -30330,6 +30358,10 @@ var acpSessionStoreMigrations = [
30330
30358
  {
30331
30359
  "name": "0000_slimy_captain_midlands.sql",
30332
30360
  "sql": "CREATE TABLE `acp_session` (\n `session_id` text PRIMARY KEY NOT NULL,\n `agent_id` text NOT NULL,\n `native_session_id` text NOT NULL,\n `cwd` text NOT NULL,\n `additional_directories` text NOT NULL,\n `prompted` integer NOT NULL,\n `title` text NOT NULL,\n `updated_at` text NOT NULL\n);\n--> statement-breakpoint\nCREATE TABLE `deleted_acp_session` (\n `session_id` text PRIMARY KEY NOT NULL\n);\n"
30361
+ },
30362
+ {
30363
+ "name": "0001_regular_ultimates.sql",
30364
+ "sql": "ALTER TABLE `acp_session` ADD `last_terminal_command_id` text;"
30333
30365
  }
30334
30366
  ];
30335
30367
 
@@ -30347,6 +30379,7 @@ var acpSession = sqliteTable("acp_session", {
30347
30379
  nativeSessionId: text("native_session_id").notNull(),
30348
30380
  cwd: text("cwd").notNull(),
30349
30381
  additionalDirectories: text("additional_directories").notNull(),
30382
+ lastTerminalCommandId: text("last_terminal_command_id"),
30350
30383
  prompted: integer2("prompted", { mode: "boolean" }).notNull(),
30351
30384
  title: text("title").notNull(),
30352
30385
  updatedAt: text("updated_at").notNull()
@@ -30362,15 +30395,17 @@ var AcpStoredSession = class {
30362
30395
  additionalDirectories;
30363
30396
  agentId;
30364
30397
  cwd;
30398
+ lastTerminalCommandId;
30365
30399
  nativeSessionId;
30366
30400
  prompted;
30367
30401
  sessionId;
30368
30402
  title;
30369
30403
  updatedAt;
30370
- constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
30404
+ constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString(), lastTerminalCommandId = null) {
30371
30405
  this.additionalDirectories = [...additionalDirectories];
30372
30406
  this.agentId = agentId;
30373
30407
  this.cwd = cwd;
30408
+ this.lastTerminalCommandId = lastTerminalCommandId;
30374
30409
  this.nativeSessionId = nativeSessionId;
30375
30410
  this.prompted = prompted;
30376
30411
  this.sessionId = sessionId;
@@ -30431,6 +30466,7 @@ var storedSessionRowSchema = external_exports.object({
30431
30466
  additionalDirectories: external_exports.string(),
30432
30467
  agentId: external_exports.string(),
30433
30468
  cwd: external_exports.string(),
30469
+ lastTerminalCommandId: external_exports.string().nullable(),
30434
30470
  nativeSessionId: external_exports.string(),
30435
30471
  prompted: external_exports.boolean(),
30436
30472
  sessionId: external_exports.string(),
@@ -30450,7 +30486,8 @@ function decodeStoredSession(value) {
30450
30486
  additionalDirectories,
30451
30487
  row.prompted,
30452
30488
  row.title,
30453
- row.updatedAt
30489
+ row.updatedAt,
30490
+ row.lastTerminalCommandId
30454
30491
  );
30455
30492
  }
30456
30493
  var SqliteAcpSessionStore = class {
@@ -30517,6 +30554,7 @@ var SqliteAcpSessionStore = class {
30517
30554
  additionalDirectories: JSON.stringify(session.additionalDirectories),
30518
30555
  agentId: session.agentId,
30519
30556
  cwd: session.cwd,
30557
+ lastTerminalCommandId: session.lastTerminalCommandId,
30520
30558
  nativeSessionId: session.nativeSessionId,
30521
30559
  prompted: session.prompted,
30522
30560
  sessionId: session.sessionId,
@@ -30569,6 +30607,7 @@ var AgentSession = class _AgentSession {
30569
30607
  #commands = [];
30570
30608
  #configRevision = 0;
30571
30609
  #configOptions = [];
30610
+ #lastTerminalCommandId = null;
30572
30611
  #prompted = false;
30573
30612
  #nativePrompt = null;
30574
30613
  #sessionLoad = { kind: "not-requested" };
@@ -30672,6 +30711,7 @@ var AgentSession = class _AgentSession {
30672
30711
  this.#capabilities,
30673
30712
  this.#commands,
30674
30713
  this.#configOptions,
30714
+ this.#prompted,
30675
30715
  this.#restored
30676
30716
  );
30677
30717
  loadHistory = async (signal) => {
@@ -30905,6 +30945,7 @@ var AgentSession = class _AgentSession {
30905
30945
  } else if (stored instanceof FoundAcpStoredSession && stored.session.agentId === this.#agentId && stored.session.cwd === this.#cwd && arraysEqual(stored.session.additionalDirectories, this.#additionalDirectories)) {
30906
30946
  this.selectNativeSession(stored.session.nativeSessionId);
30907
30947
  this.#prompted = stored.session.prompted;
30948
+ this.#lastTerminalCommandId = stored.session.lastTerminalCommandId;
30908
30949
  this.#sessionTitle = stored.session.title;
30909
30950
  this.#restored = true;
30910
30951
  if (this.#capabilities.resumeSession) {
@@ -30978,7 +31019,9 @@ var AgentSession = class _AgentSession {
30978
31019
  this.#cwd,
30979
31020
  this.#additionalDirectories,
30980
31021
  this.#prompted,
30981
- this.#sessionTitle ?? ""
31022
+ this.#sessionTitle ?? "",
31023
+ (/* @__PURE__ */ new Date()).toISOString(),
31024
+ this.#lastTerminalCommandId
30982
31025
  )
30983
31026
  );
30984
31027
  };
@@ -31178,7 +31221,9 @@ var AgentSession = class _AgentSession {
31178
31221
  this.#cwd,
31179
31222
  this.#additionalDirectories,
31180
31223
  true,
31181
- this.#sessionTitle
31224
+ this.#sessionTitle,
31225
+ (/* @__PURE__ */ new Date()).toISOString(),
31226
+ this.#lastTerminalCommandId
31182
31227
  )
31183
31228
  );
31184
31229
  this.#prompted = true;
@@ -31230,6 +31275,20 @@ var AgentSession = class _AgentSession {
31230
31275
  usage: result.usage ?? null
31231
31276
  });
31232
31277
  }
31278
+ this.#lastTerminalCommandId = options.commandId;
31279
+ this.#store.write(
31280
+ new AcpStoredSession(
31281
+ this.#mobiusSessionId,
31282
+ this.#agentId,
31283
+ this.#updates.sessionId(),
31284
+ this.#cwd,
31285
+ this.#additionalDirectories,
31286
+ true,
31287
+ this.#sessionTitle ?? "",
31288
+ (/* @__PURE__ */ new Date()).toISOString(),
31289
+ this.#lastTerminalCommandId
31290
+ )
31291
+ );
31233
31292
  return result;
31234
31293
  } catch (error62) {
31235
31294
  await running.activities.finish();
@@ -31422,7 +31481,9 @@ var AgentSession = class _AgentSession {
31422
31481
  this.#cwd,
31423
31482
  this.#additionalDirectories,
31424
31483
  this.#prompted,
31425
- this.#sessionTitle ?? ""
31484
+ this.#sessionTitle ?? "",
31485
+ (/* @__PURE__ */ new Date()).toISOString(),
31486
+ this.#lastTerminalCommandId
31426
31487
  )
31427
31488
  );
31428
31489
  }
@@ -32468,9 +32529,17 @@ var AgentRuntimeManager = class {
32468
32529
  const openedKey = `${options2.agent.id}:${runtime.nativeSessionId()}`;
32469
32530
  this.#nativeOperations.add(openedKey);
32470
32531
  try {
32532
+ const prepared = runtime.prepared();
32533
+ if (origin.kind === "fork" && options2.agent.id === "claude") {
32534
+ await this.#retireSessionProcess(
32535
+ options2.sessionId,
32536
+ runtime,
32537
+ async () => await runtime.closeProcess()
32538
+ );
32539
+ }
32471
32540
  return {
32472
32541
  sessionId: options2.sessionId,
32473
- prepared: runtime.prepared()
32542
+ prepared
32474
32543
  };
32475
32544
  } finally {
32476
32545
  this.#nativeOperations.delete(openedKey);
@@ -39047,6 +39116,7 @@ var ActiveDaemonTurn = class {
39047
39116
  commandId: decodeIdentifier(this.command.commandId),
39048
39117
  epoch: this.#epoch,
39049
39118
  messages: this.timelineMessages(),
39119
+ sequence: this.#sequence,
39050
39120
  state: "running",
39051
39121
  truncated: false
39052
39122
  };
@@ -39074,7 +39144,7 @@ var ActiveDaemonTurn = class {
39074
39144
  };
39075
39145
 
39076
39146
  // src/daemon-turn-registry.ts
39077
- var COMPLETED_TURN_RECOVERY_LIMIT = 32;
39147
+ var MAX_SESSIONS_WITH_COMPLETED_RECOVERY = 32;
39078
39148
  var waitForTurnCompletion = (turn, waitSignal) => {
39079
39149
  waitSignal.throwIfAborted();
39080
39150
  return new Promise((resolve9, reject) => {
@@ -39100,46 +39170,46 @@ var waitForTurnCompletion = (turn, waitSignal) => {
39100
39170
  });
39101
39171
  };
39102
39172
  var DaemonTurnRegistry = class {
39103
- #turns = /* @__PURE__ */ new Map();
39104
- #completed = /* @__PURE__ */ new Map();
39173
+ #inFlightByCommandId = /* @__PURE__ */ new Map();
39174
+ #latestCompletedBySessionId = /* @__PURE__ */ new Map();
39105
39175
  #operations;
39106
39176
  constructor(operations) {
39107
39177
  this.#operations = operations;
39108
39178
  }
39109
39179
  start = (command, attemptId) => {
39110
39180
  const turn = new ActiveDaemonTurn(command, attemptId);
39111
- this.#turns.set(command.commandId, turn);
39181
+ this.#inFlightByCommandId.set(command.commandId, turn);
39112
39182
  return turn;
39113
39183
  };
39114
- get = (commandId) => this.#turns.get(commandId);
39184
+ get = (commandId) => this.#inFlightByCommandId.get(commandId);
39115
39185
  finish = (turn) => {
39116
39186
  const recovery = turn.recovery();
39117
- this.#turns.delete(turn.command.commandId);
39187
+ this.#inFlightByCommandId.delete(turn.command.commandId);
39118
39188
  if (recovery !== null) {
39119
- this.#completed.delete(turn.command.sessionId);
39120
- this.#completed.set(turn.command.sessionId, {
39121
- command: turn.command,
39122
- recovery: { ...recovery, state: "completed" }
39123
- });
39124
- while (this.#completed.size > COMPLETED_TURN_RECOVERY_LIMIT) {
39125
- const oldestSessionId = this.#completed.keys().next().value;
39126
- if (oldestSessionId === void 0) {
39127
- break;
39128
- }
39129
- this.#completed.delete(oldestSessionId);
39130
- }
39189
+ this.retainLatestCompletedRecovery(turn.command, { ...recovery, state: "completed" });
39131
39190
  }
39132
39191
  turn.finish();
39133
39192
  };
39193
+ retainLatestCompletedRecovery = (command, recovery) => {
39194
+ this.#latestCompletedBySessionId.delete(command.sessionId);
39195
+ this.#latestCompletedBySessionId.set(command.sessionId, { command, recovery });
39196
+ while (this.#latestCompletedBySessionId.size > MAX_SESSIONS_WITH_COMPLETED_RECOVERY) {
39197
+ const oldestSessionId = this.#latestCompletedBySessionId.keys().next().value;
39198
+ if (oldestSessionId === void 0) {
39199
+ break;
39200
+ }
39201
+ this.#latestCompletedBySessionId.delete(oldestSessionId);
39202
+ }
39203
+ };
39134
39204
  recordEvent = (event) => {
39135
39205
  if ("commandId" in event) {
39136
- const turn = this.#turns.get(event.commandId);
39206
+ const turn = this.#inFlightByCommandId.get(event.commandId);
39137
39207
  if (turn !== void 0 && turn.command.sessionId === event.sessionId) {
39138
39208
  return turn.recordEvent(event);
39139
39209
  }
39140
39210
  }
39141
39211
  if (event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) {
39142
- for (const turn of this.#turns.values()) {
39212
+ for (const turn of this.#inFlightByCommandId.values()) {
39143
39213
  if (turn.matchesInteraction(event.interactionId, event.sessionId)) {
39144
39214
  return turn.recordEvent(event);
39145
39215
  }
@@ -39148,17 +39218,17 @@ var DaemonTurnRegistry = class {
39148
39218
  return event;
39149
39219
  };
39150
39220
  recordSteeringPrompt = (command) => {
39151
- const target = this.#turns.get(command.targetCommandId);
39221
+ const target = this.#inFlightByCommandId.get(command.targetCommandId);
39152
39222
  return target?.recordSteeringPrompt(command) ?? false;
39153
39223
  };
39154
39224
  executionSnapshot = async (matches) => {
39155
39225
  const matching = [];
39156
- for (const turn of this.#turns.values()) {
39226
+ for (const turn of this.#inFlightByCommandId.values()) {
39157
39227
  if (await matches(turn.command)) {
39158
39228
  matching.push(turn);
39159
39229
  }
39160
39230
  }
39161
- const current = matching.filter((turn) => this.#turns.get(turn.command.commandId) === turn);
39231
+ const current = matching.filter((turn) => this.#inFlightByCommandId.get(turn.command.commandId) === turn);
39162
39232
  return {
39163
39233
  turn: current.map((turn) => turn.snapshot()).find((snapshot) => snapshot !== null) ?? null,
39164
39234
  queued: current.filter((turn) => turn.isQueued()).map(({ command }) => ({
@@ -39169,7 +39239,7 @@ var DaemonTurnRegistry = class {
39169
39239
  };
39170
39240
  };
39171
39241
  snapshot = (matches) => {
39172
- for (const turn of this.#turns.values()) {
39242
+ for (const turn of this.#inFlightByCommandId.values()) {
39173
39243
  if (!matches(turn.command)) {
39174
39244
  continue;
39175
39245
  }
@@ -39181,7 +39251,7 @@ var DaemonTurnRegistry = class {
39181
39251
  return null;
39182
39252
  };
39183
39253
  recovery = (matches) => {
39184
- for (const turn of this.#turns.values()) {
39254
+ for (const turn of this.#inFlightByCommandId.values()) {
39185
39255
  if (!matches(turn.command)) {
39186
39256
  continue;
39187
39257
  }
@@ -39190,7 +39260,7 @@ var DaemonTurnRegistry = class {
39190
39260
  return recovery;
39191
39261
  }
39192
39262
  }
39193
- for (const completed of this.#completed.values()) {
39263
+ for (const completed of this.#latestCompletedBySessionId.values()) {
39194
39264
  if (matches(completed.command)) {
39195
39265
  return completed.recovery;
39196
39266
  }
@@ -39198,17 +39268,17 @@ var DaemonTurnRegistry = class {
39198
39268
  return null;
39199
39269
  };
39200
39270
  close = async () => {
39201
- const turns = [...this.#turns.values()];
39271
+ const turns = [...this.#inFlightByCommandId.values()];
39202
39272
  for (const turn of turns) {
39203
39273
  turn.abortDaemon();
39204
39274
  }
39205
39275
  await Promise.all(turns.map((turn) => turn.completion));
39206
- this.#completed.clear();
39276
+ this.#latestCompletedBySessionId.clear();
39207
39277
  };
39208
39278
  admitAndWait = async (turn, waitSignal, onQueued) => {
39209
39279
  const command = turn.command;
39210
39280
  waitSignal.throwIfAborted();
39211
- const head = () => [...this.#turns.values()].find(
39281
+ const head = () => [...this.#inFlightByCommandId.values()].find(
39212
39282
  (turn2) => turn2.command.sessionId === command.sessionId && turn2.command.disposition !== SessionPromptDisposition.Steer
39213
39283
  );
39214
39284
  if (command.disposition === SessionPromptDisposition.Queue) {
@@ -39937,6 +40007,7 @@ var readCodexFullHistory = async (agent, cwd, nativeSessionId, signal, nativeTur
39937
40007
 
39938
40008
  // src/native-sessions/service.ts
39939
40009
  import { randomUUID as randomUUID3 } from "node:crypto";
40010
+ import { basename as basename5 } from "node:path";
39940
40011
 
39941
40012
  // src/native-sessions/acp-history.ts
39942
40013
  async function readAcpHistory(sessions, directories, bindingId, options, nativeSessionId) {
@@ -40077,6 +40148,64 @@ var NativeSessionService = class {
40077
40148
  sessionId: command.sessionId
40078
40149
  };
40079
40150
  }
40151
+ if (command.action === "fork-recorded") {
40152
+ const source = this.#sessions.findSessions([command.sessionId])[0];
40153
+ if (source === void 0 || !source.prompted) {
40154
+ throw new AcpExecutionError(
40155
+ "The source conversation has no resumable native Agent session",
40156
+ {},
40157
+ "native_session_not_found"
40158
+ );
40159
+ }
40160
+ if (source.agentId !== command.agentId || source.cwd !== cwd || source.additionalDirectories.length !== additionalDirectories.length || source.additionalDirectories.some((path2, index) => path2 !== additionalDirectories[index])) {
40161
+ throw new AcpExecutionError(
40162
+ "The source conversation execution target does not match the fork request",
40163
+ {},
40164
+ "native_session_target_changed"
40165
+ );
40166
+ }
40167
+ if (source.lastTerminalCommandId !== command.throughCommandId) {
40168
+ throw new AcpExecutionError(
40169
+ "The selected Turn is no longer the source conversation head",
40170
+ {},
40171
+ "fork_point_stale"
40172
+ );
40173
+ }
40174
+ const opened2 = await this.#sessions.openNativeSession(
40175
+ { ...options, sessionId: command.targetSessionId },
40176
+ { kind: "fork", nativeSessionId: source.nativeSessionId }
40177
+ );
40178
+ signal.throwIfAborted();
40179
+ const directory = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
40180
+ const ready2 = {
40181
+ additionalDirectories: additionalDirectories.map((path2, index) => ({
40182
+ directoryId: command.additionalDirectoryIds[index] ?? command.directoryId,
40183
+ name: basename5(path2) || path2
40184
+ })),
40185
+ agentId: command.agentId,
40186
+ agentName: opened2.prepared.agentName,
40187
+ agentVersion: opened2.prepared.agentVersion,
40188
+ capabilities: opened2.prepared.capabilities,
40189
+ commands: opened2.prepared.commands,
40190
+ configOptions: opened2.prepared.configOptions,
40191
+ directoryId: command.directoryId,
40192
+ directoryLabel: directory.directoryLabel,
40193
+ kind: "session.ready",
40194
+ requestId: command.requestId,
40195
+ restored: true,
40196
+ sessionId: opened2.sessionId
40197
+ };
40198
+ this.record(command, "native_recorded_session_forked");
40199
+ return {
40200
+ action: "fork-recorded",
40201
+ kind: "native.session.result",
40202
+ ready: ready2,
40203
+ requestId: command.requestId,
40204
+ sessionId: command.sessionId,
40205
+ targetSessionId: command.targetSessionId,
40206
+ title: source.title || "Untitled session"
40207
+ };
40208
+ }
40080
40209
  const prepared = await this.#sessions.prepareNativeSessionBrowser(options);
40081
40210
  if (!prepared.capabilities.listSessions) {
40082
40211
  throw new AcpExecutionError(
@@ -41398,7 +41527,7 @@ function createAgentSettingsDiscoveryHandler(dependencies) {
41398
41527
  // src/artifacts/local-file-streamer.ts
41399
41528
  import { createReadStream as createReadStream2 } from "node:fs";
41400
41529
  import { realpath as realpath9, stat as stat8 } from "node:fs/promises";
41401
- import { basename as basename5, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
41530
+ import { basename as basename6, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
41402
41531
  import { fileURLToPath as fileURLToPath2 } from "node:url";
41403
41532
  var CHUNK_BYTES = 192 * 1024;
41404
41533
  function requestedPath(value) {
@@ -41449,7 +41578,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
41449
41578
  kind: FILE_BEGIN_KIND,
41450
41579
  mediaType: raster?.mimeType ?? "application/octet-stream",
41451
41580
  modifiedAt: before.mtime.toISOString(),
41452
- name: basename5(target) || "file",
41581
+ name: basename6(target) || "file",
41453
41582
  requestId: command.requestId,
41454
41583
  sessionId: command.sessionId
41455
41584
  });
@@ -41479,7 +41608,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
41479
41608
  }
41480
41609
 
41481
41610
  // src/authorization/resolve-session-directories.ts
41482
- import { basename as basename6 } from "node:path";
41611
+ import { basename as basename7 } from "node:path";
41483
41612
  var ResolvedSessionDirectories = class {
41484
41613
  additionalDirectories;
41485
41614
  additionalDirectoryEntries;
@@ -41504,7 +41633,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
41504
41633
  additionalDirectories.push(directory);
41505
41634
  additionalDirectoryEntries.push({
41506
41635
  directoryId: additionalDirectoryId,
41507
- name: basename6(directory)
41636
+ name: basename7(directory)
41508
41637
  });
41509
41638
  }
41510
41639
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -41719,6 +41848,7 @@ function paginateActiveTurn(history, activeTurn, sessionId, endIndex, limit) {
41719
41848
  historicalCharacterLimit
41720
41849
  ) : null;
41721
41850
  return {
41851
+ streamPosition: { commandId: activeTurn.commandId, epoch: activeTurn.epoch, sequence: activeTurn.sequence },
41722
41852
  coveredCommandIds: historical?.coveredCommandIds ?? [],
41723
41853
  coveredMessageIds: [
41724
41854
  ...historical?.coveredMessageIds ?? [],
@@ -41751,6 +41881,7 @@ function mergeLiveSessionHistory(history, activeTurn, sessionId) {
41751
41881
  }
41752
41882
  return {
41753
41883
  coveredCommandIds: page.coveredCommandIds,
41884
+ ...page.streamPosition === void 0 ? {} : { streamPosition: page.streamPosition },
41754
41885
  coveredMessageIds: page.coveredMessageIds,
41755
41886
  messages: page.messages,
41756
41887
  status: "available",
@@ -42241,14 +42372,15 @@ async function executePrompt({
42241
42372
  const appliedSettings = command.sessionConfigurations === void 0 ? void 0 : agentForSessionConfiguration(agent, command.sessionConfigurations);
42242
42373
  const executionAgent = appliedSettings?.agent ?? agent;
42243
42374
  const sessionConfigurations = appliedSettings?.sessionConfigurations;
42375
+ const preparation = {
42376
+ additionalDirectories: resolved.additionalDirectories,
42377
+ agent: executionAgent,
42378
+ cwd: resolved.cwd,
42379
+ sessionId: command.sessionId,
42380
+ signal
42381
+ };
42382
+ let prepared = await sessions.prepareExecutionSession(preparation);
42244
42383
  if (command.sessionConfigurations !== void 0) {
42245
- const prepared = await sessions.prepareExecutionSession({
42246
- additionalDirectories: resolved.additionalDirectories,
42247
- agent: executionAgent,
42248
- cwd: resolved.cwd,
42249
- sessionId: command.sessionId,
42250
- signal
42251
- });
42252
42384
  if (availability.disposition === SessionPromptDisposition.Steer) {
42253
42385
  assertAppliedSessionConfiguration(prepared.configOptions, sessionConfigurations ?? []);
42254
42386
  } else {
@@ -42264,6 +42396,7 @@ async function executePrompt({
42264
42396
  command.sessionId,
42265
42397
  signal
42266
42398
  );
42399
+ prepared = await sessions.prepareExecutionSession(preparation);
42267
42400
  }
42268
42401
  }
42269
42402
  audit.record({
@@ -42282,6 +42415,24 @@ async function executePrompt({
42282
42415
  }
42283
42416
  }
42284
42417
  const stream = activateAttempt();
42418
+ if (!prepared.prompted) {
42419
+ const listing = await directories.list(config2.bindingId, command.directoryId, signal);
42420
+ publisher.publish(command.sessionId, {
42421
+ additionalDirectories: resolved.additionalDirectoryEntries,
42422
+ agentId: command.agentId,
42423
+ agentName: prepared.agentName,
42424
+ agentVersion: prepared.agentVersion,
42425
+ capabilities: prepared.capabilities,
42426
+ commands: prepared.commands,
42427
+ configOptions: prepared.configOptions,
42428
+ directoryId: command.directoryId,
42429
+ directoryLabel: listing.directoryLabel,
42430
+ kind: SESSION_READY_KIND,
42431
+ requestId: command.commandId,
42432
+ restored: prepared.restored,
42433
+ sessionId: command.sessionId
42434
+ });
42435
+ }
42285
42436
  publisher.publish(command.sessionId, {
42286
42437
  commandId: command.commandId,
42287
42438
  epoch: stream.epoch,
@@ -42535,7 +42686,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
42535
42686
  // src/native-sessions/handle-native-requests.ts
42536
42687
  import { WebSocket as WebSocket5 } from "undici";
42537
42688
  function createNativeSessionRequestHandler(dependencies) {
42538
- const { audit, diagnostics, nativeSessions, signal, socket, timer } = dependencies;
42689
+ const { audit, diagnostics, nativeSessions, publisher, signal, socket, timer } = dependencies;
42539
42690
  const pending = /* @__PURE__ */ new Map();
42540
42691
  const cancel = (command) => {
42541
42692
  const request = pending.get(command.requestId);
@@ -42587,6 +42738,17 @@ function createNativeSessionRequestHandler(dependencies) {
42587
42738
  }
42588
42739
  operationSignal.throwIfAborted();
42589
42740
  const result = await nativeSessions.execute(command, operationSignal);
42741
+ if (command.action === "fork-recorded" && result.action === "fork-recorded") {
42742
+ publisher.publishBatch(result.ready.sessionId, [
42743
+ result.ready,
42744
+ {
42745
+ kind: "session.forked",
42746
+ sessionId: result.ready.sessionId,
42747
+ sourceSessionId: command.sessionId,
42748
+ throughCommandId: command.throughCommandId
42749
+ }
42750
+ ]);
42751
+ }
42590
42752
  if (socket.readyState === WebSocket5.OPEN) {
42591
42753
  send(socket, result);
42592
42754
  }
@@ -42672,6 +42834,7 @@ function createDaemonCommandHandler({
42672
42834
  audit,
42673
42835
  diagnostics,
42674
42836
  nativeSessions,
42837
+ publisher,
42675
42838
  signal,
42676
42839
  socket,
42677
42840
  timer
@@ -43424,6 +43587,16 @@ var SessionEventOutbox = class {
43424
43587
  this.#database.close();
43425
43588
  };
43426
43589
  enqueue = (sessionId, payload, critical) => {
43590
+ const envelope2 = this.enqueueBatch(sessionId, [payload], critical)[0];
43591
+ if (envelope2 === void 0) {
43592
+ throw new Error("The Session event outbox did not enqueue the event");
43593
+ }
43594
+ return envelope2;
43595
+ };
43596
+ enqueueBatch = (sessionId, payloads, critical) => {
43597
+ if (payloads.length < 1 || payloads.length > 32) {
43598
+ throw new Error("A Session event batch must contain between 1 and 32 events");
43599
+ }
43427
43600
  this.#database.exec("BEGIN IMMEDIATE");
43428
43601
  try {
43429
43602
  const state = this.#database.prepare(
@@ -43432,39 +43605,52 @@ var SessionEventOutbox = class {
43432
43605
  WHERE singleton = 1`
43433
43606
  ).get();
43434
43607
  const sourceSeq = rowNumber(state, "next_source_seq");
43435
- if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + 1)) {
43608
+ if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + payloads.length)) {
43436
43609
  throw new Error("The Session event source sequence is exhausted");
43437
43610
  }
43438
- const envelope2 = {
43439
- delivery: {
43440
- eventId: randomUUID7(),
43441
- kind: "reliable",
43442
- sourceSeq: sourceSeq.toString(),
43443
- streamId: rowString(state, "stream_id")
43444
- },
43445
- kind: SESSION_EVENT_KIND,
43446
- payload,
43447
- sessionId
43448
- };
43449
- const encoded = encodeSessionEventEnvelope(envelope2);
43450
- const byteLength = Buffer.byteLength(encoded, "utf8");
43611
+ const streamId = rowString(state, "stream_id");
43612
+ const stored = payloads.map((payload, index) => {
43613
+ const envelope2 = {
43614
+ delivery: {
43615
+ eventId: randomUUID7(),
43616
+ kind: "reliable",
43617
+ sourceSeq: (sourceSeq + index).toString(),
43618
+ streamId
43619
+ },
43620
+ kind: SESSION_EVENT_KIND,
43621
+ payload,
43622
+ sessionId
43623
+ };
43624
+ const encoded = encodeSessionEventEnvelope(envelope2);
43625
+ return { byteLength: Buffer.byteLength(encoded, "utf8"), encoded, envelope: envelope2 };
43626
+ });
43627
+ const byteLength = stored.reduce((total, entry) => total + entry.byteLength, 0);
43451
43628
  const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
43452
43629
  if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
43453
43630
  throw new SessionEventOutboxCapacityError();
43454
43631
  }
43455
- this.#database.prepare(
43632
+ const insert = this.#database.prepare(
43456
43633
  `INSERT INTO relay_session_event_outbox (
43457
- event_id, session_id, source_seq, byte_length, envelope_json
43458
- ) VALUES (?, ?, ?, ?, ?)`
43459
- ).run(envelope2.delivery.eventId, sessionId, sourceSeq, byteLength, encoded);
43634
+ event_id, session_id, source_seq, byte_length, envelope_json
43635
+ ) VALUES (?, ?, ?, ?, ?)`
43636
+ );
43637
+ for (const entry of stored) {
43638
+ insert.run(
43639
+ entry.envelope.delivery.eventId,
43640
+ sessionId,
43641
+ Number(entry.envelope.delivery.sourceSeq),
43642
+ entry.byteLength,
43643
+ entry.encoded
43644
+ );
43645
+ }
43460
43646
  this.#database.prepare(
43461
43647
  `UPDATE relay_session_event_outbox_state
43462
- SET next_source_seq = next_source_seq + 1,
43648
+ SET next_source_seq = next_source_seq + ?,
43463
43649
  total_bytes = total_bytes + ?
43464
43650
  WHERE singleton = 1`
43465
- ).run(byteLength);
43651
+ ).run(stored.length, byteLength);
43466
43652
  this.#database.exec("COMMIT");
43467
- return envelope2;
43653
+ return stored.map((entry) => entry.envelope);
43468
43654
  } catch (error62) {
43469
43655
  this.#database.exec("ROLLBACK");
43470
43656
  throw error62;
@@ -43565,7 +43751,7 @@ import { WebSocket as WebSocket9 } from "undici";
43565
43751
  var SESSION_EVENT_REPLAY_WINDOW = 16;
43566
43752
  var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
43567
43753
  function reservesSessionEventCapacity(event) {
43568
- if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND) {
43754
+ if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND || event.kind === SESSION_FORKED_KIND || event.kind === SESSION_READY_KIND) {
43569
43755
  return true;
43570
43756
  }
43571
43757
  return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
@@ -43638,6 +43824,11 @@ var DaemonSessionEventPublisher = class {
43638
43824
  }
43639
43825
  this.#publishNextBatch();
43640
43826
  };
43827
+ publishBatch = (sessionId, events) => {
43828
+ const critical = events.some(reservesSessionEventCapacity);
43829
+ this.#outbox.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
43830
+ this.#publishNextBatch();
43831
+ };
43641
43832
  quarantine = (rejection) => {
43642
43833
  if (!this.#outbox.quarantine(rejection)) {
43643
43834
  return;