replicas-engine 0.1.220 → 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 +163 -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";
@@ -1761,7 +1764,7 @@ function parseReplicasConfigString(content, filename) {
1761
1764
  }
1762
1765
 
1763
1766
  // ../shared/src/engine/environment.ts
1764
- var DAYTONA_SNAPSHOT_ID = "27-05-2026-royal-york-v2";
1767
+ var DAYTONA_SNAPSHOT_ID = "27-05-2026-royal-york-v3";
1765
1768
 
1766
1769
  // ../shared/src/engine/types.ts
1767
1770
  var DEFAULT_CHAT_TITLES = {
@@ -1863,6 +1866,50 @@ var MEDIA_KIND = {
1863
1866
  };
1864
1867
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
1865
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
+
1866
1913
  // src/runtime-env-loader.ts
1867
1914
  function loadRuntimeEnvFile() {
1868
1915
  let content;
@@ -6265,21 +6312,27 @@ function itemToAgentEventDrafts(item, lifecycle) {
6265
6312
  }
6266
6313
  function threadToHistoryEvents(thread) {
6267
6314
  const events = [];
6268
- 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) {
6269
6317
  const startedAt = timestampFromSeconds(turn.startedAt);
6270
6318
  const completedAt = timestampFromSeconds(turn.completedAt ?? turn.startedAt);
6271
- for (const item of turn.items) {
6272
- if (item.type === "userMessage") {
6273
- const message = item.content.filter((input) => input.type === "text").map((input) => input.text).join("\n");
6274
- if (message) {
6275
- events.push({
6276
- timestamp: startedAt,
6277
- type: "event_msg",
6278
- payload: { type: "user_message", message }
6279
- });
6280
- }
6281
- 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
+ });
6282
6333
  }
6334
+ }
6335
+ for (const item of agentItems) {
6283
6336
  for (const draft of itemToAgentEventDrafts(item, "started")) {
6284
6337
  events.push({ timestamp: startedAt, ...draft });
6285
6338
  }
@@ -6380,6 +6433,49 @@ function dispatchAspNotification(notification, handlers) {
6380
6433
  }
6381
6434
 
6382
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
+ }
6383
6479
  var CodexAspManager = class extends CodingAgentManager {
6384
6480
  currentThreadId = null;
6385
6481
  activeTurnId = null;
@@ -6425,15 +6521,13 @@ var CodexAspManager = class extends CodingAgentManager {
6425
6521
  ),
6426
6522
  this.refreshThreadGoal(host, this.currentThreadId)
6427
6523
  ]);
6428
- const events = threadToHistoryEvents(response.thread);
6429
- if (events.length > 0) {
6430
- 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");
6431
- return {
6432
- thread_id: this.currentThreadId,
6433
- events: [...events, ...supplementalEvents].sort((a, b) => a.timestamp.localeCompare(b.timestamp)),
6434
- goal: this.currentGoal
6435
- };
6436
- }
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
+ };
6437
6531
  } catch {
6438
6532
  }
6439
6533
  return {
@@ -6492,8 +6586,8 @@ var CodexAspManager = class extends CodingAgentManager {
6492
6586
  async executeGoalClearCommand(request, recordUserMessage) {
6493
6587
  const host = await getCodexAspHost();
6494
6588
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
6495
- const threadId = await this.ensureThread(host, request, developerInstructions);
6496
6589
  recordUserMessage({ command: "goal" });
6590
+ const threadId = await this.ensureThread(host, request, developerInstructions);
6497
6591
  await host.client.request(
6498
6592
  THREAD_GOAL_CLEAR_METHOD,
6499
6593
  { threadId }
@@ -6511,8 +6605,8 @@ var CodexAspManager = class extends CodingAgentManager {
6511
6605
  }
6512
6606
  }
6513
6607
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions);
6514
- const threadId = await this.ensureThread(host, request, developerInstructions);
6515
6608
  recordUserMessage(options.userMessagePayload);
6609
+ const threadId = await this.ensureThread(host, request, developerInstructions);
6516
6610
  const runTurn = options.runTurn ?? ((aspHost, aspThreadId, aspInstructions) => this.runTurn(aspHost, aspThreadId, request, aspInstructions));
6517
6611
  let completedTurn;
6518
6612
  try {
@@ -6747,8 +6841,7 @@ var CodexAspManager = class extends CodingAgentManager {
6747
6841
  }
6748
6842
  seedHistoryFromThread(thread) {
6749
6843
  const events = threadToHistoryEvents(thread);
6750
- if (events.length === 0) return;
6751
- this.historyEvents.splice(0, this.historyEvents.length, ...events);
6844
+ this.historyEvents.splice(0, this.historyEvents.length, ...mergeHistoryEvents(this.historyEvents, events));
6752
6845
  }
6753
6846
  async refreshThreadGoal(host, threadId) {
6754
6847
  try {
@@ -7457,6 +7550,19 @@ function isPersistedChat(value) {
7457
7550
  const candidate = value;
7458
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");
7459
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;
7460
7566
  var ChatService = class {
7461
7567
  constructor(workingDirectory) {
7462
7568
  this.workingDirectory = workingDirectory;
@@ -7534,7 +7640,9 @@ var ChatService = class {
7534
7640
  async sendMessage(chatId, request) {
7535
7641
  const chat = this.requireChat(chatId);
7536
7642
  const result = await chat.provider.enqueueMessage(request);
7643
+ const acceptedEvent = createUserMessageEvent(request.message, result.messageId);
7537
7644
  chat.pendingMessageIds.push(result.messageId);
7645
+ chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
7538
7646
  this.touch(chat);
7539
7647
  let recordedSender;
7540
7648
  if (request.senderUserId && request.senderEmail) {
@@ -7553,6 +7661,7 @@ var ChatService = class {
7553
7661
  messageId: result.messageId,
7554
7662
  queued: result.queued,
7555
7663
  position: result.position,
7664
+ event: acceptedEvent,
7556
7665
  ...recordedSender ? { sender: recordedSender } : {}
7557
7666
  }
7558
7667
  });
@@ -7599,8 +7708,10 @@ var ChatService = class {
7599
7708
  const chat = this.requireChat(chatId);
7600
7709
  const result = await chat.provider.interrupt();
7601
7710
  chat.hasActiveTurn = false;
7711
+ chat.activeMessageId = null;
7602
7712
  keepAliveService.stop();
7603
7713
  chat.pendingMessageIds = [];
7714
+ chat.acceptedUserEvents.clear();
7604
7715
  this.touch(chat);
7605
7716
  await this.publish({
7606
7717
  type: "chat.interrupted",
@@ -7690,9 +7801,10 @@ var ChatService = class {
7690
7801
  chat.provider.getHistory(),
7691
7802
  this.readSenders(chatId)
7692
7803
  ]);
7804
+ const acceptedEvents = [...chat.acceptedUserEvents.values()].filter((acceptedEvent) => !history.events.some((event) => isSameUserMessageEvent(event, acceptedEvent)));
7693
7805
  return {
7694
7806
  thread_id: history.thread_id,
7695
- events: history.events,
7807
+ events: [...history.events, ...acceptedEvents],
7696
7808
  goal: history.goal ?? chat.provider.getGoal?.() ?? null,
7697
7809
  senders
7698
7810
  };
@@ -7760,6 +7872,8 @@ var ChatService = class {
7760
7872
  persisted,
7761
7873
  provider,
7762
7874
  pendingMessageIds: [],
7875
+ acceptedUserEvents: /* @__PURE__ */ new Map(),
7876
+ activeMessageId: null,
7763
7877
  hasActiveTurn: false,
7764
7878
  observedBranchesByRepo: /* @__PURE__ */ new Map()
7765
7879
  };
@@ -7801,6 +7915,7 @@ var ChatService = class {
7801
7915
  return;
7802
7916
  }
7803
7917
  chat.hasActiveTurn = true;
7918
+ chat.activeMessageId = messageId;
7804
7919
  keepAliveService.start();
7805
7920
  this.publish({
7806
7921
  type: "chat.turn.started",
@@ -7811,6 +7926,25 @@ var ChatService = class {
7811
7926
  }).catch(() => {
7812
7927
  });
7813
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
+ }
7814
7948
  this.touch(chat);
7815
7949
  this.observeCurrentBranches(chat).catch(() => {
7816
7950
  });
@@ -7818,7 +7952,7 @@ var ChatService = class {
7818
7952
  type: "chat.turn.delta",
7819
7953
  payload: {
7820
7954
  chatId,
7821
- event
7955
+ event: eventToPublish
7822
7956
  }
7823
7957
  }).catch(() => {
7824
7958
  });
@@ -7840,6 +7974,7 @@ var ChatService = class {
7840
7974
  return;
7841
7975
  }
7842
7976
  chat.hasActiveTurn = false;
7977
+ chat.activeMessageId = null;
7843
7978
  keepAliveService.stop();
7844
7979
  this.publish({
7845
7980
  type: "chat.turn.completed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.220",
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",