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 ||
|
|
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
|
|
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.
|
|
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,
|
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-
|
|
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
|
|
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}.${
|
|
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 =
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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_${
|
|
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
|
|
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
|
-
|
|
3684
|
+
const hadMessages = this.queue.length > 0;
|
|
3685
|
+
this.queueCancellationVersion++;
|
|
3530
3686
|
this.queue = [];
|
|
3531
|
-
return
|
|
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.
|
|
4266
|
-
(
|
|
4267
|
-
|
|
4268
|
-
|
|
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 =
|
|
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 {
|
|
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.
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
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.
|
|
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
|
|
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,
|
|
10377
|
-
const skillName = explicitSkillName(session, message);
|
|
10378
|
-
this.
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 ??
|
|
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
|
-
|
|
12214
|
-
|
|
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
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12229
|
-
|
|
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
|
-
|
|
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) ||
|
|
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) && !
|
|
12533
|
-
const events = [...
|
|
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
|
-
|
|
12956
|
-
if (this.
|
|
13208
|
+
persistAllChats() {
|
|
13209
|
+
if (this.persistPromise) {
|
|
12957
13210
|
this.persistQueued = true;
|
|
12958
|
-
return;
|
|
13211
|
+
return this.persistPromise;
|
|
12959
13212
|
}
|
|
12960
|
-
this.
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
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
|
-
|
|
12973
|
-
}
|
|
12974
|
-
this.
|
|
12975
|
-
|
|
12976
|
-
|
|
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:
|
|
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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,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"], ["
|
|
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';
|