replicas-engine 0.1.219 → 0.1.221

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 (2) hide show
  1. package/dist/src/index.js +165 -28
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -32,6 +32,9 @@ function codexReasoningEffortForThinkingLevel(thinkingLevel) {
32
32
  }
33
33
 
34
34
  // ../shared/src/event.ts
35
+ var ACCEPTED_USER_MESSAGE_SOURCE = "replicas-chat-turn-accepted";
36
+ var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
37
+ var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
35
38
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
36
39
  var COMPACTION_STATUS_EVENT_TYPE = "compaction-status";
37
40
  var CHAT_GOAL_EVENT_TYPE = "chat-goal";
@@ -283,6 +286,8 @@ function buildPaths(homeDir) {
283
286
  }
284
287
  var DAYTONA_PATHS = buildPaths("/home/ubuntu");
285
288
  var E2B_PATHS = buildPaths("/home/user");
289
+ var WORKSPACE_SIZES = ["small", "large"];
290
+ var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
286
291
 
287
292
  // ../shared/src/runtime-env.ts
288
293
  function parsePosixEnvFile(content) {
@@ -1759,7 +1764,7 @@ function parseReplicasConfigString(content, filename) {
1759
1764
  }
1760
1765
 
1761
1766
  // ../shared/src/engine/environment.ts
1762
- var DAYTONA_SNAPSHOT_ID = "27-05-2026-royal-york-v1";
1767
+ var DAYTONA_SNAPSHOT_ID = "27-05-2026-royal-york-v3";
1763
1768
 
1764
1769
  // ../shared/src/engine/types.ts
1765
1770
  var DEFAULT_CHAT_TITLES = {
@@ -1861,6 +1866,50 @@ var MEDIA_KIND = {
1861
1866
  };
1862
1867
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
1863
1868
 
1869
+ // ../shared/src/agent-event-utils.ts
1870
+ function getUserMessage(event) {
1871
+ return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
1872
+ }
1873
+ function getUserMessageId(event) {
1874
+ const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
1875
+ return typeof messageId === "string" ? messageId : null;
1876
+ }
1877
+ function getUserMessageItemId(event) {
1878
+ const itemId = event.payload[CODEX_ASP_ITEM_ID_PAYLOAD_KEY];
1879
+ return typeof itemId === "string" ? itemId : null;
1880
+ }
1881
+ function getEventTimestampMs(event) {
1882
+ const value = Date.parse(event.timestamp);
1883
+ return Number.isNaN(value) ? 0 : value;
1884
+ }
1885
+ function areSameUserMessageEvents(a, b) {
1886
+ const aMessage = getUserMessage(a);
1887
+ const bMessage = getUserMessage(b);
1888
+ if (!aMessage || aMessage !== bMessage) return false;
1889
+ const aMessageId = getUserMessageId(a);
1890
+ const bMessageId = getUserMessageId(b);
1891
+ if (aMessageId || bMessageId) return aMessageId === bMessageId;
1892
+ const aItemId = getUserMessageItemId(a);
1893
+ const bItemId = getUserMessageItemId(b);
1894
+ if (aItemId || bItemId) return aItemId === bItemId;
1895
+ return Math.abs(getEventTimestampMs(a) - getEventTimestampMs(b)) <= 3e4;
1896
+ }
1897
+ function mergeAgentEvents(primary, supplemental, options) {
1898
+ const merged = [...primary];
1899
+ const { areDuplicates, mergeEvent } = options;
1900
+ for (const event of supplemental) {
1901
+ const existingIndex = merged.findIndex((existing) => areDuplicates(existing, event));
1902
+ if (existingIndex === -1) {
1903
+ merged.push(event);
1904
+ continue;
1905
+ }
1906
+ if (mergeEvent) {
1907
+ merged[existingIndex] = mergeEvent(merged[existingIndex], event);
1908
+ }
1909
+ }
1910
+ return merged;
1911
+ }
1912
+
1864
1913
  // src/runtime-env-loader.ts
1865
1914
  function loadRuntimeEnvFile() {
1866
1915
  let content;
@@ -6263,21 +6312,27 @@ function itemToAgentEventDrafts(item, lifecycle) {
6263
6312
  }
6264
6313
  function threadToHistoryEvents(thread) {
6265
6314
  const events = [];
6266
- for (const turn of thread.turns) {
6315
+ const turns = thread.turns.map((turn, index) => ({ turn, index })).sort((a, b) => (a.turn.startedAt ?? a.turn.completedAt ?? Number.MAX_SAFE_INTEGER) - (b.turn.startedAt ?? b.turn.completedAt ?? Number.MAX_SAFE_INTEGER) || a.index - b.index);
6316
+ for (const { turn } of turns) {
6267
6317
  const startedAt = timestampFromSeconds(turn.startedAt);
6268
6318
  const completedAt = timestampFromSeconds(turn.completedAt ?? turn.startedAt);
6269
- for (const item of turn.items) {
6270
- if (item.type === "userMessage") {
6271
- const message = item.content.filter((input) => input.type === "text").map((input) => input.text).join("\n");
6272
- if (message) {
6273
- events.push({
6274
- timestamp: startedAt,
6275
- type: "event_msg",
6276
- payload: { type: "user_message", message }
6277
- });
6278
- }
6279
- continue;
6319
+ const userMessages = turn.items.filter((item) => item.type === "userMessage");
6320
+ const agentItems = turn.items.filter((item) => item.type !== "userMessage");
6321
+ for (const item of userMessages) {
6322
+ const message = item.content.filter((input) => input.type === "text").map((input) => input.text).join("\n");
6323
+ if (message) {
6324
+ events.push({
6325
+ timestamp: startedAt,
6326
+ type: "event_msg",
6327
+ payload: {
6328
+ type: "user_message",
6329
+ message,
6330
+ [CODEX_ASP_ITEM_ID_PAYLOAD_KEY]: item.id
6331
+ }
6332
+ });
6280
6333
  }
6334
+ }
6335
+ for (const item of agentItems) {
6281
6336
  for (const draft of itemToAgentEventDrafts(item, "started")) {
6282
6337
  events.push({ timestamp: startedAt, ...draft });
6283
6338
  }
@@ -6378,6 +6433,49 @@ function dispatchAspNotification(notification, handlers) {
6378
6433
  }
6379
6434
 
6380
6435
  // src/managers/codex-asp/codex-asp-manager.ts
6436
+ function historyEventKey(event) {
6437
+ const payloadType = typeof event.payload.type === "string" ? event.payload.type : null;
6438
+ const callId = typeof event.payload.call_id === "string" ? event.payload.call_id : null;
6439
+ const itemId = typeof event.payload[CODEX_ASP_ITEM_ID_PAYLOAD_KEY] === "string" ? event.payload[CODEX_ASP_ITEM_ID_PAYLOAD_KEY] : null;
6440
+ const messageId = typeof event.payload[USER_MESSAGE_ID_PAYLOAD_KEY] === "string" ? event.payload[USER_MESSAGE_ID_PAYLOAD_KEY] : null;
6441
+ if (event.type === "response_item" && payloadType && callId) {
6442
+ return `${event.type}:${payloadType}:${callId}`;
6443
+ }
6444
+ if (event.type === "event_msg" && event.payload.type === "user_message") {
6445
+ if (messageId) return `${event.type}:user_message:message:${messageId}`;
6446
+ if (itemId) return `${event.type}:user_message:item:${itemId}`;
6447
+ const command = typeof event.payload.command === "string" ? event.payload.command : "";
6448
+ const message = typeof event.payload.message === "string" ? event.payload.message : "";
6449
+ return `${event.type}:user_message:${event.timestamp}:${command}:${message}`;
6450
+ }
6451
+ if (itemId && payloadType) {
6452
+ return `${event.type}:${payloadType}:item:${itemId}`;
6453
+ }
6454
+ return `${event.type}:${event.timestamp}:${JSON.stringify(event.payload)}`;
6455
+ }
6456
+ function areDuplicateHistoryEvents(a, b) {
6457
+ if (historyEventKey(a) === historyEventKey(b)) return true;
6458
+ return areSameUserMessageEvents(a, b);
6459
+ }
6460
+ function mergeHistoryEvent(current, candidate) {
6461
+ if (getUserMessage(current) && getUserMessage(current) === getUserMessage(candidate)) {
6462
+ return {
6463
+ ...current,
6464
+ timestamp: getEventTimestampMs(current) <= getEventTimestampMs(candidate) ? current.timestamp : candidate.timestamp,
6465
+ payload: {
6466
+ ...current.payload,
6467
+ ...candidate.payload
6468
+ }
6469
+ };
6470
+ }
6471
+ return candidate;
6472
+ }
6473
+ function mergeHistoryEvents(primary, supplemental) {
6474
+ return mergeAgentEvents(primary, supplemental, {
6475
+ areDuplicates: areDuplicateHistoryEvents,
6476
+ mergeEvent: mergeHistoryEvent
6477
+ });
6478
+ }
6381
6479
  var CodexAspManager = class extends CodingAgentManager {
6382
6480
  currentThreadId = null;
6383
6481
  activeTurnId = null;
@@ -6423,15 +6521,13 @@ var CodexAspManager = class extends CodingAgentManager {
6423
6521
  ),
6424
6522
  this.refreshThreadGoal(host, this.currentThreadId)
6425
6523
  ]);
6426
- const events = threadToHistoryEvents(response.thread);
6427
- if (events.length > 0) {
6428
- const supplementalEvents = this.historyEvents.filter((event) => event.type === CODEX_QUOTA_STATUS_EVENT_TYPE || event.type === CONTEXT_USAGE_EVENT_TYPE || event.type === CHAT_GOAL_EVENT_TYPE || event.type === "event_msg" && event.payload.command === "goal");
6429
- return {
6430
- thread_id: this.currentThreadId,
6431
- events: [...events, ...supplementalEvents].sort((a, b) => a.timestamp.localeCompare(b.timestamp)),
6432
- goal: this.currentGoal
6433
- };
6434
- }
6524
+ const events = mergeHistoryEvents(this.historyEvents, threadToHistoryEvents(response.thread));
6525
+ this.historyEvents.splice(0, this.historyEvents.length, ...events);
6526
+ return {
6527
+ thread_id: this.currentThreadId,
6528
+ events,
6529
+ goal: this.currentGoal
6530
+ };
6435
6531
  } catch {
6436
6532
  }
6437
6533
  return {
@@ -6490,8 +6586,8 @@ var CodexAspManager = class extends CodingAgentManager {
6490
6586
  async executeGoalClearCommand(request, recordUserMessage) {
6491
6587
  const host = await getCodexAspHost();
6492
6588
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
6493
- const threadId = await this.ensureThread(host, request, developerInstructions);
6494
6589
  recordUserMessage({ command: "goal" });
6590
+ const threadId = await this.ensureThread(host, request, developerInstructions);
6495
6591
  await host.client.request(
6496
6592
  THREAD_GOAL_CLEAR_METHOD,
6497
6593
  { threadId }
@@ -6509,8 +6605,8 @@ var CodexAspManager = class extends CodingAgentManager {
6509
6605
  }
6510
6606
  }
6511
6607
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
6512
- const threadId = await this.ensureThread(host, request, developerInstructions);
6513
6608
  recordUserMessage(options.userMessagePayload);
6609
+ const threadId = await this.ensureThread(host, request, developerInstructions);
6514
6610
  const runTurn = options.runTurn ?? ((aspHost, aspThreadId, aspInstructions) => this.runTurn(aspHost, aspThreadId, request, aspInstructions));
6515
6611
  let completedTurn;
6516
6612
  try {
@@ -6745,8 +6841,7 @@ var CodexAspManager = class extends CodingAgentManager {
6745
6841
  }
6746
6842
  seedHistoryFromThread(thread) {
6747
6843
  const events = threadToHistoryEvents(thread);
6748
- if (events.length === 0) return;
6749
- this.historyEvents.splice(0, this.historyEvents.length, ...events);
6844
+ this.historyEvents.splice(0, this.historyEvents.length, ...mergeHistoryEvents(this.historyEvents, events));
6750
6845
  }
6751
6846
  async refreshThreadGoal(host, threadId) {
6752
6847
  try {
@@ -7455,6 +7550,19 @@ function isPersistedChat(value) {
7455
7550
  const candidate = value;
7456
7551
  return typeof candidate.id === "string" && (candidate.provider === "claude" || candidate.provider === "codex" || candidate.provider === "relay") && typeof candidate.title === "string" && typeof candidate.createdAt === "string" && typeof candidate.updatedAt === "string" && (candidate.providerSessionId === null || typeof candidate.providerSessionId === "string") && (candidate.parentChatId === void 0 || candidate.parentChatId === null || typeof candidate.parentChatId === "string");
7457
7552
  }
7553
+ function createUserMessageEvent(message, messageId) {
7554
+ return {
7555
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7556
+ type: "event_msg",
7557
+ payload: {
7558
+ type: "user_message",
7559
+ message,
7560
+ source: ACCEPTED_USER_MESSAGE_SOURCE,
7561
+ [USER_MESSAGE_ID_PAYLOAD_KEY]: messageId
7562
+ }
7563
+ };
7564
+ }
7565
+ var isSameUserMessageEvent = areSameUserMessageEvents;
7458
7566
  var ChatService = class {
7459
7567
  constructor(workingDirectory) {
7460
7568
  this.workingDirectory = workingDirectory;
@@ -7532,7 +7640,9 @@ var ChatService = class {
7532
7640
  async sendMessage(chatId, request) {
7533
7641
  const chat = this.requireChat(chatId);
7534
7642
  const result = await chat.provider.enqueueMessage(request);
7643
+ const acceptedEvent = createUserMessageEvent(request.message, result.messageId);
7535
7644
  chat.pendingMessageIds.push(result.messageId);
7645
+ chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
7536
7646
  this.touch(chat);
7537
7647
  let recordedSender;
7538
7648
  if (request.senderUserId && request.senderEmail) {
@@ -7551,6 +7661,7 @@ var ChatService = class {
7551
7661
  messageId: result.messageId,
7552
7662
  queued: result.queued,
7553
7663
  position: result.position,
7664
+ event: acceptedEvent,
7554
7665
  ...recordedSender ? { sender: recordedSender } : {}
7555
7666
  }
7556
7667
  });
@@ -7597,8 +7708,10 @@ var ChatService = class {
7597
7708
  const chat = this.requireChat(chatId);
7598
7709
  const result = await chat.provider.interrupt();
7599
7710
  chat.hasActiveTurn = false;
7711
+ chat.activeMessageId = null;
7600
7712
  keepAliveService.stop();
7601
7713
  chat.pendingMessageIds = [];
7714
+ chat.acceptedUserEvents.clear();
7602
7715
  this.touch(chat);
7603
7716
  await this.publish({
7604
7717
  type: "chat.interrupted",
@@ -7688,9 +7801,10 @@ var ChatService = class {
7688
7801
  chat.provider.getHistory(),
7689
7802
  this.readSenders(chatId)
7690
7803
  ]);
7804
+ const acceptedEvents = [...chat.acceptedUserEvents.values()].filter((acceptedEvent) => !history.events.some((event) => isSameUserMessageEvent(event, acceptedEvent)));
7691
7805
  return {
7692
7806
  thread_id: history.thread_id,
7693
- events: history.events,
7807
+ events: [...history.events, ...acceptedEvents],
7694
7808
  goal: history.goal ?? chat.provider.getGoal?.() ?? null,
7695
7809
  senders
7696
7810
  };
@@ -7758,6 +7872,8 @@ var ChatService = class {
7758
7872
  persisted,
7759
7873
  provider,
7760
7874
  pendingMessageIds: [],
7875
+ acceptedUserEvents: /* @__PURE__ */ new Map(),
7876
+ activeMessageId: null,
7761
7877
  hasActiveTurn: false,
7762
7878
  observedBranchesByRepo: /* @__PURE__ */ new Map()
7763
7879
  };
@@ -7799,6 +7915,7 @@ var ChatService = class {
7799
7915
  return;
7800
7916
  }
7801
7917
  chat.hasActiveTurn = true;
7918
+ chat.activeMessageId = messageId;
7802
7919
  keepAliveService.start();
7803
7920
  this.publish({
7804
7921
  type: "chat.turn.started",
@@ -7809,6 +7926,25 @@ var ChatService = class {
7809
7926
  }).catch(() => {
7810
7927
  });
7811
7928
  }
7929
+ let eventToPublish = event;
7930
+ if (event.type === "event_msg" && event.payload.type === "user_message") {
7931
+ if (chat.activeMessageId) {
7932
+ event.payload[USER_MESSAGE_ID_PAYLOAD_KEY] = chat.activeMessageId;
7933
+ eventToPublish = {
7934
+ ...event,
7935
+ payload: {
7936
+ ...event.payload,
7937
+ [USER_MESSAGE_ID_PAYLOAD_KEY]: chat.activeMessageId
7938
+ }
7939
+ };
7940
+ }
7941
+ for (const [messageId, acceptedEvent] of chat.acceptedUserEvents) {
7942
+ if (isSameUserMessageEvent(event, acceptedEvent)) {
7943
+ chat.acceptedUserEvents.delete(messageId);
7944
+ break;
7945
+ }
7946
+ }
7947
+ }
7812
7948
  this.touch(chat);
7813
7949
  this.observeCurrentBranches(chat).catch(() => {
7814
7950
  });
@@ -7816,7 +7952,7 @@ var ChatService = class {
7816
7952
  type: "chat.turn.delta",
7817
7953
  payload: {
7818
7954
  chatId,
7819
- event
7955
+ event: eventToPublish
7820
7956
  }
7821
7957
  }).catch(() => {
7822
7958
  });
@@ -7838,6 +7974,7 @@ var ChatService = class {
7838
7974
  return;
7839
7975
  }
7840
7976
  chat.hasActiveTurn = false;
7977
+ chat.activeMessageId = null;
7841
7978
  keepAliveService.stop();
7842
7979
  this.publish({
7843
7980
  type: "chat.turn.completed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.219",
3
+ "version": "0.1.221",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",