replicas-engine 0.1.696 → 0.1.698

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.
@@ -724,6 +724,10 @@ function coerceClaudePartialMessagePayload(payload) {
724
724
  status: payload.status === "completed" ? "completed" : "in_progress"
725
725
  };
726
726
  }
727
+ function getClaudePartialMessageStreamId(event) {
728
+ if (event.type !== CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE) return null;
729
+ return typeof event.payload.streamId === "string" && event.payload.streamId ? event.payload.streamId : null;
730
+ }
727
731
  var ACCEPTED_USER_MESSAGE_SOURCE = "replicas-chat-turn-accepted";
728
732
  var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
729
733
  var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
@@ -3843,7 +3847,6 @@ var HOSTED_COMPOSIO_PLUGIN_DEFINITIONS = [
3843
3847
  ["xero", "xero", "OAUTH2", false, "business", "Xero"],
3844
3848
  ["brex", "brex", "API_KEY", false, "business", "Brex"],
3845
3849
  ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"],
3846
- ["docusign", "docusign", "OAUTH2", false, "business", "DocuSign"],
3847
3850
  ["canva", "canva", "OAUTH2", true, "productivity", "Canva"],
3848
3851
  ["webflow", "webflow", "API_KEY", false, "business", "Webflow"],
3849
3852
  ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"],
@@ -4060,10 +4063,11 @@ function getEventTimestampMs(event) {
4060
4063
  function areSameUserMessageEvents(a, b) {
4061
4064
  const aMessage = getUserMessage(a);
4062
4065
  const bMessage = getUserMessage(b);
4063
- if (!aMessage || aMessage !== bMessage) return false;
4066
+ if (!aMessage || !bMessage) return false;
4064
4067
  const aMessageId = getUserMessageId(a);
4065
4068
  const bMessageId = getUserMessageId(b);
4066
4069
  if (aMessageId || bMessageId) return aMessageId === bMessageId;
4070
+ if (aMessage !== bMessage) return false;
4067
4071
  const aItemId = getUserMessageItemId(a);
4068
4072
  const bItemId = getUserMessageItemId(b);
4069
4073
  if (aItemId || bItemId) return aItemId === bItemId;
@@ -4563,7 +4567,7 @@ function parseCursorEvents(events) {
4563
4567
  const message = event.payload.message;
4564
4568
  if (typeof message === "string" && message.trim()) {
4565
4569
  messages.push({
4566
- id: `cursor-user-${event.timestamp}-${messages.length}`,
4570
+ id: getUserMessageId(event) ?? `cursor-user-${event.timestamp}-${messages.length}`,
4567
4571
  type: "user",
4568
4572
  content: message,
4569
4573
  timestamp: event.timestamp
@@ -5144,7 +5148,7 @@ function parsePiEvents(events) {
5144
5148
  let activeMessageId = "assistant";
5145
5149
  for (const event of events) {
5146
5150
  if (event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string") {
5147
- messages.push({ id: `pi-user-${event.timestamp}-${messages.length}`, type: "user", content: event.payload.message, timestamp: event.timestamp });
5151
+ messages.push({ id: getUserMessageId(event) ?? `pi-user-${event.timestamp}-${messages.length}`, type: "user", content: event.payload.message, timestamp: event.timestamp });
5148
5152
  if (typeof event.payload.skillName === "string" && event.payload.skillName) {
5149
5153
  messages.push({
5150
5154
  id: `pi-skill-${event.timestamp}-${messages.length}`,
@@ -6185,7 +6189,12 @@ function parseAgentEvents(events, agentType) {
6185
6189
  function parseDisplayMessages(events, agentType, codexAspTranscript, options = {}) {
6186
6190
  const shouldFilter = options.filter ?? true;
6187
6191
  const parsedEvents = agentType === "claude" || agentType === "relay" ? parseClaudeEvents(events, options.parentToolUseId) : parseAgentEvents(events, agentType);
6188
- const legacyMessages = shouldFilter ? filterDisplayMessages(parsedEvents, agentType) : parsedEvents;
6192
+ const reconciledEvents = [];
6193
+ for (const message of parsedEvents) {
6194
+ if (message.type === "user") upsertDisplayMessage(reconciledEvents, message);
6195
+ else reconciledEvents.push(message);
6196
+ }
6197
+ const legacyMessages = shouldFilter ? filterDisplayMessages(reconciledEvents, agentType) : reconciledEvents;
6189
6198
  const applySyntheticNotices = (messages) => shouldFilter ? applyAuthFallbackNotices(applyInterruptions(messages, events), events) : messages;
6190
6199
  if (agentType !== "codex" || !codexAspTranscript) {
6191
6200
  return applySyntheticNotices(legacyMessages);
@@ -6766,7 +6775,7 @@ var DEFAULT_CODEX_ARGS = [
6766
6775
  var MIN_CODEX_CLI_VERSION = "0.144.6";
6767
6776
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
6768
6777
  var codexCliVersionEnsured = null;
6769
- var ENGINE_PACKAGE_VERSION = "0.1.696";
6778
+ var ENGINE_PACKAGE_VERSION = "0.1.698";
6770
6779
  var INITIALIZE_METHOD = "initialize";
6771
6780
  var INITIALIZED_NOTIFICATION = "initialized";
6772
6781
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -7038,6 +7047,8 @@ export {
7038
7047
  isAgentChatSkillActivityRecord,
7039
7048
  isAgentChatMcpActivityRecord,
7040
7049
  CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE,
7050
+ getClaudePartialMessageStreamId,
7051
+ ACCEPTED_USER_MESSAGE_SOURCE,
7041
7052
  USER_MESSAGE_ID_PAYLOAD_KEY,
7042
7053
  CODEX_ASP_TRANSCRIPT_UPDATED_EVENT_TYPE,
7043
7054
  CODEX_QUOTA_STATUS_EVENT_TYPE,
@@ -7,7 +7,7 @@ import {
7
7
  headlessAgentRequestSchema,
8
8
  putPresignedFile,
9
9
  recoverCompletedTurn
10
- } from "./chunk-CFT4CABT.js";
10
+ } from "./chunk-2BBE2BVQ.js";
11
11
 
12
12
  // src/headless-agent.ts
13
13
  import { createHash } from "crypto";
package/dist/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ ACCEPTED_USER_MESSAGE_SOURCE,
3
4
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
4
5
  AGENT,
5
6
  AGENT_MESSAGE_DELTA_METHOD,
@@ -131,6 +132,7 @@ import {
131
132
  getChatHistoryPageWindow,
132
133
  getChatTranscriptHistorySize,
133
134
  getClaudeModelContextWindow,
135
+ getClaudePartialMessageStreamId,
134
136
  getCodexAspTurnResponse,
135
137
  getDeepseekAssistantMessageText,
136
138
  getDefaultAgentModel,
@@ -192,13 +194,13 @@ import {
192
194
  serializeCanvasContentResponse,
193
195
  shellQuotePosix,
194
196
  stripAgentDiagnosticErrors
195
- } from "./chunk-CFT4CABT.js";
197
+ } from "./chunk-2BBE2BVQ.js";
196
198
 
197
199
  // src/index.ts
198
200
  import { serve } from "@hono/node-server";
199
201
  import { Hono as Hono2 } from "hono";
200
202
  import { existsSync as existsSync11 } from "fs";
201
- import { randomUUID as randomUUID10 } from "crypto";
203
+ import { randomUUID as randomUUID11 } from "crypto";
202
204
  import { connect } from "net";
203
205
 
204
206
  // src/managers/github-token-manager.ts
@@ -569,6 +571,7 @@ var MonolithService = class {
569
571
  var monolithService = new MonolithService();
570
572
 
571
573
  // src/utils/file.ts
574
+ import { randomUUID } from "crypto";
572
575
  import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
573
576
  import { dirname, join as join3 } from "path";
574
577
 
@@ -587,7 +590,7 @@ var AsyncLock = class {
587
590
 
588
591
  // src/utils/file.ts
589
592
  async function atomicWriteFile(path6, data, options) {
590
- const tmpFile = `${path6}.${process.pid}.${Date.now()}.tmp`;
593
+ const tmpFile = `${path6}.${process.pid}.${randomUUID()}.tmp`;
591
594
  try {
592
595
  await writeFile(tmpFile, data, { encoding: "utf-8", mode: options?.mode });
593
596
  await rename(tmpFile, path6);
@@ -2663,7 +2666,7 @@ var replicasConfigService = new ReplicasConfigService();
2663
2666
  import { mkdir as mkdir7 } from "fs/promises";
2664
2667
  import { homedir as homedir9 } from "os";
2665
2668
  import { join as join12 } from "path";
2666
- import { randomUUID } from "crypto";
2669
+ import { randomUUID as randomUUID2 } from "crypto";
2667
2670
  var ENGINE_DIR = join12(homedir9(), ".replicas", "engine");
2668
2671
  var EVENTS_FILE = join12(ENGINE_DIR, "events.jsonl");
2669
2672
  var EventService = class {
@@ -2674,7 +2677,7 @@ var EventService = class {
2674
2677
  this.writer.open(EVENTS_FILE);
2675
2678
  }
2676
2679
  subscribe(subscriber) {
2677
- const id = randomUUID();
2680
+ const id = randomUUID2();
2678
2681
  this.subscribers.set(id, subscriber);
2679
2682
  return () => {
2680
2683
  this.subscribers.delete(id);
@@ -2698,7 +2701,7 @@ var eventService = new EventService();
2698
2701
  // src/services/preview-service.ts
2699
2702
  import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
2700
2703
  import { existsSync as existsSync4 } from "fs";
2701
- import { randomUUID as randomUUID2 } from "crypto";
2704
+ import { randomUUID as randomUUID3 } from "crypto";
2702
2705
  import { homedir as homedir10 } from "os";
2703
2706
  import { dirname as dirname2, join as join13 } from "path";
2704
2707
  var PREVIEW_PORTS_FILE = join13(homedir10(), ".replicas", "preview-ports.json");
@@ -2737,7 +2740,7 @@ var PreviewService = class {
2737
2740
  data.previews.push(preview);
2738
2741
  await writePreviewsFile(data);
2739
2742
  eventService.publish({
2740
- id: randomUUID2(),
2743
+ id: randomUUID3(),
2741
2744
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2742
2745
  type: "preview.changed",
2743
2746
  payload: { previews: data.previews }
@@ -2757,7 +2760,7 @@ var PreviewService = class {
2757
2760
  }
2758
2761
  await writePreviewsFile(data);
2759
2762
  eventService.publish({
2760
- id: randomUUID2(),
2763
+ id: randomUUID3(),
2761
2764
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2762
2765
  type: "preview.changed",
2763
2766
  payload: { previews: data.previews }
@@ -2814,13 +2817,138 @@ async function registerDesktopPreview() {
2814
2817
  import { existsSync as existsSync8 } from "fs";
2815
2818
  import { appendFile as appendFile4, copyFile, mkdir as mkdir17, readFile as readFile17, rename as rename3, rm as rm2 } from "fs/promises";
2816
2819
  import { join as join29 } from "path";
2817
- import { randomUUID as randomUUID8 } from "crypto";
2820
+ import { randomUUID as randomUUID9 } from "crypto";
2821
+
2822
+ // ../shared/src/workspace-chat.ts
2823
+ var WORKSPACE_CHATS_QUERY_SCHEMA_VERSION = 1;
2824
+ var WORKSPACE_CHATS_QUERY_ROOT = `workspace-chats:v${WORKSPACE_CHATS_QUERY_SCHEMA_VERSION}`;
2825
+ function isAcceptedUserMessage(event) {
2826
+ return getUserMessage(event) !== null && event.payload.source === ACCEPTED_USER_MESSAGE_SOURCE;
2827
+ }
2828
+ function getComparableUserMessage(event) {
2829
+ const message = getUserMessage(event);
2830
+ if (message !== null) return message;
2831
+ const payloadMessage = event.payload.message;
2832
+ if (event.type !== "claude-user" || typeof payloadMessage !== "object" || payloadMessage === null || !("content" in payloadMessage)) {
2833
+ return null;
2834
+ }
2835
+ return extractToolResultText(payloadMessage.content) || null;
2836
+ }
2837
+ function areDuplicateUserMessages(a, b) {
2838
+ if (areSameUserMessageEvents(a, b)) return true;
2839
+ const aMessage = getComparableUserMessage(a);
2840
+ const bMessage = getComparableUserMessage(b);
2841
+ if (!aMessage || aMessage !== bMessage) return false;
2842
+ const aAccepted = isAcceptedUserMessage(a);
2843
+ const bAccepted = isAcceptedUserMessage(b);
2844
+ return aAccepted !== bAccepted ? areUserMessagesWithinMatchWindow(
2845
+ { content: aMessage, timestamp: a.timestamp },
2846
+ { content: bMessage, timestamp: b.timestamp }
2847
+ ) : false;
2848
+ }
2849
+ var eventJsonCache = /* @__PURE__ */ new WeakMap();
2850
+ function workspaceChatEventSignals(event) {
2851
+ let json = eventJsonCache.get(event);
2852
+ if (json === void 0) {
2853
+ json = JSON.stringify(event);
2854
+ eventJsonCache.set(event, json);
2855
+ }
2856
+ return {
2857
+ streamId: getClaudePartialMessageStreamId(event),
2858
+ json,
2859
+ userMessage: getComparableUserMessage(event),
2860
+ userMessageId: getUserMessageId(event)
2861
+ };
2862
+ }
2863
+ function indexMergedEvent(index, event, position) {
2864
+ const { streamId, json, userMessage, userMessageId } = workspaceChatEventSignals(event);
2865
+ if (streamId !== null && !index.byStreamId.has(streamId)) {
2866
+ index.byStreamId.set(streamId, position);
2867
+ }
2868
+ if (!index.byJson.has(json)) index.byJson.set(json, position);
2869
+ if (userMessageId !== null && !index.byUserMessageId.has(userMessageId)) {
2870
+ index.byUserMessageId.set(userMessageId, position);
2871
+ }
2872
+ if (userMessage !== null) {
2873
+ const bucket = index.byUserMessage.get(userMessage);
2874
+ if (!bucket) {
2875
+ index.byUserMessage.set(userMessage, [{ position, event }]);
2876
+ return;
2877
+ }
2878
+ const insertAt = bucket.findIndex((entry) => entry.position > position);
2879
+ bucket.splice(insertAt === -1 ? bucket.length : insertAt, 0, { position, event });
2880
+ }
2881
+ }
2882
+ function reindexReplacedEvent(index, previous, event, position) {
2883
+ const { streamId, json, userMessage, userMessageId } = workspaceChatEventSignals(previous);
2884
+ if (streamId !== null && index.byStreamId.get(streamId) === position) {
2885
+ index.byStreamId.delete(streamId);
2886
+ }
2887
+ if (index.byJson.get(json) === position) index.byJson.delete(json);
2888
+ if (userMessageId !== null && index.byUserMessageId.get(userMessageId) === position) {
2889
+ index.byUserMessageId.delete(userMessageId);
2890
+ }
2891
+ if (userMessage !== null) {
2892
+ const bucket = index.byUserMessage.get(userMessage) ?? [];
2893
+ const entryIndex = bucket.findIndex((entry) => entry.position === position);
2894
+ if (entryIndex !== -1) bucket.splice(entryIndex, 1);
2895
+ if (bucket.length === 0) {
2896
+ index.byUserMessage.delete(userMessage);
2897
+ }
2898
+ }
2899
+ indexMergedEvent(index, event, position);
2900
+ }
2901
+ function findDuplicatePosition(index, event) {
2902
+ const { streamId, json, userMessage, userMessageId } = workspaceChatEventSignals(event);
2903
+ let match = -1;
2904
+ const consider = (position) => {
2905
+ if (position !== void 0 && (match === -1 || position < match)) match = position;
2906
+ };
2907
+ if (streamId !== null) consider(index.byStreamId.get(streamId));
2908
+ consider(index.byJson.get(json));
2909
+ if (userMessageId !== null) consider(index.byUserMessageId.get(userMessageId));
2910
+ if (userMessage !== null) {
2911
+ for (const entry of index.byUserMessage.get(userMessage) ?? []) {
2912
+ if (areDuplicateUserMessages(entry.event, event)) {
2913
+ consider(entry.position);
2914
+ break;
2915
+ }
2916
+ }
2917
+ }
2918
+ return match;
2919
+ }
2920
+ function mergeWorkspaceChatHistoryEvents(primary, supplemental) {
2921
+ if (supplemental.length === 0) return [...primary];
2922
+ const merged = [...primary];
2923
+ const index = {
2924
+ byStreamId: /* @__PURE__ */ new Map(),
2925
+ byJson: /* @__PURE__ */ new Map(),
2926
+ byUserMessageId: /* @__PURE__ */ new Map(),
2927
+ byUserMessage: /* @__PURE__ */ new Map()
2928
+ };
2929
+ for (let position = 0; position < merged.length; position++) {
2930
+ indexMergedEvent(index, merged[position], position);
2931
+ }
2932
+ for (const event of supplemental) {
2933
+ const position = findDuplicatePosition(index, event);
2934
+ const messageId = getUserMessageId(event);
2935
+ if (position === -1) {
2936
+ indexMergedEvent(index, event, merged.length);
2937
+ merged.push(event);
2938
+ } else if (getClaudePartialMessageStreamId(event) !== null || isAcceptedUserMessage(merged[position]) && !isAcceptedUserMessage(event) || messageId !== null && messageId === getUserMessageId(merged[position])) {
2939
+ const previous = merged[position];
2940
+ merged[position] = event;
2941
+ reindexReplacedEvent(index, previous, event, position);
2942
+ }
2943
+ }
2944
+ return merged;
2945
+ }
2818
2946
 
2819
2947
  // src/managers/claude-manager.ts
2820
2948
  import {
2821
2949
  query
2822
2950
  } from "@anthropic-ai/claude-agent-sdk";
2823
- import { randomUUID as randomUUID4 } from "crypto";
2951
+ import { randomUUID as randomUUID5 } from "crypto";
2824
2952
  import { dirname as dirname4, join as join17 } from "path";
2825
2953
  import { mkdir as mkdir10 } from "fs/promises";
2826
2954
  import { homedir as homedir12 } from "os";
@@ -3206,7 +3334,7 @@ function extractPlanFromCodexAspNotification(notification) {
3206
3334
  }
3207
3335
 
3208
3336
  // src/utils/image-utils.ts
3209
- import { randomUUID as randomUUID3 } from "crypto";
3337
+ import { randomUUID as randomUUID4 } from "crypto";
3210
3338
  import { mkdir as mkdir9, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
3211
3339
  import { homedir as homedir11 } from "os";
3212
3340
  import { join as join14 } from "path";
@@ -3315,7 +3443,7 @@ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join14(hom
3315
3443
  try {
3316
3444
  for (const image of images) {
3317
3445
  const ext = image.source.media_type.split("/")[1] || "png";
3318
- const filename = `img_${randomUUID3()}.${ext}`;
3446
+ const filename = `img_${randomUUID4()}.${ext}`;
3319
3447
  const filepath = join14(tempImageDir, filename);
3320
3448
  await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
3321
3449
  tempPaths.push(filepath);
@@ -3385,6 +3513,12 @@ var MessageQueueService = class {
3385
3513
  onProcessingChanged;
3386
3514
  onMessageStarted;
3387
3515
  acceptanceByMessage = /* @__PURE__ */ new WeakMap();
3516
+ // A withdrawal mark implies the entry was present when its acceptance
3517
+ // rejected; intentional removals (remove, clear, drain, steer) can never
3518
+ // coincide with it, so recovery only needs this mark plus the cancellation
3519
+ // version below.
3520
+ acceptanceWithdrawals = /* @__PURE__ */ new WeakSet();
3521
+ queueCancellationVersion = 0;
3388
3522
  constructor(processMessage, onProcessingChanged = () => {
3389
3523
  }, onMessageStarted = async () => {
3390
3524
  }) {
@@ -3403,13 +3537,32 @@ var MessageQueueService = class {
3403
3537
  }) {
3404
3538
  if (this.processing && this.canMergeIntoTail(request)) {
3405
3539
  const tail = this.queue[this.queue.length - 1];
3406
- this.mergeInto(tail, request);
3407
3540
  const response2 = {
3408
3541
  queued: true,
3409
3542
  messageId: tail.id,
3410
3543
  position: this.queue.length
3411
3544
  };
3412
- const acceptance = (this.acceptanceByMessage.get(tail) ?? Promise.resolve()).then(() => onAccepted(response2));
3545
+ const cancellationVersion = this.queueCancellationVersion;
3546
+ const prior = this.acceptanceByMessage.get(tail) ?? Promise.resolve();
3547
+ const acceptance = prior.catch(() => {
3548
+ }).then(async () => {
3549
+ let acceptedMessage;
3550
+ while (true) {
3551
+ const baseMessage = tail.message;
3552
+ acceptedMessage = {
3553
+ ...tail,
3554
+ message: `${baseMessage}${MERGED_MESSAGE_SEPARATOR}${request.message}`,
3555
+ ...tail.images || request.images?.length ? { images: [...tail.images ?? [], ...request.images ?? []] } : {}
3556
+ };
3557
+ await onAccepted(response2, acceptedMessage);
3558
+ if (tail.message === baseMessage) break;
3559
+ }
3560
+ tail.message = acceptedMessage.message;
3561
+ if (acceptedMessage.images) tail.images = [...acceptedMessage.images];
3562
+ if (!this.queue.includes(tail) && this.queueCancellationVersion === cancellationVersion && this.acceptanceWithdrawals.delete(tail)) {
3563
+ this.queue.push(tail);
3564
+ }
3565
+ });
3413
3566
  this.acceptanceByMessage.set(tail, acceptance);
3414
3567
  await acceptance;
3415
3568
  return response2;
@@ -3430,7 +3583,14 @@ var MessageQueueService = class {
3430
3583
  messageId,
3431
3584
  position: this.queue.length
3432
3585
  };
3433
- const acceptance = onAccepted(response2);
3586
+ const acceptance = onAccepted(response2, queuedMessage).catch((error) => {
3587
+ const index = this.queue.indexOf(queuedMessage);
3588
+ if (index !== -1) {
3589
+ this.queue.splice(index, 1);
3590
+ this.acceptanceWithdrawals.add(queuedMessage);
3591
+ }
3592
+ throw error;
3593
+ });
3434
3594
  this.acceptanceByMessage.set(queuedMessage, acceptance);
3435
3595
  await acceptance;
3436
3596
  return response2;
@@ -3440,7 +3600,7 @@ var MessageQueueService = class {
3440
3600
  messageId,
3441
3601
  position: 0
3442
3602
  };
3443
- await onAccepted(response);
3603
+ await onAccepted(response, queuedMessage);
3444
3604
  this.startProcessing(queuedMessage).catch((error) => {
3445
3605
  console.error("[MessageQueue] Unhandled error in startProcessing:", error);
3446
3606
  });
@@ -3452,12 +3612,6 @@ var MessageQueueService = class {
3452
3612
  if (!tail) return false;
3453
3613
  return tail.merge === true && tail.type === request.type;
3454
3614
  }
3455
- mergeInto(tail, request) {
3456
- tail.message = `${tail.message}${MERGED_MESSAGE_SEPARATOR}${request.message}`;
3457
- if (request.images && request.images.length > 0) {
3458
- tail.images = [...tail.images ?? [], ...request.images];
3459
- }
3460
- }
3461
3615
  async startProcessing(queuedMessage) {
3462
3616
  this.processing = true;
3463
3617
  this.onProcessingChanged(true);
@@ -3506,6 +3660,7 @@ var MessageQueueService = class {
3506
3660
  drained.push(text);
3507
3661
  totalChars += text.length;
3508
3662
  }
3663
+ this.queueCancellationVersion++;
3509
3664
  this.queue = [];
3510
3665
  return drained;
3511
3666
  }
@@ -3526,9 +3681,10 @@ var MessageQueueService = class {
3526
3681
  return true;
3527
3682
  }
3528
3683
  clearQueue() {
3529
- if (this.queue.length === 0) return false;
3684
+ const hadMessages = this.queue.length > 0;
3685
+ this.queueCancellationVersion++;
3530
3686
  this.queue = [];
3531
- return true;
3687
+ return hadMessages;
3532
3688
  }
3533
3689
  updateQueuedMessage(messageId, message) {
3534
3690
  const queuedMessage = this.queue.find((item) => item.id === messageId);
@@ -3569,6 +3725,7 @@ var MessageQueueService = class {
3569
3725
  * Reset everything including clearing processing state
3570
3726
  */
3571
3727
  reset() {
3728
+ this.queueCancellationVersion++;
3572
3729
  this.queue = [];
3573
3730
  this.processing = false;
3574
3731
  }
@@ -3647,6 +3804,21 @@ var CodingAgentManager = class {
3647
3804
  this.onEvent(event);
3648
3805
  historyFile.append(event);
3649
3806
  }
3807
+ recordUserMessageHistoryEvent(request, historyFile, extraPayload = {}) {
3808
+ this.recordHistoryEvent(
3809
+ "event_msg",
3810
+ this.createUserMessageHistoryPayload(request, extraPayload),
3811
+ historyFile
3812
+ );
3813
+ }
3814
+ createUserMessageHistoryPayload(request, extraPayload = {}) {
3815
+ return {
3816
+ type: "user_message",
3817
+ message: request.message,
3818
+ ...extraPayload,
3819
+ ...request.messageId ? { [USER_MESSAGE_ID_PAYLOAD_KEY]: request.messageId } : {}
3820
+ };
3821
+ }
3650
3822
  initializeManager(processMessage) {
3651
3823
  this.messageQueue = new MessageQueueService(
3652
3824
  processMessage,
@@ -3655,6 +3827,11 @@ var CodingAgentManager = class {
3655
3827
  );
3656
3828
  this.initialized = this.initialize();
3657
3829
  }
3830
+ async persistAcceptedMessage(event) {
3831
+ const history = this.getHistorySink();
3832
+ if (!history?.appendDurably) throw new Error("Chat history is unavailable");
3833
+ await history.appendDurably(event);
3834
+ }
3658
3835
  async interrupt() {
3659
3836
  const queue = this.messageQueue.drainQueue({
3660
3837
  maxItems: MAX_INTERRUPT_QUEUE_ITEMS,
@@ -4262,12 +4439,13 @@ var CodexHistoryFile = class {
4262
4439
  writeLock = new AsyncLock();
4263
4440
  /** Best-effort ordered append; failures must not disrupt the turn. */
4264
4441
  append(event) {
4265
- void this.writeLock.run(
4266
- () => appendFile2(this.filePath, `${JSON.stringify(event)}
4267
- `).catch((error) => {
4268
- console.error("[CodexHistoryFile] Failed to append event:", error);
4269
- })
4270
- );
4442
+ void this.appendDurably(event).catch((error) => {
4443
+ console.error("[CodexHistoryFile] Failed to append event:", error);
4444
+ });
4445
+ }
4446
+ appendDurably(event) {
4447
+ return this.writeLock.run(() => appendFile2(this.filePath, `${JSON.stringify(event)}
4448
+ `));
4271
4449
  }
4272
4450
  async flush() {
4273
4451
  await this.writeLock.drain();
@@ -4791,7 +4969,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
4791
4969
  if (!handler) {
4792
4970
  return { behavior: "allow" };
4793
4971
  }
4794
- const requestId = randomUUID4();
4972
+ const requestId = randomUUID5();
4795
4973
  const toolUseId = options.toolUseID;
4796
4974
  const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
4797
4975
  const result = await new Promise((resolve5) => {
@@ -6462,6 +6640,13 @@ var DuplicateDefaultChatError = class extends Error {
6462
6640
  this.name = "DuplicateDefaultChatError";
6463
6641
  }
6464
6642
  };
6643
+ var ChatMessagePersistenceError = class extends Error {
6644
+ constructor(cause) {
6645
+ super(`Failed to persist user message: ${cause instanceof Error ? cause.message : String(cause)}`);
6646
+ this.name = "ChatMessagePersistenceError";
6647
+ this.cause = cause;
6648
+ }
6649
+ };
6465
6650
 
6466
6651
  // src/managers/codex-asp/codex-asp-manager.ts
6467
6652
  var CodexQuotaError = class extends Error {
@@ -6571,7 +6756,13 @@ var CodexAspManager = class extends CodingAgentManager {
6571
6756
  this.currentThreadId = this.initialSessionId ?? replayed?.transcript?.threadId ?? null;
6572
6757
  }
6573
6758
  getHistorySink() {
6574
- return { append: (event) => this.trackHistoryEvent(event) };
6759
+ return {
6760
+ append: (event) => this.trackHistoryEvent(event),
6761
+ appendDurably: (event) => {
6762
+ if (!this.historyFile) throw new Error("Chat history is unavailable");
6763
+ return this.historyFile.appendDurably(event);
6764
+ }
6765
+ };
6575
6766
  }
6576
6767
  async interruptActiveTurn() {
6577
6768
  if (!this.currentThreadId || !this.activeTurnId) {
@@ -6769,13 +6960,10 @@ var CodexAspManager = class extends CodingAgentManager {
6769
6960
  }
6770
6961
  recordUserMessageEvent(request, extraPayload = {}) {
6771
6962
  const images = imageContentToUserMessageImages(request.images);
6772
- this.recordCodexHistoryEvent("event_msg", {
6773
- type: "user_message",
6774
- message: request.message,
6775
- ...request.messageId ? { [USER_MESSAGE_ID_PAYLOAD_KEY]: request.messageId } : {},
6963
+ this.recordCodexHistoryEvent("event_msg", this.createUserMessageHistoryPayload(request, {
6776
6964
  ...images ? { images } : {},
6777
6965
  ...extraPayload
6778
- });
6966
+ }));
6779
6967
  }
6780
6968
  async processMessageInternal(request) {
6781
6969
  let userMessageRecorded = false;
@@ -8088,10 +8276,7 @@ var CursorManager = class extends CodingAgentManager {
8088
8276
  }
8089
8277
  }, CURSOR_RECONNECT_NOTICE_DELAY_MS);
8090
8278
  agent = await this.ensureAgent(request);
8091
- this.recordHistoryEvent("event_msg", {
8092
- type: "user_message",
8093
- message: request.message
8094
- }, this.historyFile);
8279
+ this.recordUserMessageHistoryEvent(request, this.historyFile);
8095
8280
  run = await agent.send(message, {
8096
8281
  model: { id: model },
8097
8282
  mode: request.planMode ? "plan" : "agent",
@@ -8337,7 +8522,7 @@ ${instructions}
8337
8522
 
8338
8523
  // src/managers/deepseek-manager.ts
8339
8524
  import { spawn as spawn3 } from "child_process";
8340
- import { randomUUID as randomUUID5 } from "crypto";
8525
+ import { randomUUID as randomUUID6 } from "crypto";
8341
8526
  import { createRequire } from "module";
8342
8527
  import { dirname as dirname6, join as join21 } from "path";
8343
8528
  import { fileURLToPath } from "url";
@@ -8379,7 +8564,7 @@ var DeepseekApiClient = class extends AbstractApiClient {
8379
8564
  return this.readWebSocket("/api/events.host", signal, hostFrameSchema, onOpen);
8380
8565
  }
8381
8566
  async executeCommand(sessionId, line) {
8382
- const rpcId = randomUUID5();
8567
+ const rpcId = randomUUID6();
8383
8568
  const response = await this.doFetch(new URL("/api/commands/execute", this.baseUrl), {
8384
8569
  method: "POST",
8385
8570
  headers: { "content-type": "application/json" },
@@ -8492,7 +8677,7 @@ var DeepseekManager = class extends CodingAgentManager {
8492
8677
  mode: "steer",
8493
8678
  content: [{ type: "text", text: request.message }]
8494
8679
  }));
8495
- this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
8680
+ this.recordUserMessageHistoryEvent(request, this.historyFile);
8496
8681
  return true;
8497
8682
  }
8498
8683
  async getHistory(page = {}) {
@@ -8706,7 +8891,7 @@ var DeepseekManager = class extends CodingAgentManager {
8706
8891
  ...effort ? { reasoningEffort: effort } : {}
8707
8892
  }));
8708
8893
  }
8709
- this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
8894
+ this.recordUserMessageHistoryEvent(request, this.historyFile);
8710
8895
  this.turnCompletion = new Promise((resolve5) => {
8711
8896
  this.resolveTurnCompletion = resolve5;
8712
8897
  });
@@ -8747,7 +8932,7 @@ import { spawn as spawn5 } from "child_process";
8747
8932
 
8748
8933
  // src/managers/acp-manager.ts
8749
8934
  import { spawn as spawn4 } from "child_process";
8750
- import { randomUUID as randomUUID6 } from "crypto";
8935
+ import { randomUUID as randomUUID7 } from "crypto";
8751
8936
  import { mkdir as mkdir13, readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
8752
8937
  import { dirname as dirname7 } from "path";
8753
8938
  import { Writable } from "stream";
@@ -8920,7 +9105,7 @@ var AcpManager = class extends CodingAgentManager {
8920
9105
  createTerminal(params) {
8921
9106
  this.assertSession(params.sessionId);
8922
9107
  if (this.planMode) throw new Error("ACP terminals are disabled in plan mode.");
8923
- const terminalId = randomUUID6();
9108
+ const terminalId = randomUUID7();
8924
9109
  const outputByteLimit = Math.max(1, Math.min(params.outputByteLimit ?? 1048576, 10485760));
8925
9110
  const child = spawn4(params.command, params.args ?? [], {
8926
9111
  cwd: params.cwd ?? this.workingDirectory,
@@ -9051,7 +9236,7 @@ var AcpManager = class extends CodingAgentManager {
9051
9236
  }
9052
9237
  async requestUserInput(params) {
9053
9238
  if (!this.interactiveTools) return { outcome: { outcome: "cancelled" } };
9054
- const requestId = randomUUID6();
9239
+ const requestId = randomUUID7();
9055
9240
  const toolCallId = params.toolCall.toolCallId;
9056
9241
  return new Promise((resolve5) => {
9057
9242
  this.pendingPermissions.set(requestId, { toolCallId, options: params.options, resolve: resolve5 });
@@ -9112,7 +9297,7 @@ var AcpManager = class extends CodingAgentManager {
9112
9297
  const context = this.context;
9113
9298
  const sessionId = this.sessionId;
9114
9299
  if (!context || !sessionId) throw new Error(`${this.acp.command} ACP failed to initialize.`);
9115
- this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
9300
+ this.recordUserMessageHistoryEvent(request, this.historyFile);
9116
9301
  this.planMode = request.planMode ?? false;
9117
9302
  this.interactiveTools = request.enableInteractiveTools ?? false;
9118
9303
  await this.setConfig("model", model);
@@ -9419,17 +9604,21 @@ async function getDefaultOpencodeModel(provider) {
9419
9604
  function getOpenCodeGoModel(model) {
9420
9605
  return model.replace(/^[^/]+\//, "");
9421
9606
  }
9422
- function opencodeConfig(provider, model) {
9607
+ function opencodeConfig(provider, model, providerModels = [model]) {
9423
9608
  let config;
9424
9609
  if (provider === OPENCODE_GO_PROVIDER) {
9425
9610
  config = {
9426
9611
  enabled_providers: [OPENCODE_GO_PROVIDER],
9427
9612
  model: `${OPENCODE_GO_PROVIDER}/${model}`,
9613
+ provider: {
9614
+ [OPENCODE_GO_PROVIDER]: {
9615
+ models: Object.fromEntries(providerModels.map((candidate) => [candidate, {}]))
9616
+ }
9617
+ },
9428
9618
  permission: OPENCODE_WORKSPACE_PERMISSION,
9429
9619
  share: "disabled"
9430
9620
  };
9431
9621
  } else if (provider === ASTER_PROVIDER) {
9432
- const models = getConfiguredOpencodeModels(model, provider);
9433
9622
  config = {
9434
9623
  enabled_providers: [ASTER_PROVIDER],
9435
9624
  model: `${ASTER_PROVIDER}/${model}`,
@@ -9438,20 +9627,19 @@ function opencodeConfig(provider, model) {
9438
9627
  npm: "@ai-sdk/openai-compatible",
9439
9628
  name: "Aster",
9440
9629
  options: { baseURL: ASTER_BASE_URL },
9441
- models: Object.fromEntries(models.map((candidate) => [candidate, {}]))
9630
+ models: Object.fromEntries(providerModels.map((candidate) => [candidate, {}]))
9442
9631
  }
9443
9632
  },
9444
9633
  permission: OPENCODE_WORKSPACE_PERMISSION,
9445
9634
  share: "disabled"
9446
9635
  };
9447
9636
  } else {
9448
- const models = getConfiguredOpencodeModels(model, provider);
9449
9637
  config = {
9450
9638
  enabled_providers: ["openrouter"],
9451
9639
  model: `openrouter/${model}`,
9452
9640
  provider: {
9453
9641
  openrouter: {
9454
- models: Object.fromEntries(models.map((candidate) => [candidate, {}]))
9642
+ models: Object.fromEntries(providerModels.map((candidate) => [candidate, {}]))
9455
9643
  }
9456
9644
  },
9457
9645
  permission: OPENCODE_WORKSPACE_PERMISSION,
@@ -9460,9 +9648,6 @@ function opencodeConfig(provider, model) {
9460
9648
  }
9461
9649
  return config;
9462
9650
  }
9463
- function getConfiguredOpencodeModels(model, provider) {
9464
- return [.../* @__PURE__ */ new Set([model, ...provider === ASTER_PROVIDER ? ASTER_MODELS : []])];
9465
- }
9466
9651
  function opencodeCommandToSlashCommand(command) {
9467
9652
  return createProviderSlashCommand("opencode", command.name, command.description);
9468
9653
  }
@@ -9592,6 +9777,7 @@ var OpencodeManager = class extends CodingAgentManager {
9592
9777
  historyFile;
9593
9778
  activeAbortController = null;
9594
9779
  configuredModels = /* @__PURE__ */ new Set();
9780
+ clientUpdate = Promise.resolve();
9595
9781
  providerId = "openrouter";
9596
9782
  eventAbortController = null;
9597
9783
  eventSubscriptionReady = Promise.resolve();
@@ -9656,7 +9842,7 @@ var OpencodeManager = class extends CodingAgentManager {
9656
9842
  this.slashCommandsRequest ??= (async () => {
9657
9843
  try {
9658
9844
  const provider = await getOpencodeProvider();
9659
- const client2 = await this.ensureClient(await getDefaultOpencodeModel(provider), provider);
9845
+ const client2 = await this.ensureClient(await getDefaultOpencodeModel(provider), provider, true);
9660
9846
  const directories = [this.workingDirectory, ...await getAgentAdditionalDirectories()];
9661
9847
  const perDirectory = await Promise.all(directories.map(async (directory) => {
9662
9848
  try {
@@ -9686,68 +9872,80 @@ var OpencodeManager = class extends CodingAgentManager {
9686
9872
  })();
9687
9873
  return this.slashCommandsRequest;
9688
9874
  }
9689
- async ensureClient(model, providerId) {
9690
- if (this.client && this.providerId !== providerId) {
9875
+ async ensureClient(model, providerId, reuseExisting = false) {
9876
+ const previousUpdate = this.clientUpdate;
9877
+ let finishUpdate = () => {
9878
+ };
9879
+ this.clientUpdate = new Promise((resolve5) => {
9880
+ finishUpdate = resolve5;
9881
+ });
9882
+ await previousUpdate;
9883
+ try {
9884
+ if (reuseExisting && this.client) return this.client;
9885
+ if (this.client && this.providerId !== providerId) {
9886
+ this.eventAbortController?.abort();
9887
+ this.server?.close();
9888
+ this.client = null;
9889
+ this.server = null;
9890
+ this.configuredModels.clear();
9891
+ }
9892
+ this.providerId = providerId;
9893
+ const configuredModel = providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
9894
+ const providerModels = providerId === OPENCODE_GO_PROVIDER ? Object.keys((await fetchOpenCodeGoCatalog()).models) : providerId === ASTER_PROVIDER ? ASTER_MODELS : await getAllowedOpenRouterModels();
9895
+ if (!providerModels.includes(configuredModel)) {
9896
+ throw new Error(`Model ${configuredModel} is not available through ${providerId}.`);
9897
+ }
9898
+ if (this.client && this.configuredModels.has(configuredModel)) {
9899
+ return this.client;
9900
+ }
9901
+ if (!await hasOpencodeCredentials(providerId)) {
9902
+ throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
9903
+ }
9691
9904
  this.eventAbortController?.abort();
9692
9905
  this.server?.close();
9693
- this.client = null;
9694
- this.server = null;
9695
- this.configuredModels.clear();
9696
- }
9697
- this.providerId = providerId;
9698
- const configuredModel = providerId === OPENCODE_GO_PROVIDER ? getOpenCodeGoModel(model) : model;
9699
- const providerModels = providerId === ASTER_PROVIDER ? ASTER_MODELS : providerId === "openrouter" ? await getAllowedOpenRouterModels() : null;
9700
- if (providerModels && !providerModels.includes(configuredModel)) {
9701
- throw new Error(`Model ${configuredModel} is not available through ${providerId}.`);
9702
- }
9703
- if (this.client && (providerId === OPENCODE_GO_PROVIDER || this.configuredModels.has(configuredModel))) {
9704
- return this.client;
9705
- }
9706
- if (!await hasOpencodeCredentials(providerId)) {
9707
- throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
9708
- }
9709
- this.eventAbortController?.abort();
9710
- this.server?.close();
9711
- const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
9712
- if (!pathEntries.includes(OPENCODE_SHIM_DIR)) {
9713
- process.env.PATH = [OPENCODE_SHIM_DIR, ...pathEntries].join(delimiter);
9714
- }
9715
- const password = process.env.OPENCODE_SERVER_PASSWORD || randomBytes2(24).toString("base64url");
9716
- process.env.OPENCODE_SERVER_PASSWORD = password;
9717
- const config = opencodeConfig(providerId, configuredModel);
9718
- const mcp = await readProvisionedOpencodeMcpConfig();
9719
- if (mcp && Object.keys(mcp).length > 0) config.mcp = mcp;
9720
- const server = await createOpencodeServer({
9721
- port: 0,
9722
- timeout: OPENCODE_SERVER_STARTUP_TIMEOUT_MS,
9723
- config
9724
- });
9725
- const client2 = createOpencodeClient({
9726
- baseUrl: server.url,
9727
- fetch: opencodeFetch,
9728
- headers: {
9729
- Authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
9730
- }
9731
- });
9732
- this.client = client2;
9733
- this.server = server;
9734
- this.configuredModels = this.providerId === OPENCODE_GO_PROVIDER ? /* @__PURE__ */ new Set() : new Set(getConfiguredOpencodeModels(model, providerId));
9735
- const eventController = new AbortController();
9736
- this.eventAbortController = eventController;
9737
- this.eventSubscriptionReady = new Promise((resolve5) => {
9738
- this.resolveEventSubscriptionReady = resolve5;
9739
- });
9740
- this.subscribeToEvents(client2, eventController).catch((error) => {
9741
- this.resolveEventSubscriptionReady?.();
9742
- this.resolveEventSubscriptionReady = null;
9743
- console.error("[OpencodeManager] Event subscription failed:", error);
9744
- this.recordHistoryEvent("opencode-error", opencodeErrorPayload(error), this.historyFile);
9745
- });
9746
- await Promise.race([
9747
- this.eventSubscriptionReady,
9748
- new Promise((resolve5) => setTimeout(resolve5, 2e3))
9749
- ]);
9750
- return client2;
9906
+ const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
9907
+ if (!pathEntries.includes(OPENCODE_SHIM_DIR)) {
9908
+ process.env.PATH = [OPENCODE_SHIM_DIR, ...pathEntries].join(delimiter);
9909
+ }
9910
+ const password = process.env.OPENCODE_SERVER_PASSWORD || randomBytes2(24).toString("base64url");
9911
+ process.env.OPENCODE_SERVER_PASSWORD = password;
9912
+ const config = opencodeConfig(providerId, configuredModel, providerModels);
9913
+ const mcp = await readProvisionedOpencodeMcpConfig();
9914
+ if (mcp && Object.keys(mcp).length > 0) config.mcp = mcp;
9915
+ const server = await createOpencodeServer({
9916
+ port: 0,
9917
+ timeout: OPENCODE_SERVER_STARTUP_TIMEOUT_MS,
9918
+ config
9919
+ });
9920
+ const client2 = createOpencodeClient({
9921
+ baseUrl: server.url,
9922
+ fetch: opencodeFetch,
9923
+ headers: {
9924
+ Authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
9925
+ }
9926
+ });
9927
+ this.client = client2;
9928
+ this.server = server;
9929
+ this.configuredModels = new Set(providerModels);
9930
+ const eventController = new AbortController();
9931
+ this.eventAbortController = eventController;
9932
+ this.eventSubscriptionReady = new Promise((resolve5) => {
9933
+ this.resolveEventSubscriptionReady = resolve5;
9934
+ });
9935
+ this.subscribeToEvents(client2, eventController).catch((error) => {
9936
+ this.resolveEventSubscriptionReady?.();
9937
+ this.resolveEventSubscriptionReady = null;
9938
+ console.error("[OpencodeManager] Event subscription failed:", error);
9939
+ this.recordHistoryEvent("opencode-error", opencodeErrorPayload(error), this.historyFile);
9940
+ });
9941
+ await Promise.race([
9942
+ this.eventSubscriptionReady,
9943
+ new Promise((resolve5) => setTimeout(resolve5, 2e3))
9944
+ ]);
9945
+ return client2;
9946
+ } finally {
9947
+ finishUpdate();
9948
+ }
9751
9949
  }
9752
9950
  async ensureSession(client2, model, agent, variant) {
9753
9951
  if (this.sessionId) {
@@ -9811,10 +10009,7 @@ var OpencodeManager = class extends CodingAgentManager {
9811
10009
  const variant = await this.getThinkingVariant(client2, providerModel, request.thinkingLevel);
9812
10010
  const sessionId = await this.ensureSession(client2, providerModel, agent, variant);
9813
10011
  const system = this.buildCombinedInstructions(request.customInstructions);
9814
- this.recordHistoryEvent("event_msg", {
9815
- type: "user_message",
9816
- message: request.message
9817
- }, this.historyFile);
10012
+ this.recordUserMessageHistoryEvent(request, this.historyFile);
9818
10013
  const result = await client2.session.prompt({
9819
10014
  sessionID: sessionId,
9820
10015
  directory: this.workingDirectory,
@@ -10248,7 +10443,7 @@ var PiManager = class extends CodingAgentManager {
10248
10443
  const session = this.session;
10249
10444
  if (!session?.isStreaming) return false;
10250
10445
  await session.steer(request.message);
10251
- this.recordUserMessage(session, request.message);
10446
+ this.recordUserMessage(session, request);
10252
10447
  return true;
10253
10448
  }
10254
10449
  async getHistory(page = {}) {
@@ -10373,18 +10568,18 @@ var PiManager = class extends CodingAgentManager {
10373
10568
  const payload = eventPayload(event);
10374
10569
  this.recordHistoryEvent(`pi-${event.type}`, payload, this.historyFile);
10375
10570
  }
10376
- recordUserMessage(session, message) {
10377
- const skillName = explicitSkillName(session, message);
10378
- this.recordHistoryEvent("event_msg", {
10379
- type: "user_message",
10380
- message,
10381
- ...skillName ? { skillName } : {}
10382
- }, this.historyFile);
10571
+ recordUserMessage(session, request) {
10572
+ const skillName = explicitSkillName(session, request.message);
10573
+ this.recordUserMessageHistoryEvent(
10574
+ request,
10575
+ this.historyFile,
10576
+ skillName ? { skillName } : {}
10577
+ );
10383
10578
  }
10384
10579
  async processMessageInternal(request) {
10385
10580
  try {
10386
10581
  const session = await this.ensureSession(request);
10387
- this.recordUserMessage(session, request.message);
10582
+ this.recordUserMessage(session, request);
10388
10583
  const images = request.images && request.images.length > 0 ? (await normalizeImages(request.images)).map((image) => ({
10389
10584
  type: "image",
10390
10585
  data: image.source.data,
@@ -11011,6 +11206,9 @@ var RelayManager = class {
11011
11206
  async enqueueMessage(request, onAccepted) {
11012
11207
  return this.inner.enqueueMessage(request, onAccepted);
11013
11208
  }
11209
+ persistAcceptedMessage(event) {
11210
+ return this.inner.persistAcceptedMessage(event);
11211
+ }
11014
11212
  async interrupt() {
11015
11213
  return this.inner.interrupt();
11016
11214
  }
@@ -11059,7 +11257,7 @@ import {
11059
11257
  unlink as unlink3
11060
11258
  } from "fs/promises";
11061
11259
  import { join as join24 } from "path";
11062
- import { randomUUID as randomUUID7 } from "crypto";
11260
+ import { randomUUID as randomUUID8 } from "crypto";
11063
11261
 
11064
11262
  // src/analytics/agent/activity/skill-mcp-call-extractor.ts
11065
11263
  var NON_MCP_SERVERS = /* @__PURE__ */ new Set(["claude", "cursor", "deepseek", "fx", "kimi", "opencode", "pi", "custom", "dynamic"]);
@@ -11280,7 +11478,7 @@ var AgentChatTurnActivityTracker = class {
11280
11478
  const credential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
11281
11479
  this.pendingMessages.delete(messageId);
11282
11480
  this.activeTurns.set(chatId, {
11283
- turnId: randomUUID7(),
11481
+ turnId: randomUUID8(),
11284
11482
  startedAtMs: Date.now(),
11285
11483
  provider,
11286
11484
  model: attributes.model ?? getDefaultAgentModel(provider),
@@ -12112,7 +12310,7 @@ var ChatService = class {
12112
12310
  workingDirectory;
12113
12311
  agentChatActivityTrackerService;
12114
12312
  chats = /* @__PURE__ */ new Map();
12115
- persistInFlight = false;
12313
+ persistPromise = null;
12116
12314
  persistQueued = false;
12117
12315
  async initialize() {
12118
12316
  await mkdir17(ENGINE_DIR2, { recursive: true });
@@ -12178,7 +12376,7 @@ var ChatService = class {
12178
12376
  }
12179
12377
  const existingDefault = isDefaultChat({ provider: request.provider, title }) ? Array.from(this.chats.values()).find((chat) => !chat.persisted.deletedAt && chat.persisted.provider === request.provider && isDefaultChat(chat.persisted)) : void 0;
12180
12378
  if (existingDefault) {
12181
- if (!request.id || hasChatStarted(this.toSummary(existingDefault))) {
12379
+ if (!request.id || existingDefault.acceptingMessages > 0 || hasChatStarted(this.toSummary(existingDefault))) {
12182
12380
  throw new DuplicateDefaultChatError(request.provider);
12183
12381
  }
12184
12382
  this.chats.delete(existingDefault.persisted.id);
@@ -12188,7 +12386,7 @@ var ChatService = class {
12188
12386
  throw new ChatNotFoundError(parentChatId);
12189
12387
  }
12190
12388
  const persisted = {
12191
- id: request.id ?? randomUUID8(),
12389
+ id: request.id ?? randomUUID9(),
12192
12390
  provider: request.provider,
12193
12391
  title,
12194
12392
  createdAt: now,
@@ -12210,42 +12408,94 @@ var ChatService = class {
12210
12408
  }
12211
12409
  async sendMessage(chatId, request) {
12212
12410
  const chat = this.requireChat(chatId);
12213
- if (request.idempotencyKey) {
12214
- const accepted = chat.acceptedSendResponses.get(request.idempotencyKey);
12411
+ const idempotencyKey = request.idempotencyKey ?? request.messageId;
12412
+ if (idempotencyKey) {
12413
+ const accepted = chat.acceptedSendResponses.get(idempotencyKey);
12215
12414
  if (accepted) {
12216
12415
  return accepted;
12217
12416
  }
12417
+ const accepting2 = chat.acceptingSendResponses.get(idempotencyKey);
12418
+ if (accepting2) return accepting2;
12218
12419
  }
12219
- return chat.provider.enqueueMessage(
12220
- request,
12221
- (result) => this.handleMessageAccepted(chatId, chat, request, result)
12222
- );
12223
- }
12224
- async handleMessageAccepted(chatId, chat, request, result) {
12225
- if (request.idempotencyKey) {
12226
- chat.acceptedSendResponses.set(request.idempotencyKey, result);
12227
- for (const key of chat.acceptedSendResponses.keys()) {
12228
- if (chat.acceptedSendResponses.size <= MAX_ACCEPTED_SEND_RESPONSES) break;
12229
- chat.acceptedSendResponses.delete(key);
12420
+ const accepting = (async () => {
12421
+ chat.acceptingMessages += 1;
12422
+ try {
12423
+ return await chat.provider.enqueueMessage(
12424
+ request,
12425
+ (result, acceptedMessage) => this.handleMessageAccepted(
12426
+ chatId,
12427
+ chat,
12428
+ request,
12429
+ acceptedMessage,
12430
+ result
12431
+ )
12432
+ );
12433
+ } finally {
12434
+ chat.acceptingMessages -= 1;
12435
+ }
12436
+ })();
12437
+ if (!idempotencyKey) return accepting;
12438
+ chat.acceptingSendResponses.set(idempotencyKey, accepting);
12439
+ try {
12440
+ return await accepting;
12441
+ } finally {
12442
+ if (chat.acceptingSendResponses.get(idempotencyKey) === accepting) {
12443
+ chat.acceptingSendResponses.delete(idempotencyKey);
12230
12444
  }
12231
- chat.persisted.acceptedSendResponses = Object.fromEntries(chat.acceptedSendResponses);
12232
12445
  }
12446
+ }
12447
+ async handleMessageAccepted(chatId, chat, request, acceptedMessage, result) {
12233
12448
  const submittedAt = request.submittedAt ?? (/* @__PURE__ */ new Date()).toISOString();
12234
12449
  const { event: acceptedEvent, sender: recordedSender } = createTurnMetadata(
12235
- request,
12450
+ acceptedMessage,
12236
12451
  result.messageId,
12237
12452
  submittedAt
12238
12453
  );
12454
+ try {
12455
+ await chat.provider.persistAcceptedMessage(acceptedEvent);
12456
+ } catch (error) {
12457
+ throw new ChatMessagePersistenceError(error);
12458
+ }
12459
+ const previousAcceptedSendResponses = new Map(chat.acceptedSendResponses);
12460
+ const previousPendingMessageIds = [...chat.pendingMessageIds];
12461
+ const previousAcceptedEvent = chat.acceptedUserEvents.get(result.messageId);
12462
+ const previousPersistedResponses = chat.persisted.acceptedSendResponses;
12463
+ const previousLastMessageText = chat.persisted.lastMessageText;
12464
+ const previousUpdatedAt = chat.persisted.updatedAt;
12465
+ const idempotencyKey = request.idempotencyKey ?? request.messageId;
12466
+ if (idempotencyKey) {
12467
+ chat.acceptedSendResponses.set(idempotencyKey, result);
12468
+ for (const key of chat.acceptedSendResponses.keys()) {
12469
+ if (chat.acceptedSendResponses.size <= MAX_ACCEPTED_SEND_RESPONSES) break;
12470
+ chat.acceptedSendResponses.delete(key);
12471
+ }
12472
+ chat.persisted.acceptedSendResponses = Object.fromEntries(chat.acceptedSendResponses);
12473
+ }
12239
12474
  if (!chat.pendingMessageIds.includes(result.messageId)) {
12240
12475
  chat.pendingMessageIds.push(result.messageId);
12241
12476
  }
12477
+ chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
12478
+ chat.persisted.lastMessageText = request.message.trim().slice(0, LAST_MESSAGE_PREVIEW_MAX) || null;
12479
+ chat.persisted.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
12480
+ try {
12481
+ await this.persistAllChats();
12482
+ } catch (error) {
12483
+ chat.acceptedSendResponses.clear();
12484
+ for (const [key, response] of previousAcceptedSendResponses) {
12485
+ chat.acceptedSendResponses.set(key, response);
12486
+ }
12487
+ chat.pendingMessageIds = previousPendingMessageIds;
12488
+ if (previousAcceptedEvent) chat.acceptedUserEvents.set(result.messageId, previousAcceptedEvent);
12489
+ else chat.acceptedUserEvents.delete(result.messageId);
12490
+ chat.persisted.acceptedSendResponses = previousPersistedResponses;
12491
+ chat.persisted.lastMessageText = previousLastMessageText;
12492
+ chat.persisted.updatedAt = previousUpdatedAt;
12493
+ throw new ChatMessagePersistenceError(error);
12494
+ }
12242
12495
  this.agentChatActivityTrackerService.noteMessageAccepted(result.messageId, request);
12243
12496
  if (request.errorNotificationTarget) {
12244
12497
  chat.errorNotificationTargets.set(result.messageId, request.errorNotificationTarget);
12245
12498
  }
12246
- chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
12247
- chat.persisted.lastMessageText = request.message.trim().slice(0, LAST_MESSAGE_PREVIEW_MAX) || null;
12248
- this.touch(chat);
12249
12499
  if (recordedSender) {
12250
12500
  await this.appendSender(chatId, recordedSender);
12251
12501
  }
@@ -12522,15 +12772,16 @@ var ChatService = class {
12522
12772
  chat.provider.getHistory(page),
12523
12773
  this.readSenders(chatId)
12524
12774
  ]);
12775
+ const historyEvents = mergeWorkspaceChatHistoryEvents([], history.events);
12525
12776
  for (const [messageId, acceptedEvent] of chat.acceptedUserEvents) {
12526
- if (acceptedEventInCodexTranscript(acceptedEvent, history.codexAspTranscript) || history.events.some((event) => isSameAcceptedUserEvent(event, acceptedEvent))) {
12777
+ if (acceptedEventInCodexTranscript(acceptedEvent, history.codexAspTranscript) || historyEvents.some((event) => isSameAcceptedUserEvent(event, acceptedEvent))) {
12527
12778
  chat.acceptedUserEvents.delete(messageId);
12528
12779
  }
12529
12780
  }
12530
12781
  const isLatestPage = isLatestChatHistoryPage(page);
12531
12782
  const queuedMessageIds = new Set(chat.provider.getQueue().map((message) => message.id));
12532
- const acceptedEvents = (isLatestPage ? [...chat.acceptedUserEvents.entries()] : []).filter(([messageId]) => messageId === chat.activeMessageId || !chat.hasActiveTurn && chat.pendingMessageIds[0] === messageId && !queuedMessageIds.has(messageId)).map(([, acceptedEvent]) => acceptedEvent).filter((acceptedEvent) => !acceptedEventInCodexTranscript(acceptedEvent, history.codexAspTranscript) && !history.events.some((event) => isSameAcceptedUserEvent(event, acceptedEvent)));
12533
- const events = [...history.events, ...acceptedEvents].sort((a, b) => getEventTimestampMs(a) - getEventTimestampMs(b));
12783
+ const acceptedEvents = (isLatestPage ? [...chat.acceptedUserEvents.entries()] : []).filter(([messageId]) => messageId === chat.activeMessageId || !chat.hasActiveTurn && chat.pendingMessageIds[0] === messageId && !queuedMessageIds.has(messageId)).map(([, acceptedEvent]) => acceptedEvent).filter((acceptedEvent) => !acceptedEventInCodexTranscript(acceptedEvent, history.codexAspTranscript) && !historyEvents.some((event) => isSameAcceptedUserEvent(event, acceptedEvent)));
12784
+ const events = [...historyEvents, ...acceptedEvents].sort((a, b) => getEventTimestampMs(a) - getEventTimestampMs(b));
12534
12785
  if (page.limit !== void 0 && "beforeCursor" in history) {
12535
12786
  const cursor = parseChatHistoryCursor(history.beforeCursor ?? void 0);
12536
12787
  return {
@@ -12548,7 +12799,7 @@ var ChatService = class {
12548
12799
  return {
12549
12800
  ...history,
12550
12801
  events,
12551
- totalEvents: (history.totalEvents ?? history.eventsStartIndex + history.events.length) + acceptedEvents.length,
12802
+ totalEvents: (history.totalEvents ?? history.eventsStartIndex + history.events.length) - (history.events.length - historyEvents.length) + acceptedEvents.length,
12552
12803
  goal: history.goal ?? chat.provider.getGoal?.() ?? null,
12553
12804
  senders
12554
12805
  };
@@ -12747,6 +12998,8 @@ var ChatService = class {
12747
12998
  pendingMessageIds: [],
12748
12999
  acceptedUserEvents: /* @__PURE__ */ new Map(),
12749
13000
  acceptedSendResponses: new Map(Object.entries(persisted.acceptedSendResponses ?? {})),
13001
+ acceptingSendResponses: /* @__PURE__ */ new Map(),
13002
+ acceptingMessages: 0,
12750
13003
  activeMessageId: null,
12751
13004
  hasActiveTurn: false,
12752
13005
  observedBranchesByRepo: /* @__PURE__ */ new Map(),
@@ -12952,35 +13205,33 @@ var ChatService = class {
12952
13205
  }
12953
13206
  }
12954
13207
  }
12955
- async persistAllChats() {
12956
- if (this.persistInFlight) {
13208
+ persistAllChats() {
13209
+ if (this.persistPromise) {
12957
13210
  this.persistQueued = true;
12958
- return;
13211
+ return this.persistPromise;
12959
13212
  }
12960
- this.persistInFlight = true;
12961
- try {
12962
- const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
12963
- try {
12964
- await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
12965
- } catch (error) {
12966
- if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
12967
- console.error("[ChatService] Failed to update chats backup:", error);
13213
+ this.persistPromise = (async () => {
13214
+ do {
13215
+ this.persistQueued = false;
13216
+ const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
13217
+ try {
13218
+ await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
13219
+ } catch (error) {
13220
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
13221
+ console.error("[ChatService] Failed to update chats backup:", error);
13222
+ }
12968
13223
  }
12969
- }
12970
- await atomicWriteFile(CHATS_FILE, `${JSON.stringify(payload, null, 2)}
13224
+ await atomicWriteFile(CHATS_FILE, `${JSON.stringify(payload, null, 2)}
12971
13225
  `);
12972
- } catch {
12973
- } finally {
12974
- this.persistInFlight = false;
12975
- if (this.persistQueued) {
12976
- this.persistQueued = false;
12977
- void this.persistAllChats();
12978
- }
12979
- }
13226
+ } while (this.persistQueued);
13227
+ })().finally(() => {
13228
+ this.persistPromise = null;
13229
+ });
13230
+ return this.persistPromise;
12980
13231
  }
12981
13232
  async publish(input) {
12982
13233
  const event = {
12983
- id: randomUUID8(),
13234
+ id: randomUUID9(),
12984
13235
  ts: (/* @__PURE__ */ new Date()).toISOString(),
12985
13236
  ...input
12986
13237
  };
@@ -13722,7 +13973,7 @@ ${combinedScript}` : combinedScript;
13722
13973
  }
13723
13974
 
13724
13975
  // src/services/terminal-service.ts
13725
- import { randomUUID as randomUUID9 } from "crypto";
13976
+ import { randomUUID as randomUUID10 } from "crypto";
13726
13977
  import { existsSync as existsSync10 } from "fs";
13727
13978
  import { spawn as spawn7 } from "node-pty";
13728
13979
  var MAX_REPLAY_CHARS = 1024 * 1024;
@@ -13741,7 +13992,7 @@ var TerminalService = class {
13741
13992
  code: "limit"
13742
13993
  });
13743
13994
  }
13744
- const id = randomUUID9();
13995
+ const id = randomUUID10();
13745
13996
  const shell = process.env.SHELL && existsSync10(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
13746
13997
  const pty = spawn7(shell, ["-l"], {
13747
13998
  name: "xterm-256color",
@@ -14096,6 +14347,9 @@ function createV1Routes(deps) {
14096
14347
  if (details.includes("Chat not found")) {
14097
14348
  return c.json(jsonError("Failed to send message", details), 404);
14098
14349
  }
14350
+ if (error instanceof ChatMessagePersistenceError) {
14351
+ return c.json(jsonError("Failed to save user message", details), 500);
14352
+ }
14099
14353
  return c.json(jsonError("Failed to send message", details), 400);
14100
14354
  }
14101
14355
  });
@@ -15027,7 +15281,7 @@ function startStatusBroadcaster() {
15027
15281
  if (serialized !== previousRepoStatus) {
15028
15282
  previousRepoStatus = serialized;
15029
15283
  eventService.publish({
15030
- id: randomUUID10(),
15284
+ id: randomUUID11(),
15031
15285
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15032
15286
  type: "repo.status.changed",
15033
15287
  payload: { repos }
@@ -15048,7 +15302,7 @@ function startStatusBroadcaster() {
15048
15302
  if (engineStatusJson !== previousEngineStatus) {
15049
15303
  previousEngineStatus = engineStatusJson;
15050
15304
  eventService.publish({
15051
- id: randomUUID10(),
15305
+ id: randomUUID11(),
15052
15306
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15053
15307
  type: "engine.status.changed",
15054
15308
  payload: { status: engineStatus }
@@ -15067,7 +15321,7 @@ function startStatusBroadcaster() {
15067
15321
  previousHookStatus = hookSnapshot;
15068
15322
  if (!lastHooksRunning && hooksRunning) {
15069
15323
  eventService.publish({
15070
- id: randomUUID10(),
15324
+ id: randomUUID11(),
15071
15325
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15072
15326
  type: "hooks.started",
15073
15327
  payload: { running: true, completed: false }
@@ -15076,7 +15330,7 @@ function startStatusBroadcaster() {
15076
15330
  }
15077
15331
  if (hooksRunning) {
15078
15332
  eventService.publish({
15079
- id: randomUUID10(),
15333
+ id: randomUUID11(),
15080
15334
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15081
15335
  type: "hooks.progress",
15082
15336
  payload: { running: true, completed: false }
@@ -15085,7 +15339,7 @@ function startStatusBroadcaster() {
15085
15339
  }
15086
15340
  if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
15087
15341
  eventService.publish({
15088
- id: randomUUID10(),
15342
+ id: randomUUID11(),
15089
15343
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15090
15344
  type: "hooks.completed",
15091
15345
  payload: { running: false, completed: true }
@@ -15094,7 +15348,7 @@ function startStatusBroadcaster() {
15094
15348
  }
15095
15349
  if (lastHooksRunning && !hooksRunning && hooksFailed) {
15096
15350
  eventService.publish({
15097
- id: randomUUID10(),
15351
+ id: randomUUID11(),
15098
15352
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15099
15353
  type: "hooks.failed",
15100
15354
  payload: { running: false, completed: hooksCompleted }
@@ -15102,7 +15356,7 @@ function startStatusBroadcaster() {
15102
15356
  });
15103
15357
  }
15104
15358
  eventService.publish({
15105
- id: randomUUID10(),
15359
+ id: randomUUID11(),
15106
15360
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15107
15361
  type: "hooks.status",
15108
15362
  payload: {
@@ -15157,20 +15411,20 @@ serve(
15157
15411
  }
15158
15412
  const repos = await gitService.listRepos();
15159
15413
  await eventService.publish({
15160
- id: randomUUID10(),
15414
+ id: randomUUID11(),
15161
15415
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15162
15416
  type: "repo.discovered",
15163
15417
  payload: { repos }
15164
15418
  });
15165
15419
  const repoStatuses = await gitService.listRepos();
15166
15420
  await eventService.publish({
15167
- id: randomUUID10(),
15421
+ id: randomUUID11(),
15168
15422
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15169
15423
  type: "repo.status.changed",
15170
15424
  payload: { repos: repoStatuses }
15171
15425
  });
15172
15426
  await eventService.publish({
15173
- id: randomUUID10(),
15427
+ id: randomUUID11(),
15174
15428
  ts: (/* @__PURE__ */ new Date()).toISOString(),
15175
15429
  type: "engine.ready",
15176
15430
  payload: { version: "v1" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.696",
3
+ "version": "0.1.698",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -1,7 +1,7 @@
1
1
  // Generated from shared/src by generate-workspace-sdk-types.mjs; do not edit.
2
2
  export declare const HOSTED_PLUGIN_AUTH_SCHEMES: readonly ["API_KEY", "BASIC", "BEARER_TOKEN", "DCR_OAUTH", "OAUTH1", "OAUTH2"];
3
3
  export type HostedPluginAuthScheme = (typeof HOSTED_PLUGIN_AUTH_SCHEMES)[number];
4
- declare const HOSTED_COMPOSIO_PLUGIN_DEFINITIONS: readonly [["notion", "notion", "OAUTH2", true, "productivity", "Notion"], ["jira", "jira", "OAUTH2", true, "productivity", "Jira"], ["confluence", "confluence", "OAUTH2", true, "productivity", "Confluence"], ["googlecalendar", "googlecalendar", "OAUTH2", true, "productivity", "Google Calendar"], ["microsoftteams", "microsoft_teams", "OAUTH2", true, "productivity", "Microsoft Teams"], ["outlook", "outlook", "OAUTH2", true, "productivity", "Outlook"], ["bitbucket", "bitbucket", "OAUTH2", true, "data", "Bitbucket"], ["datadog", "datadog", "API_KEY", false, "data", "Datadog"], ["pagerduty", "pagerduty", "OAUTH2", true, "data", "PagerDuty"], ["intercom", "intercom", "OAUTH2", true, "business", "Intercom"], ["zendesk", "zendesk", "OAUTH2", true, "business", "Zendesk"], ["hubspot", "hubspot", "OAUTH2", true, "business", "HubSpot"], ["supabase", "supabase", "OAUTH2", true, "data", "Supabase"], ["figma", "figma", "OAUTH2", true, "productivity", "Figma"], ["launchdarkly", "launch_darkly", "API_KEY", false, "data", "LaunchDarkly"], ["asana", "asana", "OAUTH2", true, "productivity", "Asana"], ["clickup", "clickup", "OAUTH2", true, "productivity", "ClickUp"], ["trello", "trello", "OAUTH1", true, "productivity", "Trello"], ["todoist", "todoist", "OAUTH2", true, "productivity", "Todoist"], ["airtable", "airtable", "OAUTH2", true, "productivity", "Airtable"], ["coda", "coda", "API_KEY", false, "productivity", "Coda"], ["miro", "miro", "OAUTH2", true, "productivity", "Miro"], ["sharepoint", "share_point", "OAUTH2", true, "productivity", "SharePoint"], ["onedrive", "one_drive", "OAUTH2", true, "productivity", "OneDrive"], ["googleslides", "googleslides", "OAUTH2", true, "productivity", "Google Slides"], ["googlemeet", "googlemeet", "OAUTH2", true, "productivity", "Google Meet"], ["googletasks", "googletasks", "OAUTH2", true, "productivity", "Google Tasks"], ["dropbox", "dropbox", "OAUTH2", true, "productivity", "Dropbox"], ["box", "box", "OAUTH2", true, "productivity", "Box"], ["discord", "discord", "OAUTH2", true, "productivity", "Discord"], ["discordbot", "discordbot", "OAUTH2", true, "productivity", "Discord Bot"], ["zoom", "zoom", "OAUTH2", true, "productivity", "Zoom"], ["googlechat", "google_chat", "OAUTH2", false, "productivity", "Google Chat"], ["newrelic", "new_relic", "API_KEY", false, "data", "New Relic"], ["betterstack", "better_stack", "API_KEY", false, "data", "Better Stack"], ["incidentio", "incident_io", "API_KEY", false, "data", "incident.io"], ["grafana", "grafana", "BEARER_TOKEN", false, "data", "Grafana"], ["honeycomb", "honeycomb_mcp", "DCR_OAUTH", false, "data", "Honeycomb MCP"], ["bugsnag", "bugsnag", "API_KEY", false, "data", "Bugsnag"], ["circleci", "circleci", "API_KEY", false, "data", "CircleCI"], ["buildkite", "buildkite", "API_KEY", false, "data", "Buildkite"], ["dockerhub", "docker_hub", "API_KEY", false, "data", "Docker Hub"], ["digitalocean", "digital_ocean", "OAUTH2", true, "data", "DigitalOcean"], ["railway", "railway", "API_KEY", false, "data", "Railway"], ["render", "render", "API_KEY", false, "data", "Render"], ["firebase", "firebase", "API_KEY", false, "data", "Firebase"], ["cloudinary", "cloudinary", "API_KEY", false, "data", "Cloudinary"], ["configcat", "configcat", "API_KEY", false, "data", "ConfigCat"], ["contextdev", "context_dev", "API_KEY", false, "data", "Context.dev"], ["googleanalytics", "google_analytics", "OAUTH2", true, "data", "Google Analytics"], ["googlebigquery", "googlebigquery", "OAUTH2", true, "data", "Google BigQuery"], ["amplitude", "amplitude", "API_KEY", false, "data", "Amplitude"], ["mixpanel", "mixpanel", "BASIC", false, "data", "Mixpanel"], ["segment", "segment", "API_KEY", false, "data", "Segment"], ["databricks", "databricks", "API_KEY", false, "data", "Databricks"], ["snowflake", "snowflake", "OAUTH2", false, "data", "Snowflake"], ["algolia", "algolia", "API_KEY", false, "data", "Algolia"], ["elasticsearch", "elasticsearch", "API_KEY", false, "data", "Elasticsearch"], ["salesforce", "salesforce", "OAUTH2", true, "business", "Salesforce"], ["pipedrive", "pipedrive", "API_KEY", false, "business", "Pipedrive"], ["close", "close", "API_KEY", false, "business", "Close"], ["apollo", "apollo", "API_KEY", false, "business", "Apollo"], ["gong", "gong", "OAUTH2", true, "business", "Gong"], ["freshdesk", "freshdesk", "API_KEY", false, "business", "Freshdesk"], ["helpscout", "help_scout", "OAUTH2", false, "business", "Help Scout"], ["servicenow", "servicenow", "BASIC", false, "business", "ServiceNow"], ["mailchimp", "mailchimp", "OAUTH2", true, "business", "Mailchimp"], ["customerio", "customerio", "API_KEY", false, "business", "Customer.io"], ["klaviyo", "klaviyo", "API_KEY", false, "business", "Klaviyo"], ["shopify", "shopify", "API_KEY", false, "business", "Shopify"], ["quickbooks", "quickbooks", "OAUTH2", true, "business", "QuickBooks"], ["xero", "xero", "OAUTH2", false, "business", "Xero"], ["brex", "brex", "API_KEY", false, "business", "Brex"], ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"], ["docusign", "docusign", "OAUTH2", false, "business", "DocuSign"], ["canva", "canva", "OAUTH2", true, "productivity", "Canva"], ["webflow", "webflow", "API_KEY", false, "business", "Webflow"], ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"], ["browserbase", "browserbase_tool", "API_KEY", false, "data", "Browserbase"], ["exa", "exa", "API_KEY", false, "data", "Exa"], ["youtube", "youtube", "OAUTH2", true, "business", "YouTube"], ["twitter", "twitter", "OAUTH2", false, "business", "Twitter/X"], ["instagram", "instagram", "OAUTH2", true, "business", "Instagram"], ["facebook", "facebook", "OAUTH2", true, "business", "Facebook"]];
4
+ declare const HOSTED_COMPOSIO_PLUGIN_DEFINITIONS: readonly [["notion", "notion", "OAUTH2", true, "productivity", "Notion"], ["jira", "jira", "OAUTH2", true, "productivity", "Jira"], ["confluence", "confluence", "OAUTH2", true, "productivity", "Confluence"], ["googlecalendar", "googlecalendar", "OAUTH2", true, "productivity", "Google Calendar"], ["microsoftteams", "microsoft_teams", "OAUTH2", true, "productivity", "Microsoft Teams"], ["outlook", "outlook", "OAUTH2", true, "productivity", "Outlook"], ["bitbucket", "bitbucket", "OAUTH2", true, "data", "Bitbucket"], ["datadog", "datadog", "API_KEY", false, "data", "Datadog"], ["pagerduty", "pagerduty", "OAUTH2", true, "data", "PagerDuty"], ["intercom", "intercom", "OAUTH2", true, "business", "Intercom"], ["zendesk", "zendesk", "OAUTH2", true, "business", "Zendesk"], ["hubspot", "hubspot", "OAUTH2", true, "business", "HubSpot"], ["supabase", "supabase", "OAUTH2", true, "data", "Supabase"], ["figma", "figma", "OAUTH2", true, "productivity", "Figma"], ["launchdarkly", "launch_darkly", "API_KEY", false, "data", "LaunchDarkly"], ["asana", "asana", "OAUTH2", true, "productivity", "Asana"], ["clickup", "clickup", "OAUTH2", true, "productivity", "ClickUp"], ["trello", "trello", "OAUTH1", true, "productivity", "Trello"], ["todoist", "todoist", "OAUTH2", true, "productivity", "Todoist"], ["airtable", "airtable", "OAUTH2", true, "productivity", "Airtable"], ["coda", "coda", "API_KEY", false, "productivity", "Coda"], ["miro", "miro", "OAUTH2", true, "productivity", "Miro"], ["sharepoint", "share_point", "OAUTH2", true, "productivity", "SharePoint"], ["onedrive", "one_drive", "OAUTH2", true, "productivity", "OneDrive"], ["googleslides", "googleslides", "OAUTH2", true, "productivity", "Google Slides"], ["googlemeet", "googlemeet", "OAUTH2", true, "productivity", "Google Meet"], ["googletasks", "googletasks", "OAUTH2", true, "productivity", "Google Tasks"], ["dropbox", "dropbox", "OAUTH2", true, "productivity", "Dropbox"], ["box", "box", "OAUTH2", true, "productivity", "Box"], ["discord", "discord", "OAUTH2", true, "productivity", "Discord"], ["discordbot", "discordbot", "OAUTH2", true, "productivity", "Discord Bot"], ["zoom", "zoom", "OAUTH2", true, "productivity", "Zoom"], ["googlechat", "google_chat", "OAUTH2", false, "productivity", "Google Chat"], ["newrelic", "new_relic", "API_KEY", false, "data", "New Relic"], ["betterstack", "better_stack", "API_KEY", false, "data", "Better Stack"], ["incidentio", "incident_io", "API_KEY", false, "data", "incident.io"], ["grafana", "grafana", "BEARER_TOKEN", false, "data", "Grafana"], ["honeycomb", "honeycomb_mcp", "DCR_OAUTH", false, "data", "Honeycomb MCP"], ["bugsnag", "bugsnag", "API_KEY", false, "data", "Bugsnag"], ["circleci", "circleci", "API_KEY", false, "data", "CircleCI"], ["buildkite", "buildkite", "API_KEY", false, "data", "Buildkite"], ["dockerhub", "docker_hub", "API_KEY", false, "data", "Docker Hub"], ["digitalocean", "digital_ocean", "OAUTH2", true, "data", "DigitalOcean"], ["railway", "railway", "API_KEY", false, "data", "Railway"], ["render", "render", "API_KEY", false, "data", "Render"], ["firebase", "firebase", "API_KEY", false, "data", "Firebase"], ["cloudinary", "cloudinary", "API_KEY", false, "data", "Cloudinary"], ["configcat", "configcat", "API_KEY", false, "data", "ConfigCat"], ["contextdev", "context_dev", "API_KEY", false, "data", "Context.dev"], ["googleanalytics", "google_analytics", "OAUTH2", true, "data", "Google Analytics"], ["googlebigquery", "googlebigquery", "OAUTH2", true, "data", "Google BigQuery"], ["amplitude", "amplitude", "API_KEY", false, "data", "Amplitude"], ["mixpanel", "mixpanel", "BASIC", false, "data", "Mixpanel"], ["segment", "segment", "API_KEY", false, "data", "Segment"], ["databricks", "databricks", "API_KEY", false, "data", "Databricks"], ["snowflake", "snowflake", "OAUTH2", false, "data", "Snowflake"], ["algolia", "algolia", "API_KEY", false, "data", "Algolia"], ["elasticsearch", "elasticsearch", "API_KEY", false, "data", "Elasticsearch"], ["salesforce", "salesforce", "OAUTH2", true, "business", "Salesforce"], ["pipedrive", "pipedrive", "API_KEY", false, "business", "Pipedrive"], ["close", "close", "API_KEY", false, "business", "Close"], ["apollo", "apollo", "API_KEY", false, "business", "Apollo"], ["gong", "gong", "OAUTH2", true, "business", "Gong"], ["freshdesk", "freshdesk", "API_KEY", false, "business", "Freshdesk"], ["helpscout", "help_scout", "OAUTH2", false, "business", "Help Scout"], ["servicenow", "servicenow", "BASIC", false, "business", "ServiceNow"], ["mailchimp", "mailchimp", "OAUTH2", true, "business", "Mailchimp"], ["customerio", "customerio", "API_KEY", false, "business", "Customer.io"], ["klaviyo", "klaviyo", "API_KEY", false, "business", "Klaviyo"], ["shopify", "shopify", "API_KEY", false, "business", "Shopify"], ["quickbooks", "quickbooks", "OAUTH2", true, "business", "QuickBooks"], ["xero", "xero", "OAUTH2", false, "business", "Xero"], ["brex", "brex", "API_KEY", false, "business", "Brex"], ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"], ["canva", "canva", "OAUTH2", true, "productivity", "Canva"], ["webflow", "webflow", "API_KEY", false, "business", "Webflow"], ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"], ["browserbase", "browserbase_tool", "API_KEY", false, "data", "Browserbase"], ["exa", "exa", "API_KEY", false, "data", "Exa"], ["youtube", "youtube", "OAUTH2", true, "business", "YouTube"], ["twitter", "twitter", "OAUTH2", false, "business", "Twitter/X"], ["instagram", "instagram", "OAUTH2", true, "business", "Instagram"], ["facebook", "facebook", "OAUTH2", true, "business", "Facebook"]];
5
5
  type HostedComposioPlugin = {
6
6
  id: (typeof HOSTED_COMPOSIO_PLUGIN_DEFINITIONS)[number][0];
7
7
  backend: 'composio';