replicas-engine 0.1.655 → 0.1.657
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.
- package/README.md +1 -0
- package/dist/src/{chunk-542J7B2X.js → chunk-HF62RZGX.js} +141 -8
- package/dist/src/headless-agent.js +1 -1
- package/dist/src/index.js +254 -173
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Chats:
|
|
|
28
28
|
- `GET /chats/:chatId`
|
|
29
29
|
- `DELETE /chats/:chatId`
|
|
30
30
|
- `GET /chats/:chatId/history`
|
|
31
|
+
- `GET /chats/:chatId/history-page?limit=<count>` (`limit` required)
|
|
31
32
|
- `GET /chats/:chatId/slash-commands`
|
|
32
33
|
- `POST /chats/:chatId/messages`
|
|
33
34
|
- `POST /chats/:chatId/interrupt`
|
|
@@ -329,6 +329,8 @@ var DEFAULT_DEEPSEEK_MODEL = DEFAULT_OPENCODE_MODEL;
|
|
|
329
329
|
var DEFAULT_PI_MODEL = DEFAULT_OPENCODE_MODEL;
|
|
330
330
|
var OPENROUTER_MODELS = [
|
|
331
331
|
DEFAULT_OPENCODE_MODEL,
|
|
332
|
+
"deepseek/deepseek-v4-pro",
|
|
333
|
+
"deepseek/deepseek-v4-flash",
|
|
332
334
|
"minimax/minimax-m3",
|
|
333
335
|
"xiaomi/mimo-v2.5-pro",
|
|
334
336
|
"moonshotai/kimi-k2.6"
|
|
@@ -444,6 +446,8 @@ var MODEL_LABELS = {
|
|
|
444
446
|
[GPT_5_6_LUNA_MODEL]: "GPT-5.6 Luna",
|
|
445
447
|
"gpt-5.5": "GPT-5.5",
|
|
446
448
|
[DEFAULT_OPENCODE_MODEL]: "GLM 5.2 via OpenRouter",
|
|
449
|
+
"deepseek/deepseek-v4-pro": "DeepSeek V4 Pro via OpenRouter",
|
|
450
|
+
"deepseek/deepseek-v4-flash": "DeepSeek V4 Flash via OpenRouter",
|
|
447
451
|
"minimax/minimax-m3": "MiniMax M3 via OpenRouter",
|
|
448
452
|
"xiaomi/mimo-v2.5-pro": "MiMo V2.5 Pro via OpenRouter",
|
|
449
453
|
"moonshotai/kimi-k2.6": "Kimi K2.6 via OpenRouter",
|
|
@@ -758,7 +762,7 @@ var sendChatMessageRequestSchema = z3.object({
|
|
|
758
762
|
]).optional()
|
|
759
763
|
}).passthrough();
|
|
760
764
|
function isChatMessageSender(value) {
|
|
761
|
-
return isRecord(value) && typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
765
|
+
return isRecord(value) && (value.messageId === void 0 || typeof value.messageId === "string") && typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
762
766
|
}
|
|
763
767
|
function normalizeCodexAspTranscriptStatus(status, failed = false) {
|
|
764
768
|
if (failed || status === "failed" || status === "declined") return "failed";
|
|
@@ -881,6 +885,27 @@ function applyCodexAspTranscriptDelta(current, delta) {
|
|
|
881
885
|
turns
|
|
882
886
|
};
|
|
883
887
|
}
|
|
888
|
+
function isLatestChatHistoryPage(params) {
|
|
889
|
+
return params.beforeCursor === void 0 && params.beforeEvent === void 0 && params.beforeTurn === void 0;
|
|
890
|
+
}
|
|
891
|
+
function getChatHistoryPageSenders(senders, params) {
|
|
892
|
+
return isLatestChatHistoryPage(params) ? senders : senders?.filter((sender) => sender.messageId);
|
|
893
|
+
}
|
|
894
|
+
function parseChatHistoryCursor(value) {
|
|
895
|
+
if (value === void 0) return void 0;
|
|
896
|
+
try {
|
|
897
|
+
const parsed = JSON.parse(value);
|
|
898
|
+
if (!isRecord(parsed) || parsed.mode !== "range" && parsed.mode !== "legacy") return void 0;
|
|
899
|
+
const valid = (part) => part === null || typeof part === "string";
|
|
900
|
+
if (!valid(parsed.events) || !valid(parsed.turns)) return void 0;
|
|
901
|
+
return { mode: parsed.mode, events: parsed.events, turns: parsed.turns };
|
|
902
|
+
} catch {
|
|
903
|
+
return void 0;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
function createChatHistoryCursor(cursor, mode = "range") {
|
|
907
|
+
return cursor.events === null && cursor.turns === null ? null : JSON.stringify({ mode, ...cursor });
|
|
908
|
+
}
|
|
884
909
|
function chatHistoryPageParamsFromQuery(query) {
|
|
885
910
|
const parse = (value) => {
|
|
886
911
|
if (value === void 0) return void 0;
|
|
@@ -890,7 +915,8 @@ function chatHistoryPageParamsFromQuery(query) {
|
|
|
890
915
|
return {
|
|
891
916
|
limit: parse(query.limit),
|
|
892
917
|
beforeEvent: parse(query.beforeEvent),
|
|
893
|
-
beforeTurn: parse(query.beforeTurn)
|
|
918
|
+
beforeTurn: parse(query.beforeTurn),
|
|
919
|
+
beforeCursor: query.beforeCursor || void 0
|
|
894
920
|
};
|
|
895
921
|
}
|
|
896
922
|
function getChatHistoryPageWindow(totalItems, limit, before) {
|
|
@@ -5723,6 +5749,105 @@ var EMAIL_KEYS = [
|
|
|
5723
5749
|
var MANUAL_EMAIL_TEMPLATE_KEYS = ["product_update"];
|
|
5724
5750
|
var EMAIL_TEMPLATE_KEYS = [...EMAIL_KEYS, ...MANUAL_EMAIL_TEMPLATE_KEYS];
|
|
5725
5751
|
|
|
5752
|
+
// ../shared/src/chat-transcript-pages.ts
|
|
5753
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5754
|
+
var MAX_SOURCE_PAGE_BYTES = 4 * 1024 * 1024;
|
|
5755
|
+
var SOURCE_RANGE_BYTES = 256 * 1024;
|
|
5756
|
+
async function readExactRange(readRange, startByte, endByte, error) {
|
|
5757
|
+
const bytes = await readRange(startByte, endByte);
|
|
5758
|
+
if (bytes.length !== endByte - startByte) throw new Error(error);
|
|
5759
|
+
return bytes;
|
|
5760
|
+
}
|
|
5761
|
+
async function readJsonlPage(args) {
|
|
5762
|
+
let endByte = args.size;
|
|
5763
|
+
if (args.before === null) endByte = 0;
|
|
5764
|
+
else if (args.before !== void 0) {
|
|
5765
|
+
endByte = /^(0|[1-9]\d*)$/.test(args.before) ? Number(args.before) : Number.NaN;
|
|
5766
|
+
}
|
|
5767
|
+
if (!Number.isSafeInteger(endByte) || endByte < 0 || endByte > args.size || !Number.isSafeInteger(args.limit) || args.limit < 1) {
|
|
5768
|
+
throw new RangeError("Invalid JSONL page");
|
|
5769
|
+
}
|
|
5770
|
+
let startByte = endByte;
|
|
5771
|
+
let bytes = new Uint8Array();
|
|
5772
|
+
let items = [];
|
|
5773
|
+
while (startByte > 0 && items.length < args.limit && (endByte - startByte < MAX_SOURCE_PAGE_BYTES || bytes.indexOf(10) === -1)) {
|
|
5774
|
+
const nextStartByte = Math.max(0, startByte - SOURCE_RANGE_BYTES);
|
|
5775
|
+
const chunk = await readExactRange(
|
|
5776
|
+
args.readRange,
|
|
5777
|
+
nextStartByte,
|
|
5778
|
+
startByte,
|
|
5779
|
+
"Incomplete JSONL page read"
|
|
5780
|
+
);
|
|
5781
|
+
const combined = new Uint8Array(chunk.length + bytes.length);
|
|
5782
|
+
combined.set(chunk);
|
|
5783
|
+
combined.set(bytes, chunk.length);
|
|
5784
|
+
bytes = combined;
|
|
5785
|
+
startByte = nextStartByte;
|
|
5786
|
+
items = [];
|
|
5787
|
+
let lineStart = 0;
|
|
5788
|
+
for (let lineEnd = bytes.indexOf(10); lineEnd !== -1; lineEnd = bytes.indexOf(10, lineStart)) {
|
|
5789
|
+
if (!(startByte > 0 && lineStart === 0)) {
|
|
5790
|
+
try {
|
|
5791
|
+
const item = args.parse(JSON.parse(decoder.decode(bytes.subarray(lineStart, lineEnd))));
|
|
5792
|
+
if (item !== void 0) items.push({ item, startByte: startByte + lineStart });
|
|
5793
|
+
} catch {
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
lineStart = lineEnd + 1;
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
const selected = items.slice(-args.limit);
|
|
5800
|
+
const firstCompleteLine = bytes.indexOf(10) + 1;
|
|
5801
|
+
let before = 0;
|
|
5802
|
+
if (selected.length === args.limit) before = selected[0].startByte;
|
|
5803
|
+
else if (startByte > 0) before = startByte + firstCompleteLine;
|
|
5804
|
+
return {
|
|
5805
|
+
items: selected.map(({ item }) => item),
|
|
5806
|
+
before: before > 0 ? String(before) : null
|
|
5807
|
+
};
|
|
5808
|
+
}
|
|
5809
|
+
function parseChatTranscriptHistoryIndex(value) {
|
|
5810
|
+
if (!isRecord(value) || value.version !== 2 || !isNonNegativeInteger(value.sourceSize)) return void 0;
|
|
5811
|
+
if (value.codexAspTranscript === void 0) {
|
|
5812
|
+
return { version: 2, sourceSize: value.sourceSize };
|
|
5813
|
+
}
|
|
5814
|
+
if (!isRecord(value.codexAspTranscript) || typeof value.codexAspTranscript.threadId !== "string" || typeof value.codexAspTranscript.updatedAt !== "string" || !isNonNegativeInteger(value.codexAspTranscript.size)) return void 0;
|
|
5815
|
+
return {
|
|
5816
|
+
version: 2,
|
|
5817
|
+
sourceSize: value.sourceSize,
|
|
5818
|
+
codexAspTranscript: {
|
|
5819
|
+
threadId: value.codexAspTranscript.threadId,
|
|
5820
|
+
updatedAt: value.codexAspTranscript.updatedAt,
|
|
5821
|
+
size: value.codexAspTranscript.size
|
|
5822
|
+
}
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
function getChatTranscriptHistorySize(index) {
|
|
5826
|
+
return index.codexAspTranscript?.size ?? 0;
|
|
5827
|
+
}
|
|
5828
|
+
function createChatTranscriptPages(sourceBytes) {
|
|
5829
|
+
const history = parseAgentEventJsonlWithCodexAspTranscript(new TextDecoder("utf-8", { fatal: true }).decode(sourceBytes));
|
|
5830
|
+
const bytes = new TextEncoder().encode(history.transcript?.turns.length ? `${history.transcript.turns.map((turn) => JSON.stringify(turn)).join("\n")}
|
|
5831
|
+
` : "");
|
|
5832
|
+
return {
|
|
5833
|
+
bytes,
|
|
5834
|
+
index: {
|
|
5835
|
+
version: 2,
|
|
5836
|
+
sourceSize: sourceBytes.byteLength,
|
|
5837
|
+
...history.transcript ? {
|
|
5838
|
+
codexAspTranscript: {
|
|
5839
|
+
threadId: history.transcript.threadId,
|
|
5840
|
+
updatedAt: history.transcript.updatedAt,
|
|
5841
|
+
size: bytes.byteLength
|
|
5842
|
+
}
|
|
5843
|
+
} : {}
|
|
5844
|
+
}
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
function isNonNegativeInteger(value) {
|
|
5848
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5726
5851
|
// ../shared/src/object-store/types.ts
|
|
5727
5852
|
var MEDIA_KIND = {
|
|
5728
5853
|
IMAGE: "image",
|
|
@@ -5737,8 +5862,9 @@ function normalizeChatMessageSenders(value) {
|
|
|
5737
5862
|
if (!Array.isArray(value)) return void 0;
|
|
5738
5863
|
const senders = value.flatMap((sender) => {
|
|
5739
5864
|
if (!isChatMessageSender(sender)) return [];
|
|
5740
|
-
const { senderUserId, senderEmail, senderDisplayName, senderAvatarUrl, recordedAt } = sender;
|
|
5865
|
+
const { messageId, senderUserId, senderEmail, senderDisplayName, senderAvatarUrl, recordedAt } = sender;
|
|
5741
5866
|
return [{
|
|
5867
|
+
...typeof messageId === "string" ? { messageId } : {},
|
|
5742
5868
|
senderUserId,
|
|
5743
5869
|
senderEmail,
|
|
5744
5870
|
...typeof senderDisplayName === "string" ? { senderDisplayName } : {},
|
|
@@ -5760,6 +5886,7 @@ function normalizeChatTranscriptMetadata(value) {
|
|
|
5760
5886
|
parentChatId,
|
|
5761
5887
|
deletedAt,
|
|
5762
5888
|
senders,
|
|
5889
|
+
historyIndex,
|
|
5763
5890
|
captureId,
|
|
5764
5891
|
captureReason,
|
|
5765
5892
|
sha256
|
|
@@ -5774,6 +5901,8 @@ function normalizeChatTranscriptMetadata(value) {
|
|
|
5774
5901
|
if (typeof deletedAt === "string" || deletedAt === null) metadata.deletedAt = deletedAt;
|
|
5775
5902
|
const normalizedSenders = normalizeChatMessageSenders(senders);
|
|
5776
5903
|
if (normalizedSenders) metadata.senders = normalizedSenders;
|
|
5904
|
+
const parsedHistoryIndex = parseChatTranscriptHistoryIndex(historyIndex);
|
|
5905
|
+
if (parsedHistoryIndex) metadata.historyIndex = parsedHistoryIndex;
|
|
5777
5906
|
if (typeof captureId === "string") metadata.captureId = captureId;
|
|
5778
5907
|
if (captureReason === "workspace_sleep") metadata.captureReason = captureReason;
|
|
5779
5908
|
if (typeof sha256 === "string") metadata.sha256 = sha256;
|
|
@@ -5823,9 +5952,6 @@ var memoryGenerationManifestSchema = z6.object({
|
|
|
5823
5952
|
rolloutSources: z6.record(z6.string(), z6.string()).optional()
|
|
5824
5953
|
});
|
|
5825
5954
|
|
|
5826
|
-
// ../shared/src/chat-transcript-pages.ts
|
|
5827
|
-
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5828
|
-
|
|
5829
5955
|
// ../shared/src/skill-registry.ts
|
|
5830
5956
|
var SKILL_REGISTRY_MANIFEST_VERSION = 1;
|
|
5831
5957
|
function isSkillRegistryManifest(value) {
|
|
@@ -6100,7 +6226,7 @@ var DEFAULT_CODEX_ARGS = [
|
|
|
6100
6226
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
6101
6227
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
6102
6228
|
var codexCliVersionEnsured = null;
|
|
6103
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
6229
|
+
var ENGINE_PACKAGE_VERSION = "0.1.657";
|
|
6104
6230
|
var INITIALIZE_METHOD = "initialize";
|
|
6105
6231
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
6106
6232
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -6434,8 +6560,11 @@ export {
|
|
|
6434
6560
|
isCodexAspTranscript,
|
|
6435
6561
|
isCodexAspTranscriptDelta,
|
|
6436
6562
|
applyCodexAspTranscriptDelta,
|
|
6563
|
+
isLatestChatHistoryPage,
|
|
6564
|
+
getChatHistoryPageSenders,
|
|
6565
|
+
parseChatHistoryCursor,
|
|
6566
|
+
createChatHistoryCursor,
|
|
6437
6567
|
chatHistoryPageParamsFromQuery,
|
|
6438
|
-
getChatHistoryPageWindow,
|
|
6439
6568
|
paginateChatHistory,
|
|
6440
6569
|
CODEX_AUTH_ENV_KEYS,
|
|
6441
6570
|
codexAuthEnvFromResponse,
|
|
@@ -6464,6 +6593,10 @@ export {
|
|
|
6464
6593
|
parseLatestCodexAspTranscriptTurn,
|
|
6465
6594
|
parseAgentEvents,
|
|
6466
6595
|
parseDisplayMessages,
|
|
6596
|
+
isAgentBackendEvent,
|
|
6597
|
+
readJsonlPage,
|
|
6598
|
+
getChatTranscriptHistorySize,
|
|
6599
|
+
createChatTranscriptPages,
|
|
6467
6600
|
parseChatTranscriptArtifact,
|
|
6468
6601
|
MEMORY_ROOT,
|
|
6469
6602
|
MEMORY_SUMMARY_FILENAME,
|
package/dist/src/index.js
CHANGED
|
@@ -104,7 +104,9 @@ import {
|
|
|
104
104
|
coerceBackgroundTaskPayload,
|
|
105
105
|
coerceClaudeResultPayload,
|
|
106
106
|
createAcceptedUserMessageEvent,
|
|
107
|
+
createChatHistoryCursor,
|
|
107
108
|
createChatRequestSchema,
|
|
109
|
+
createChatTranscriptPages,
|
|
108
110
|
createErrorResult,
|
|
109
111
|
createProviderSlashCommand,
|
|
110
112
|
createSuccessResult,
|
|
@@ -119,7 +121,8 @@ import {
|
|
|
119
121
|
extractToolResultText,
|
|
120
122
|
findGitCommitSignals,
|
|
121
123
|
findPrMergeSignals,
|
|
122
|
-
|
|
124
|
+
getChatHistoryPageSenders,
|
|
125
|
+
getChatTranscriptHistorySize,
|
|
123
126
|
getClaudeModelContextWindow,
|
|
124
127
|
getCodexAspTurnResponse,
|
|
125
128
|
getDeepseekAssistantMessageText,
|
|
@@ -132,6 +135,7 @@ import {
|
|
|
132
135
|
gitIdentityConfigCommands,
|
|
133
136
|
hasChatStarted,
|
|
134
137
|
imageContentToUserMessageImages,
|
|
138
|
+
isAgentBackendEvent,
|
|
135
139
|
isAgentChatMcpActivityRecord,
|
|
136
140
|
isAgentChatSkillActivityRecord,
|
|
137
141
|
isAgentChatTurnActivityRecord,
|
|
@@ -143,6 +147,7 @@ import {
|
|
|
143
147
|
isCodexAuthError,
|
|
144
148
|
isDefaultChat,
|
|
145
149
|
isGitHubUrl,
|
|
150
|
+
isLatestChatHistoryPage,
|
|
146
151
|
isRecord,
|
|
147
152
|
isSkillRegistryManifest,
|
|
148
153
|
isTerminalBackgroundTaskStatus,
|
|
@@ -158,6 +163,7 @@ import {
|
|
|
158
163
|
paginateEngineLogContent,
|
|
159
164
|
parseAgentEventJsonlWithCodexAspTranscript,
|
|
160
165
|
parseAgentEvents,
|
|
166
|
+
parseChatHistoryCursor,
|
|
161
167
|
parseChatTranscriptArtifact,
|
|
162
168
|
parseCodeHostPrUrl,
|
|
163
169
|
parseDisplayMessages,
|
|
@@ -169,6 +175,7 @@ import {
|
|
|
169
175
|
percentage,
|
|
170
176
|
putPresignedFile,
|
|
171
177
|
raceWithTimeout,
|
|
178
|
+
readJsonlPage,
|
|
172
179
|
readReplicasRuntimeEnv,
|
|
173
180
|
recoverCompletedTurn,
|
|
174
181
|
resolveWarmHookConfig,
|
|
@@ -176,13 +183,13 @@ import {
|
|
|
176
183
|
serializeCanvasContentResponse,
|
|
177
184
|
shellQuotePosix,
|
|
178
185
|
stripAgentDiagnosticErrors
|
|
179
|
-
} from "./chunk-
|
|
186
|
+
} from "./chunk-HF62RZGX.js";
|
|
180
187
|
|
|
181
188
|
// src/index.ts
|
|
182
189
|
import { serve } from "@hono/node-server";
|
|
183
190
|
import { Hono as Hono2 } from "hono";
|
|
184
191
|
import { existsSync as existsSync11 } from "fs";
|
|
185
|
-
import { randomUUID as
|
|
192
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
186
193
|
import { connect } from "net";
|
|
187
194
|
|
|
188
195
|
// src/managers/github-token-manager.ts
|
|
@@ -2735,7 +2742,7 @@ import { existsSync as existsSync8 } from "fs";
|
|
|
2735
2742
|
import { appendFile as appendFile4, copyFile, mkdir as mkdir16, readFile as readFile15, rename as rename3, rm as rm2 } from "fs/promises";
|
|
2736
2743
|
import { homedir as homedir15 } from "os";
|
|
2737
2744
|
import { join as join29 } from "path";
|
|
2738
|
-
import { randomUUID as
|
|
2745
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
2739
2746
|
|
|
2740
2747
|
// src/managers/claude-manager.ts
|
|
2741
2748
|
import {
|
|
@@ -4147,11 +4154,11 @@ var CodexHistoryFile = class {
|
|
|
4147
4154
|
}
|
|
4148
4155
|
filePath;
|
|
4149
4156
|
writeLock = new AsyncLock();
|
|
4150
|
-
eventLineIndex = null;
|
|
4151
4157
|
/** Best-effort ordered append; failures must not disrupt the turn. */
|
|
4152
4158
|
append(event) {
|
|
4153
4159
|
void this.writeLock.run(
|
|
4154
|
-
() => appendFile2(this.filePath, JSON.stringify(event)
|
|
4160
|
+
() => appendFile2(this.filePath, `${JSON.stringify(event)}
|
|
4161
|
+
`).catch((error) => {
|
|
4155
4162
|
console.error("[CodexHistoryFile] Failed to append event:", error);
|
|
4156
4163
|
})
|
|
4157
4164
|
);
|
|
@@ -4172,85 +4179,51 @@ var CodexHistoryFile = class {
|
|
|
4172
4179
|
}
|
|
4173
4180
|
async loadEventsPage(page) {
|
|
4174
4181
|
if (page.limit === void 0) {
|
|
4175
|
-
const
|
|
4176
|
-
return { events:
|
|
4182
|
+
const history = await this.load();
|
|
4183
|
+
return { events: history.events };
|
|
4184
|
+
}
|
|
4185
|
+
const cursor = parseChatHistoryCursor(page.beforeCursor);
|
|
4186
|
+
if (page.beforeCursor !== void 0 && cursor?.mode !== "range") throw new RangeError("Invalid chat history cursor");
|
|
4187
|
+
if (cursor?.events === null) {
|
|
4188
|
+
return {
|
|
4189
|
+
events: [],
|
|
4190
|
+
beforeCursor: createChatHistoryCursor({
|
|
4191
|
+
events: null,
|
|
4192
|
+
turns: cursor.turns
|
|
4193
|
+
})
|
|
4194
|
+
};
|
|
4177
4195
|
}
|
|
4178
4196
|
let file;
|
|
4179
4197
|
try {
|
|
4180
4198
|
file = await open(this.filePath, "r");
|
|
4181
4199
|
} catch (error) {
|
|
4182
|
-
if (
|
|
4183
|
-
|
|
4200
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
4201
|
+
return { events: [], beforeCursor: null };
|
|
4184
4202
|
}
|
|
4185
|
-
|
|
4203
|
+
throw error;
|
|
4186
4204
|
}
|
|
4187
4205
|
try {
|
|
4188
|
-
const
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
position += bytesRead2;
|
|
4207
|
-
}
|
|
4208
|
-
index = { size: stats.size, mtimeMs: stats.mtimeMs, starts };
|
|
4209
|
-
this.eventLineIndex = index;
|
|
4210
|
-
}
|
|
4211
|
-
const totalEvents2 = index.starts.length;
|
|
4212
|
-
const { startIndex: eventsStartIndex2, endIndex: eventsEnd2 } = getChatHistoryPageWindow(
|
|
4213
|
-
totalEvents2,
|
|
4214
|
-
page.limit,
|
|
4215
|
-
page.beforeEvent
|
|
4216
|
-
);
|
|
4217
|
-
const startByte = index.starts[eventsStartIndex2] ?? index.size;
|
|
4218
|
-
const endByte = index.starts[eventsEnd2] ?? index.size;
|
|
4219
|
-
const content = Buffer.allocUnsafe(endByte - startByte);
|
|
4220
|
-
let bytesRead = 0;
|
|
4221
|
-
while (bytesRead < content.length) {
|
|
4222
|
-
const result = await file.read(
|
|
4223
|
-
content,
|
|
4224
|
-
bytesRead,
|
|
4225
|
-
content.length - bytesRead,
|
|
4226
|
-
startByte + bytesRead
|
|
4227
|
-
);
|
|
4228
|
-
if (result.bytesRead === 0) break;
|
|
4229
|
-
bytesRead += result.bytesRead;
|
|
4230
|
-
}
|
|
4231
|
-
if (bytesRead === content.length) {
|
|
4232
|
-
const parsed = parseAgentEventJsonlWithCodexAspTranscript(content.toString("utf-8"));
|
|
4233
|
-
if (!parsed.transcript && parsed.events.length === eventsEnd2 - eventsStartIndex2) {
|
|
4234
|
-
return { events: parsed.events, eventsStartIndex: eventsStartIndex2, totalEvents: totalEvents2 };
|
|
4235
|
-
}
|
|
4236
|
-
}
|
|
4237
|
-
} catch (error) {
|
|
4238
|
-
console.error("[CodexHistoryFile] Failed to load history page:", error);
|
|
4206
|
+
const result = await readJsonlPage({
|
|
4207
|
+
size: (await file.stat()).size,
|
|
4208
|
+
limit: page.limit,
|
|
4209
|
+
before: cursor?.events,
|
|
4210
|
+
readRange: async (startByte, endByte) => {
|
|
4211
|
+
const bytes = Buffer.allocUnsafe(endByte - startByte);
|
|
4212
|
+
const { bytesRead } = await file.read(bytes, 0, bytes.length, startByte);
|
|
4213
|
+
return bytes.subarray(0, bytesRead);
|
|
4214
|
+
},
|
|
4215
|
+
parse: (value) => isAgentBackendEvent(value) && value.type !== CODEX_ASP_TRANSCRIPT_UPDATED_EVENT_TYPE ? value : void 0
|
|
4216
|
+
});
|
|
4217
|
+
return {
|
|
4218
|
+
events: result.items,
|
|
4219
|
+
beforeCursor: createChatHistoryCursor({
|
|
4220
|
+
events: result.before,
|
|
4221
|
+
turns: cursor?.turns ?? null
|
|
4222
|
+
})
|
|
4223
|
+
};
|
|
4239
4224
|
} finally {
|
|
4240
4225
|
await file.close();
|
|
4241
4226
|
}
|
|
4242
|
-
const history = await this.load();
|
|
4243
|
-
const totalEvents = history.events.length;
|
|
4244
|
-
const { startIndex: eventsStartIndex, endIndex: eventsEnd } = getChatHistoryPageWindow(
|
|
4245
|
-
totalEvents,
|
|
4246
|
-
page.limit,
|
|
4247
|
-
page.beforeEvent
|
|
4248
|
-
);
|
|
4249
|
-
return {
|
|
4250
|
-
events: history.events.slice(eventsStartIndex, eventsEnd),
|
|
4251
|
-
eventsStartIndex,
|
|
4252
|
-
totalEvents
|
|
4253
|
-
};
|
|
4254
4227
|
}
|
|
4255
4228
|
};
|
|
4256
4229
|
|
|
@@ -5721,6 +5694,7 @@ var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
|
|
|
5721
5694
|
var THREAD_START_METHOD = "thread/start";
|
|
5722
5695
|
var THREAD_RESUME_METHOD = "thread/resume";
|
|
5723
5696
|
var THREAD_READ_METHOD = "thread/read";
|
|
5697
|
+
var THREAD_TURNS_LIST_METHOD = "thread/turns/list";
|
|
5724
5698
|
var THREAD_GOAL_SET_METHOD = "thread/goal/set";
|
|
5725
5699
|
var THREAD_GOAL_GET_METHOD = "thread/goal/get";
|
|
5726
5700
|
var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
|
|
@@ -6049,8 +6023,11 @@ function formatTurnFailure(turn) {
|
|
|
6049
6023
|
return parts.join(" ");
|
|
6050
6024
|
}
|
|
6051
6025
|
function threadToAspTranscript(thread) {
|
|
6026
|
+
return turnsToAspTranscript(thread.id, timestampFromSeconds(thread.updatedAt), thread.turns);
|
|
6027
|
+
}
|
|
6028
|
+
function turnsToAspTranscript(threadId, updatedAt, sourceTurns) {
|
|
6052
6029
|
let sequence = 0;
|
|
6053
|
-
const turns =
|
|
6030
|
+
const turns = sourceTurns.map((turn, index) => ({ turn, index })).sort((a, b) => (a.turn.startedAt ?? a.turn.completedAt ?? Number.MAX_SAFE_INTEGER) - (b.turn.startedAt ?? b.turn.completedAt ?? Number.MAX_SAFE_INTEGER) || a.index - b.index).map(({ turn }) => {
|
|
6054
6031
|
const startedAt = timestampFromSeconds(turn.startedAt);
|
|
6055
6032
|
const completedAt = turn.completedAt === null ? null : timestampFromSeconds(turn.completedAt);
|
|
6056
6033
|
const itemTimestamp = completedAt ?? startedAt;
|
|
@@ -6080,8 +6057,8 @@ function threadToAspTranscript(thread) {
|
|
|
6080
6057
|
};
|
|
6081
6058
|
});
|
|
6082
6059
|
return {
|
|
6083
|
-
threadId
|
|
6084
|
-
updatedAt
|
|
6060
|
+
threadId,
|
|
6061
|
+
updatedAt,
|
|
6085
6062
|
turns
|
|
6086
6063
|
};
|
|
6087
6064
|
}
|
|
@@ -6403,7 +6380,7 @@ async function readCodexAspThreadHistory(threadId) {
|
|
|
6403
6380
|
}
|
|
6404
6381
|
}
|
|
6405
6382
|
for (const entry of entries) {
|
|
6406
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
6383
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || entry.name.endsWith(".pages.jsonl")) continue;
|
|
6407
6384
|
const history = await new CodexHistoryFile(join19(CODEX_HISTORY_DIR, entry.name)).load();
|
|
6408
6385
|
const transcript = history.transcriptsByThreadId.get(threadId);
|
|
6409
6386
|
if (transcript) {
|
|
@@ -6452,7 +6429,6 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6452
6429
|
activeTurnId = null;
|
|
6453
6430
|
threadAttached = false;
|
|
6454
6431
|
historyFile;
|
|
6455
|
-
historyEvents = [];
|
|
6456
6432
|
codexAspTranscript = null;
|
|
6457
6433
|
lastEmittedTranscripts = /* @__PURE__ */ new Map();
|
|
6458
6434
|
codexAspSequence = 0;
|
|
@@ -6474,9 +6450,12 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6474
6450
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
6475
6451
|
}
|
|
6476
6452
|
async initialize() {
|
|
6453
|
+
if (this.initialSessionId) {
|
|
6454
|
+
this.currentThreadId = this.initialSessionId;
|
|
6455
|
+
return;
|
|
6456
|
+
}
|
|
6477
6457
|
const replayed = await this.historyFile?.load();
|
|
6478
6458
|
if (replayed) {
|
|
6479
|
-
this.historyEvents.push(...replayed.events);
|
|
6480
6459
|
if (replayed.transcript) {
|
|
6481
6460
|
this.mergeTranscriptSnapshot(replayed.transcript);
|
|
6482
6461
|
}
|
|
@@ -6565,51 +6544,87 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6565
6544
|
return true;
|
|
6566
6545
|
}
|
|
6567
6546
|
async getHistory(page = {}) {
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
}
|
|
6578
|
-
}
|
|
6579
|
-
return
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6547
|
+
await this.historyFile?.flush();
|
|
6548
|
+
if (page.limit === void 0) {
|
|
6549
|
+
const local = async () => {
|
|
6550
|
+
const history = await this.historyFile?.load();
|
|
6551
|
+
return {
|
|
6552
|
+
thread_id: this.currentThreadId,
|
|
6553
|
+
events: history?.events ?? [],
|
|
6554
|
+
codexAspTranscript: this.currentThreadId ? history?.transcriptsByThreadId.get(this.currentThreadId) ?? null : null,
|
|
6555
|
+
goal: this.currentGoal
|
|
6556
|
+
};
|
|
6557
|
+
};
|
|
6558
|
+
if (!this.currentThreadId) return local();
|
|
6559
|
+
return this.readFullNativeHistory().catch(local);
|
|
6560
|
+
}
|
|
6561
|
+
const events = await this.historyFile?.loadEventsPage(page) ?? { events: [], beforeCursor: null };
|
|
6562
|
+
if (!this.currentThreadId) return { thread_id: null, ...events, goal: this.currentGoal };
|
|
6563
|
+
const cursor = parseChatHistoryCursor(page.beforeCursor);
|
|
6564
|
+
if (page.beforeCursor !== void 0 && cursor?.mode !== "range") throw new RangeError("Invalid chat history cursor");
|
|
6565
|
+
let transcript = null;
|
|
6566
|
+
let turnsBefore = null;
|
|
6567
|
+
if (cursor?.turns !== null) {
|
|
6587
6568
|
const host = await getCodexAspHost();
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
),
|
|
6593
|
-
this.refreshThreadGoal(host, this.currentThreadId)
|
|
6594
|
-
]);
|
|
6595
|
-
const transcript = this.mergeTranscriptSnapshot(threadToAspTranscript(response.thread));
|
|
6596
|
-
if (transcript) {
|
|
6597
|
-
transcript.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6598
|
-
}
|
|
6599
|
-
return paginateChatHistory({
|
|
6600
|
-
thread_id: this.currentThreadId,
|
|
6601
|
-
events: [...this.historyEvents],
|
|
6602
|
-
codexAspTranscript: transcript,
|
|
6603
|
-
goal: this.currentGoal
|
|
6604
|
-
}, page);
|
|
6605
|
-
} catch {
|
|
6569
|
+
if (page.beforeCursor === void 0) await this.refreshThreadGoal(host, this.currentThreadId);
|
|
6570
|
+
const turns = await this.readNativeTurnsPage(page.limit, cursor?.turns, host);
|
|
6571
|
+
transcript = turns.transcript;
|
|
6572
|
+
turnsBefore = turns.before;
|
|
6606
6573
|
}
|
|
6607
|
-
|
|
6574
|
+
const eventCursor = parseChatHistoryCursor(events.beforeCursor ?? void 0);
|
|
6575
|
+
return {
|
|
6576
|
+
thread_id: this.currentThreadId,
|
|
6577
|
+
...events,
|
|
6578
|
+
codexAspTranscript: transcript,
|
|
6579
|
+
goal: this.currentGoal,
|
|
6580
|
+
beforeCursor: createChatHistoryCursor({
|
|
6581
|
+
events: eventCursor?.events ?? null,
|
|
6582
|
+
turns: turnsBefore
|
|
6583
|
+
})
|
|
6584
|
+
};
|
|
6585
|
+
}
|
|
6586
|
+
async readFullNativeHistory() {
|
|
6587
|
+
if (!this.currentThreadId) {
|
|
6588
|
+
return { thread_id: null, events: [], goal: this.currentGoal };
|
|
6589
|
+
}
|
|
6590
|
+
const host = await getCodexAspHost();
|
|
6591
|
+
const [response, history] = await Promise.all([
|
|
6592
|
+
host.client.request(
|
|
6593
|
+
THREAD_READ_METHOD,
|
|
6594
|
+
{ threadId: this.currentThreadId, includeTurns: true }
|
|
6595
|
+
),
|
|
6596
|
+
this.historyFile?.load()
|
|
6597
|
+
]);
|
|
6598
|
+
const transcript = this.mergeTranscriptSnapshot(threadToAspTranscript(response.thread));
|
|
6599
|
+
return {
|
|
6608
6600
|
thread_id: this.currentThreadId,
|
|
6609
|
-
events: [
|
|
6610
|
-
codexAspTranscript:
|
|
6601
|
+
events: history?.events ?? [],
|
|
6602
|
+
codexAspTranscript: transcript,
|
|
6611
6603
|
goal: this.currentGoal
|
|
6612
|
-
}
|
|
6604
|
+
};
|
|
6605
|
+
}
|
|
6606
|
+
async readNativeTurnsPage(limit, cursor, host) {
|
|
6607
|
+
if (!this.currentThreadId) throw new Error("A thread is required for native turn pagination");
|
|
6608
|
+
const response = await host.client.request(
|
|
6609
|
+
THREAD_TURNS_LIST_METHOD,
|
|
6610
|
+
{
|
|
6611
|
+
threadId: this.currentThreadId,
|
|
6612
|
+
cursor: cursor ?? null,
|
|
6613
|
+
limit,
|
|
6614
|
+
sortDirection: "desc",
|
|
6615
|
+
itemsView: "full"
|
|
6616
|
+
}
|
|
6617
|
+
);
|
|
6618
|
+
const transcript = turnsToAspTranscript(
|
|
6619
|
+
this.currentThreadId,
|
|
6620
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
6621
|
+
response.data
|
|
6622
|
+
);
|
|
6623
|
+
if (cursor === void 0) {
|
|
6624
|
+
this.codexAspTranscript = transcript;
|
|
6625
|
+
this.syncTranscriptSequence(transcript);
|
|
6626
|
+
}
|
|
6627
|
+
return { transcript, before: response.nextCursor };
|
|
6613
6628
|
}
|
|
6614
6629
|
getGoal() {
|
|
6615
6630
|
return this.currentGoal;
|
|
@@ -7538,7 +7553,6 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7538
7553
|
return event;
|
|
7539
7554
|
}
|
|
7540
7555
|
trackHistoryEvent(event) {
|
|
7541
|
-
this.historyEvents.push(event);
|
|
7542
7556
|
this.historyFile?.append(event);
|
|
7543
7557
|
}
|
|
7544
7558
|
emitTranscriptUpdated(threadId, options = {}) {
|
|
@@ -8094,6 +8108,7 @@ ${instructions}
|
|
|
8094
8108
|
|
|
8095
8109
|
// src/managers/deepseek-manager.ts
|
|
8096
8110
|
import { spawn as spawn3 } from "child_process";
|
|
8111
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
8097
8112
|
import { createRequire } from "module";
|
|
8098
8113
|
import { dirname as dirname6, join as join21 } from "path";
|
|
8099
8114
|
import { fileURLToPath } from "url";
|
|
@@ -8103,7 +8118,7 @@ import { z as z2 } from "zod";
|
|
|
8103
8118
|
import WebSocket from "ws";
|
|
8104
8119
|
import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
|
|
8105
8120
|
import { hostFrameSchema, muxFrameSchema } from "@deepseek-ai/dsh-host-apiproxy/api/events.schema";
|
|
8106
|
-
import { serverRequestSchema } from "@deepseek-ai/dsh-host-apiproxy/api/rpc.schema";
|
|
8121
|
+
import { serverRequestSchema, serverResponseSchema } from "@deepseek-ai/dsh-host-apiproxy/api/rpc.schema";
|
|
8107
8122
|
import { sessionIdSchema } from "@deepseek-ai/dsh-host-apiproxy/api/sessions.schema";
|
|
8108
8123
|
var questionSelectionSchema = z2.record(z2.string(), z2.object({
|
|
8109
8124
|
options: z2.array(z2.string()).optional(),
|
|
@@ -8134,6 +8149,26 @@ var DeepseekApiClient = class extends AbstractApiClient {
|
|
|
8134
8149
|
openHost(_payload, signal, onOpen) {
|
|
8135
8150
|
return this.readWebSocket("/api/events.host", signal, hostFrameSchema, onOpen);
|
|
8136
8151
|
}
|
|
8152
|
+
async executeCommand(sessionId, line) {
|
|
8153
|
+
const rpcId = randomUUID5();
|
|
8154
|
+
const response = await this.doFetch(new URL("/api/commands/execute", this.baseUrl), {
|
|
8155
|
+
method: "POST",
|
|
8156
|
+
headers: { "content-type": "application/json" },
|
|
8157
|
+
body: JSON.stringify({
|
|
8158
|
+
type: "client-request",
|
|
8159
|
+
rpcId,
|
|
8160
|
+
method: "commands/execute",
|
|
8161
|
+
payload: { args: { agentId: sessionId, line } }
|
|
8162
|
+
})
|
|
8163
|
+
});
|
|
8164
|
+
if (!response.ok) throw new Error(`DeepSeek Harness command failed with HTTP ${response.status}.`);
|
|
8165
|
+
const result = serverResponseSchema.parse(await response.json());
|
|
8166
|
+
if (result.rpcId !== rpcId) throw new Error("DeepSeek Harness command response did not match its request.");
|
|
8167
|
+
if (!result.result.ok) throw new Error(result.result.error.message);
|
|
8168
|
+
const execution = z2.object({ result: z2.object({ kind: z2.enum(["success", "error"]), text: z2.string().optional() }) }).safeParse(result.result.value);
|
|
8169
|
+
if (!execution.success) throw new Error(`DeepSeek Harness did not recognize ${line}.`);
|
|
8170
|
+
if (execution.data.result.kind === "error") throw new Error(execution.data.result.text ?? `DeepSeek Harness rejected ${line}.`);
|
|
8171
|
+
}
|
|
8137
8172
|
async *readWebSocket(path6, signal, schema, onOpen) {
|
|
8138
8173
|
const url = new URL(path6, this.baseUrl);
|
|
8139
8174
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
@@ -8452,18 +8487,12 @@ ${combinedInstructions}
|
|
|
8452
8487
|
</system_instructions>
|
|
8453
8488
|
|
|
8454
8489
|
${request.message}` : request.message;
|
|
8455
|
-
|
|
8456
|
-
unwrap(await client.sessions.prompt({
|
|
8457
|
-
sessionId: this.sessionId,
|
|
8458
|
-
mode: "queue",
|
|
8459
|
-
content: [{ type: "text", text: "/plan off" }]
|
|
8460
|
-
}));
|
|
8461
|
-
}
|
|
8490
|
+
await client.executeCommand(this.sessionId, request.planMode ? "/plan" : "/plan off");
|
|
8462
8491
|
unwrap(await client.sessions.prompt({
|
|
8463
8492
|
sessionId: this.sessionId,
|
|
8464
8493
|
mode: "queue",
|
|
8465
8494
|
content: [
|
|
8466
|
-
{ type: "text", text:
|
|
8495
|
+
{ type: "text", text: prompt },
|
|
8467
8496
|
...images.map((image) => ({
|
|
8468
8497
|
type: "image",
|
|
8469
8498
|
mediaType: image.source.media_type,
|
|
@@ -10184,7 +10213,7 @@ import {
|
|
|
10184
10213
|
unlink as unlink3
|
|
10185
10214
|
} from "fs/promises";
|
|
10186
10215
|
import { join as join24 } from "path";
|
|
10187
|
-
import { randomUUID as
|
|
10216
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
10188
10217
|
|
|
10189
10218
|
// src/analytics/agent/activity/skill-mcp-call-extractor.ts
|
|
10190
10219
|
var NON_MCP_SERVERS = /* @__PURE__ */ new Set(["claude", "cursor", "deepseek", "opencode", "pi", "custom", "dynamic"]);
|
|
@@ -10405,7 +10434,7 @@ var AgentChatTurnActivityTracker = class {
|
|
|
10405
10434
|
const credential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
|
|
10406
10435
|
this.pendingMessages.delete(messageId);
|
|
10407
10436
|
this.activeTurns.set(chatId, {
|
|
10408
|
-
turnId:
|
|
10437
|
+
turnId: randomUUID6(),
|
|
10409
10438
|
startedAtMs: Date.now(),
|
|
10410
10439
|
provider,
|
|
10411
10440
|
model: attributes.model ?? getDefaultAgentModel(provider),
|
|
@@ -10800,7 +10829,7 @@ async function reconcileCanvasItems(filenames) {
|
|
|
10800
10829
|
// src/services/upload-chat-transcripts.ts
|
|
10801
10830
|
import { createReadStream } from "fs";
|
|
10802
10831
|
import { createHash as createHash2 } from "crypto";
|
|
10803
|
-
import {
|
|
10832
|
+
import { readFile as readFile14, readdir as readdir8, stat as stat4 } from "fs/promises";
|
|
10804
10833
|
import { basename as basename2, join as join27 } from "path";
|
|
10805
10834
|
|
|
10806
10835
|
// src/services/chat/chat-senders.ts
|
|
@@ -10859,6 +10888,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
10859
10888
|
async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
10860
10889
|
const { size } = await stat4(filePath);
|
|
10861
10890
|
if (size === 0) return null;
|
|
10891
|
+
const historyPages = createChatTranscriptPages(await readFile14(filePath));
|
|
10862
10892
|
const metadata = chat ? {
|
|
10863
10893
|
provider: chat.provider,
|
|
10864
10894
|
credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
|
|
@@ -10882,6 +10912,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
10882
10912
|
} catch (error) {
|
|
10883
10913
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
10884
10914
|
}
|
|
10915
|
+
if (historyPages.index.codexAspTranscript) metadata.historyIndex = historyPages.index;
|
|
10885
10916
|
const uploadRequest = { chatId, size, metadata };
|
|
10886
10917
|
const prepareResponse = await monolithRequest("/v1/engine/chat-transcripts/upload-url", {
|
|
10887
10918
|
body: uploadRequest
|
|
@@ -10890,11 +10921,25 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
10890
10921
|
throw new Error(`prepare failed: ${prepareResponse.status} ${await prepareResponse.text()}`);
|
|
10891
10922
|
}
|
|
10892
10923
|
const prepareBody = await prepareResponse.json();
|
|
10893
|
-
if (!isRecord(prepareBody) || typeof prepareBody.uploadUrl !== "string" && prepareBody.uploadUrl !== null) {
|
|
10924
|
+
if (!isRecord(prepareBody) || typeof prepareBody.uploadUrl !== "string" && prepareBody.uploadUrl !== null || prepareBody.historyPagesUploadUrl !== void 0 && typeof prepareBody.historyPagesUploadUrl !== "string") {
|
|
10894
10925
|
throw new Error("prepare failed: invalid response");
|
|
10895
10926
|
}
|
|
10896
10927
|
if (prepareBody.uploadUrl === null) return null;
|
|
10897
|
-
|
|
10928
|
+
const uploads = [putPresignedFile(prepareBody.uploadUrl, filePath, size, "application/x-ndjson")];
|
|
10929
|
+
const historyPagesSize = getChatTranscriptHistorySize(historyPages.index);
|
|
10930
|
+
if (historyPagesSize > 0) {
|
|
10931
|
+
if (typeof prepareBody.historyPagesUploadUrl !== "string") {
|
|
10932
|
+
throw new Error("prepare failed: missing history pages upload URL");
|
|
10933
|
+
}
|
|
10934
|
+
uploads.push(fetch(prepareBody.historyPagesUploadUrl, {
|
|
10935
|
+
method: "PUT",
|
|
10936
|
+
headers: { "content-type": "application/x-ndjson" },
|
|
10937
|
+
body: new Uint8Array(historyPages.bytes).buffer
|
|
10938
|
+
}).then((response) => {
|
|
10939
|
+
if (!response.ok) throw new Error(`history pages upload failed: ${response.status}`);
|
|
10940
|
+
}));
|
|
10941
|
+
}
|
|
10942
|
+
await Promise.all(uploads);
|
|
10898
10943
|
const finalizeResponse = await monolithRequest("/v1/engine/chat-transcripts/finalize", {
|
|
10899
10944
|
body: uploadRequest
|
|
10900
10945
|
});
|
|
@@ -11273,7 +11318,7 @@ var ChatService = class {
|
|
|
11273
11318
|
throw new ChatNotFoundError(parentChatId);
|
|
11274
11319
|
}
|
|
11275
11320
|
const persisted = {
|
|
11276
|
-
id: request.id ??
|
|
11321
|
+
id: request.id ?? randomUUID7(),
|
|
11277
11322
|
provider: request.provider,
|
|
11278
11323
|
title,
|
|
11279
11324
|
createdAt: now,
|
|
@@ -11310,10 +11355,11 @@ var ChatService = class {
|
|
|
11310
11355
|
}
|
|
11311
11356
|
chat.persisted.acceptedSendResponses = Object.fromEntries(chat.acceptedSendResponses);
|
|
11312
11357
|
}
|
|
11358
|
+
const submittedAt = request.submittedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
11313
11359
|
const acceptedEvent = createAcceptedUserMessageEvent(
|
|
11314
11360
|
request.message,
|
|
11315
11361
|
result.messageId,
|
|
11316
|
-
|
|
11362
|
+
submittedAt,
|
|
11317
11363
|
request.images
|
|
11318
11364
|
);
|
|
11319
11365
|
if (!chat.pendingMessageIds.includes(result.messageId)) {
|
|
@@ -11329,11 +11375,12 @@ var ChatService = class {
|
|
|
11329
11375
|
let recordedSender;
|
|
11330
11376
|
if (request.senderUserId && request.senderEmail) {
|
|
11331
11377
|
recordedSender = {
|
|
11378
|
+
messageId: result.messageId,
|
|
11332
11379
|
senderUserId: request.senderUserId,
|
|
11333
11380
|
senderEmail: request.senderEmail,
|
|
11334
11381
|
...request.senderDisplayName ? { senderDisplayName: request.senderDisplayName } : {},
|
|
11335
11382
|
...request.senderAvatarUrl ? { senderAvatarUrl: request.senderAvatarUrl } : {},
|
|
11336
|
-
recordedAt:
|
|
11383
|
+
recordedAt: submittedAt
|
|
11337
11384
|
};
|
|
11338
11385
|
await this.appendSender(chatId, recordedSender);
|
|
11339
11386
|
}
|
|
@@ -11362,15 +11409,11 @@ var ChatService = class {
|
|
|
11362
11409
|
}
|
|
11363
11410
|
}
|
|
11364
11411
|
async readSenders(chatId) {
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
} catch (error) {
|
|
11368
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
11369
|
-
return [];
|
|
11370
|
-
}
|
|
11412
|
+
return readFile15(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
|
|
11413
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
11371
11414
|
console.error("[ChatService] Failed to read sender records:", error);
|
|
11372
11415
|
return [];
|
|
11373
|
-
}
|
|
11416
|
+
});
|
|
11374
11417
|
}
|
|
11375
11418
|
async interrupt(chatId) {
|
|
11376
11419
|
const chat = this.requireChat(chatId);
|
|
@@ -11592,7 +11635,12 @@ var ChatService = class {
|
|
|
11592
11635
|
return descendants;
|
|
11593
11636
|
}
|
|
11594
11637
|
async deleteHistoryFile(persisted) {
|
|
11595
|
-
|
|
11638
|
+
const historyPath = join29(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
|
|
11639
|
+
await Promise.all([
|
|
11640
|
+
historyPath,
|
|
11641
|
+
`${historyPath}.pages.jsonl`,
|
|
11642
|
+
`${historyPath}.pages.index.json`
|
|
11643
|
+
].map((path6) => rm2(path6, { force: true })));
|
|
11596
11644
|
await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
|
|
11597
11645
|
}
|
|
11598
11646
|
async getChatHistory(chatId, page = {}) {
|
|
@@ -11606,10 +11654,23 @@ var ChatService = class {
|
|
|
11606
11654
|
chat.acceptedUserEvents.delete(messageId);
|
|
11607
11655
|
}
|
|
11608
11656
|
}
|
|
11609
|
-
const isLatestPage = page
|
|
11657
|
+
const isLatestPage = isLatestChatHistoryPage(page);
|
|
11610
11658
|
const queuedMessageIds = new Set(chat.provider.getQueue().map((message) => message.id));
|
|
11611
11659
|
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)));
|
|
11612
11660
|
const events = [...history.events, ...acceptedEvents].sort((a, b) => getEventTimestampMs(a) - getEventTimestampMs(b));
|
|
11661
|
+
if (page.limit !== void 0 && "beforeCursor" in history) {
|
|
11662
|
+
const cursor = parseChatHistoryCursor(history.beforeCursor ?? void 0);
|
|
11663
|
+
return {
|
|
11664
|
+
...history,
|
|
11665
|
+
events,
|
|
11666
|
+
goal: history.goal ?? chat.provider.getGoal?.() ?? null,
|
|
11667
|
+
senders: getChatHistoryPageSenders(senders, page),
|
|
11668
|
+
beforeCursor: createChatHistoryCursor({
|
|
11669
|
+
events: cursor?.events ?? null,
|
|
11670
|
+
turns: cursor?.turns ?? null
|
|
11671
|
+
})
|
|
11672
|
+
};
|
|
11673
|
+
}
|
|
11613
11674
|
if (history.eventsStartIndex !== void 0) {
|
|
11614
11675
|
return {
|
|
11615
11676
|
...history,
|
|
@@ -12002,7 +12063,7 @@ var ChatService = class {
|
|
|
12002
12063
|
}
|
|
12003
12064
|
async publish(input) {
|
|
12004
12065
|
const event = {
|
|
12005
|
-
id:
|
|
12066
|
+
id: randomUUID7(),
|
|
12006
12067
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12007
12068
|
...input
|
|
12008
12069
|
};
|
|
@@ -12744,7 +12805,7 @@ ${combinedScript}` : combinedScript;
|
|
|
12744
12805
|
}
|
|
12745
12806
|
|
|
12746
12807
|
// src/services/terminal-service.ts
|
|
12747
|
-
import { randomUUID as
|
|
12808
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
12748
12809
|
import { existsSync as existsSync10 } from "fs";
|
|
12749
12810
|
import { spawn as spawn5 } from "node-pty";
|
|
12750
12811
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
@@ -12763,7 +12824,7 @@ var TerminalService = class {
|
|
|
12763
12824
|
code: "limit"
|
|
12764
12825
|
});
|
|
12765
12826
|
}
|
|
12766
|
-
const id =
|
|
12827
|
+
const id = randomUUID8();
|
|
12767
12828
|
const shell = process.env.SHELL && existsSync10(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
12768
12829
|
const pty = spawn5(shell, ["-l"], {
|
|
12769
12830
|
name: "xterm-256color",
|
|
@@ -12940,6 +13001,22 @@ function jsonError(message, details) {
|
|
|
12940
13001
|
}
|
|
12941
13002
|
function createV1Routes(deps) {
|
|
12942
13003
|
const app2 = new Hono();
|
|
13004
|
+
const loadChatHistory = async (c, chatId, page, failureMessage, unexpectedStatus, legacy = false) => {
|
|
13005
|
+
try {
|
|
13006
|
+
const history = await deps.chatService.getChatHistory(chatId, legacy && page.limit !== void 0 ? {} : page);
|
|
13007
|
+
if (!legacy) return c.json(history);
|
|
13008
|
+
return c.json(paginateChatHistory({
|
|
13009
|
+
...history,
|
|
13010
|
+
senders: getChatHistoryPageSenders(history.senders, page)
|
|
13011
|
+
}, page));
|
|
13012
|
+
} catch (error) {
|
|
13013
|
+
const status = error instanceof ChatNotFoundError ? 404 : unexpectedStatus;
|
|
13014
|
+
return c.json(jsonError(
|
|
13015
|
+
failureMessage,
|
|
13016
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
13017
|
+
), status);
|
|
13018
|
+
}
|
|
13019
|
+
};
|
|
12943
13020
|
app2.get("/events", async () => {
|
|
12944
13021
|
const encoder = new TextEncoder();
|
|
12945
13022
|
let unsubscribe = null;
|
|
@@ -13059,19 +13136,23 @@ function createV1Routes(deps) {
|
|
|
13059
13136
|
return c.json(jsonError("Failed to restore chat", details), 500);
|
|
13060
13137
|
}
|
|
13061
13138
|
});
|
|
13062
|
-
app2.get("/chats/:chatId/history",
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
return c.json(jsonError("Failed to load chat history",
|
|
13139
|
+
app2.get("/chats/:chatId/history", (c) => loadChatHistory(
|
|
13140
|
+
c,
|
|
13141
|
+
c.req.param("chatId"),
|
|
13142
|
+
chatHistoryPageParamsFromQuery(c.req.query()),
|
|
13143
|
+
"Failed to load chat history",
|
|
13144
|
+
404,
|
|
13145
|
+
true
|
|
13146
|
+
));
|
|
13147
|
+
app2.get("/chats/:chatId/history-page", async (c) => {
|
|
13148
|
+
const page = chatHistoryPageParamsFromQuery(c.req.query());
|
|
13149
|
+
if (page.limit === void 0) {
|
|
13150
|
+
return c.json(jsonError("Failed to load chat history page", "limit is required"), 400);
|
|
13151
|
+
}
|
|
13152
|
+
if (page.beforeCursor !== void 0 && !parseChatHistoryCursor(page.beforeCursor)) {
|
|
13153
|
+
return c.json(jsonError("Failed to load chat history page", "invalid cursor"), 400);
|
|
13074
13154
|
}
|
|
13155
|
+
return loadChatHistory(c, c.req.param("chatId"), page, "Failed to load chat history page", 500);
|
|
13075
13156
|
});
|
|
13076
13157
|
app2.get("/chats/:chatId/slash-commands", async (c) => {
|
|
13077
13158
|
try {
|
|
@@ -14058,7 +14139,7 @@ function startStatusBroadcaster() {
|
|
|
14058
14139
|
if (serialized !== previousRepoStatus) {
|
|
14059
14140
|
previousRepoStatus = serialized;
|
|
14060
14141
|
eventService.publish({
|
|
14061
|
-
id:
|
|
14142
|
+
id: randomUUID9(),
|
|
14062
14143
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14063
14144
|
type: "repo.status.changed",
|
|
14064
14145
|
payload: { repos }
|
|
@@ -14079,7 +14160,7 @@ function startStatusBroadcaster() {
|
|
|
14079
14160
|
if (engineStatusJson !== previousEngineStatus) {
|
|
14080
14161
|
previousEngineStatus = engineStatusJson;
|
|
14081
14162
|
eventService.publish({
|
|
14082
|
-
id:
|
|
14163
|
+
id: randomUUID9(),
|
|
14083
14164
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14084
14165
|
type: "engine.status.changed",
|
|
14085
14166
|
payload: { status: engineStatus }
|
|
@@ -14098,7 +14179,7 @@ function startStatusBroadcaster() {
|
|
|
14098
14179
|
previousHookStatus = hookSnapshot;
|
|
14099
14180
|
if (!lastHooksRunning && hooksRunning) {
|
|
14100
14181
|
eventService.publish({
|
|
14101
|
-
id:
|
|
14182
|
+
id: randomUUID9(),
|
|
14102
14183
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14103
14184
|
type: "hooks.started",
|
|
14104
14185
|
payload: { running: true, completed: false }
|
|
@@ -14107,7 +14188,7 @@ function startStatusBroadcaster() {
|
|
|
14107
14188
|
}
|
|
14108
14189
|
if (hooksRunning) {
|
|
14109
14190
|
eventService.publish({
|
|
14110
|
-
id:
|
|
14191
|
+
id: randomUUID9(),
|
|
14111
14192
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14112
14193
|
type: "hooks.progress",
|
|
14113
14194
|
payload: { running: true, completed: false }
|
|
@@ -14116,7 +14197,7 @@ function startStatusBroadcaster() {
|
|
|
14116
14197
|
}
|
|
14117
14198
|
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
14118
14199
|
eventService.publish({
|
|
14119
|
-
id:
|
|
14200
|
+
id: randomUUID9(),
|
|
14120
14201
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14121
14202
|
type: "hooks.completed",
|
|
14122
14203
|
payload: { running: false, completed: true }
|
|
@@ -14125,7 +14206,7 @@ function startStatusBroadcaster() {
|
|
|
14125
14206
|
}
|
|
14126
14207
|
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
14127
14208
|
eventService.publish({
|
|
14128
|
-
id:
|
|
14209
|
+
id: randomUUID9(),
|
|
14129
14210
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14130
14211
|
type: "hooks.failed",
|
|
14131
14212
|
payload: { running: false, completed: hooksCompleted }
|
|
@@ -14133,7 +14214,7 @@ function startStatusBroadcaster() {
|
|
|
14133
14214
|
});
|
|
14134
14215
|
}
|
|
14135
14216
|
eventService.publish({
|
|
14136
|
-
id:
|
|
14217
|
+
id: randomUUID9(),
|
|
14137
14218
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14138
14219
|
type: "hooks.status",
|
|
14139
14220
|
payload: {
|
|
@@ -14188,20 +14269,20 @@ serve(
|
|
|
14188
14269
|
}
|
|
14189
14270
|
const repos = await gitService.listRepos();
|
|
14190
14271
|
await eventService.publish({
|
|
14191
|
-
id:
|
|
14272
|
+
id: randomUUID9(),
|
|
14192
14273
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14193
14274
|
type: "repo.discovered",
|
|
14194
14275
|
payload: { repos }
|
|
14195
14276
|
});
|
|
14196
14277
|
const repoStatuses = await gitService.listRepos();
|
|
14197
14278
|
await eventService.publish({
|
|
14198
|
-
id:
|
|
14279
|
+
id: randomUUID9(),
|
|
14199
14280
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14200
14281
|
type: "repo.status.changed",
|
|
14201
14282
|
payload: { repos: repoStatuses }
|
|
14202
14283
|
});
|
|
14203
14284
|
await eventService.publish({
|
|
14204
|
-
id:
|
|
14285
|
+
id: randomUUID9(),
|
|
14205
14286
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14206
14287
|
type: "engine.ready",
|
|
14207
14288
|
payload: { version: "v1" }
|