codex-mobile-re 0.1.131 → 0.1.133
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/dist/assets/{AutomationsPanel-CZ_k1aNL.js → AutomationsPanel-B9A37CNa.js} +1 -1
- package/dist/assets/DirectoryHub-D_TdDpHF.js +2 -0
- package/dist/assets/{QueuedMessages-DSxRli0y.js → QueuedMessages-CsAVexKb.js} +1 -1
- package/dist/assets/{ReviewPane-DO60w4j5.js → ReviewPane-XhRr3646.js} +1 -1
- package/dist/assets/{RightFilePreview-B2tx1lOm.js → RightFilePreview-Ch_neGS2.js} +1 -1
- package/dist/assets/RightFilesPanel-5fMx5J84.js +1 -0
- package/dist/assets/RightGitPanel-irYPFJZZ.js +2 -0
- package/dist/assets/{SettingsDialog-D9kB2mzO.js → SettingsDialog-Dg10TF-D.js} +3 -3
- package/dist/assets/ThreadConversation-8TMOUqdg.js +25 -0
- package/dist/assets/ThreadConversation-Dx1ZKW94.css +1 -0
- package/dist/assets/{ThreadPendingRequestPanel-DGbhyPFH.js → ThreadPendingRequestPanel-DorbAtp9.js} +1 -1
- package/dist/assets/{ThreadTerminalPanel-DhYhXg8u.js → ThreadTerminalPanel-CKhCiseu.js} +1 -1
- package/dist/assets/index-CMrfiiel.js +98 -0
- package/dist/assets/index-Dm1iQsik.css +1 -0
- package/dist/index.html +2 -2
- package/dist-cli/index.js +257 -221
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/DirectoryHub-Yd0fIqxf.js +0 -2
- package/dist/assets/RightFilesPanel-r9ijJqw4.js +0 -1
- package/dist/assets/RightGitPanel-BxZdreeN.js +0 -2
- package/dist/assets/ThreadConversation-CVVohemt.js +0 -39
- package/dist/assets/ThreadConversation-D1JHRys4.css +0 -1
- package/dist/assets/index-ClI-fbXf.js +0 -83
- package/dist/assets/index-DthBwStG.css +0 -1
package/dist/index.html
CHANGED
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/ibm-plex-sans-400.woff2" crossorigin />
|
|
37
37
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/ibm-plex-mono-400.woff2" crossorigin />
|
|
38
38
|
<title>Codex Web</title>
|
|
39
|
-
<script type="module" crossorigin src="/assets/index-
|
|
40
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
39
|
+
<script type="module" crossorigin src="/assets/index-CMrfiiel.js"></script>
|
|
40
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Dm1iQsik.css">
|
|
41
41
|
</head>
|
|
42
42
|
<body>
|
|
43
43
|
<div id="app"></div>
|
package/dist-cli/index.js
CHANGED
|
@@ -5077,6 +5077,210 @@ async function handleSkillsRoutes(req, res, url, context) {
|
|
|
5077
5077
|
// src/server/telegramThreadBridge.ts
|
|
5078
5078
|
import { basename as basename2 } from "path";
|
|
5079
5079
|
|
|
5080
|
+
// src/server/bridge/threadTurnPage.ts
|
|
5081
|
+
var ThreadTurnPageUnsupportedError = class extends Error {
|
|
5082
|
+
constructor(cause) {
|
|
5083
|
+
super(`thread/turns/list is not supported by this app-server: ${getErrorMessage(cause, "unknown error")}`);
|
|
5084
|
+
this.name = "ThreadTurnPageUnsupportedError";
|
|
5085
|
+
}
|
|
5086
|
+
};
|
|
5087
|
+
var TURN_LIST_UNSUPPORTED_PATTERN = /-32601|not supported|not implemented|unknown variant|method not found|unknown method/i;
|
|
5088
|
+
function isTurnListUnsupportedError(error) {
|
|
5089
|
+
return TURN_LIST_UNSUPPORTED_PATTERN.test(getErrorMessage(error, ""));
|
|
5090
|
+
}
|
|
5091
|
+
var TURN_ID_PAGE_SIZE = 1e4;
|
|
5092
|
+
var TURN_ID_MAX_PAGES = 50;
|
|
5093
|
+
var TURN_LIST_SERVER_PAGE_CLAMP = 100;
|
|
5094
|
+
var ThreadTurnPageCursorChain = class {
|
|
5095
|
+
constructor(options = {}) {
|
|
5096
|
+
this.cursorByThreadId = /* @__PURE__ */ new Map();
|
|
5097
|
+
this.maxThreads = options.maxThreads ?? 64;
|
|
5098
|
+
this.maxTurnsPerThread = options.maxTurnsPerThread ?? 256;
|
|
5099
|
+
}
|
|
5100
|
+
/** Record that `olderCursor` reaches the turns just before `oldestTurnId`. */
|
|
5101
|
+
record(threadId, oldestTurnId, olderCursor) {
|
|
5102
|
+
if (!threadId || !oldestTurnId) return;
|
|
5103
|
+
const cursor = readNonEmptyString(olderCursor);
|
|
5104
|
+
if (!cursor) return;
|
|
5105
|
+
let perThread = this.cursorByThreadId.get(threadId);
|
|
5106
|
+
if (!perThread) {
|
|
5107
|
+
perThread = /* @__PURE__ */ new Map();
|
|
5108
|
+
this.cursorByThreadId.set(threadId, perThread);
|
|
5109
|
+
if (this.cursorByThreadId.size > this.maxThreads) {
|
|
5110
|
+
const oldestThreadId = this.cursorByThreadId.keys().next().value;
|
|
5111
|
+
if (typeof oldestThreadId === "string") this.cursorByThreadId.delete(oldestThreadId);
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
perThread.delete(oldestTurnId);
|
|
5115
|
+
perThread.set(oldestTurnId, cursor);
|
|
5116
|
+
if (perThread.size > this.maxTurnsPerThread) {
|
|
5117
|
+
const oldestTurnId2 = perThread.keys().next().value;
|
|
5118
|
+
if (typeof oldestTurnId2 === "string") perThread.delete(oldestTurnId2);
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
lookup(threadId, turnId) {
|
|
5122
|
+
if (!threadId || !turnId) return null;
|
|
5123
|
+
const perThread = this.cursorByThreadId.get(threadId);
|
|
5124
|
+
if (!perThread) return null;
|
|
5125
|
+
const cursor = perThread.get(turnId);
|
|
5126
|
+
if (cursor === void 0) return null;
|
|
5127
|
+
perThread.delete(turnId);
|
|
5128
|
+
perThread.set(turnId, cursor);
|
|
5129
|
+
return cursor;
|
|
5130
|
+
}
|
|
5131
|
+
clear(threadId) {
|
|
5132
|
+
if (threadId) this.cursorByThreadId.delete(threadId);
|
|
5133
|
+
else this.cursorByThreadId.clear();
|
|
5134
|
+
}
|
|
5135
|
+
/**
|
|
5136
|
+
* Restore a persisted snapshot (round-136). Entries go in through `record`,
|
|
5137
|
+
* so the caps and the least-recently-recorded eviction order apply to restored
|
|
5138
|
+
* anchors exactly as they do to fresh ones -- the caller therefore feeds the
|
|
5139
|
+
* snapshot oldest-first, and a snapshot older than the caps degrades to "the
|
|
5140
|
+
* newest entries survive" rather than to a rejected file.
|
|
5141
|
+
*/
|
|
5142
|
+
hydrate(state) {
|
|
5143
|
+
if (!state) return;
|
|
5144
|
+
for (const [threadId, perThread] of Object.entries(state)) {
|
|
5145
|
+
for (const [turnId, cursor] of Object.entries(perThread ?? {})) {
|
|
5146
|
+
this.record(threadId, turnId, cursor);
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
/** What is currently known, oldest registration first (the persist order). */
|
|
5151
|
+
snapshot() {
|
|
5152
|
+
const state = {};
|
|
5153
|
+
for (const [threadId, perThread] of this.cursorByThreadId) {
|
|
5154
|
+
const entries = {};
|
|
5155
|
+
for (const [turnId, cursor] of perThread) entries[turnId] = cursor;
|
|
5156
|
+
if (Object.keys(entries).length > 0) state[threadId] = entries;
|
|
5157
|
+
}
|
|
5158
|
+
return state;
|
|
5159
|
+
}
|
|
5160
|
+
};
|
|
5161
|
+
async function readThreadTurnIds(rpc, threadId) {
|
|
5162
|
+
if (!threadId) return null;
|
|
5163
|
+
const descending = [];
|
|
5164
|
+
let cursor = "";
|
|
5165
|
+
for (let page = 0; page < TURN_ID_MAX_PAGES; page += 1) {
|
|
5166
|
+
const params = {
|
|
5167
|
+
threadId,
|
|
5168
|
+
limit: TURN_ID_PAGE_SIZE,
|
|
5169
|
+
sortDirection: "desc",
|
|
5170
|
+
itemsView: "notLoaded"
|
|
5171
|
+
};
|
|
5172
|
+
if (cursor) params.cursor = cursor;
|
|
5173
|
+
let result;
|
|
5174
|
+
try {
|
|
5175
|
+
result = await rpc("thread/turns/list", params);
|
|
5176
|
+
} catch (error) {
|
|
5177
|
+
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
5178
|
+
return null;
|
|
5179
|
+
}
|
|
5180
|
+
const record = asRecord(result);
|
|
5181
|
+
if (!record || !Array.isArray(record.data)) return null;
|
|
5182
|
+
for (const turn of record.data) {
|
|
5183
|
+
const turnId = readNonEmptyString(asRecord(turn)?.id);
|
|
5184
|
+
if (!turnId) return null;
|
|
5185
|
+
descending.push(turnId);
|
|
5186
|
+
}
|
|
5187
|
+
const nextCursor = readNonEmptyString(record.nextCursor);
|
|
5188
|
+
if (!nextCursor) return descending.reverse();
|
|
5189
|
+
cursor = nextCursor;
|
|
5190
|
+
}
|
|
5191
|
+
return null;
|
|
5192
|
+
}
|
|
5193
|
+
async function buildThreadTurnPage(rpc, threadId, turns, startTurnIndex, hasMoreOlder) {
|
|
5194
|
+
let metadata;
|
|
5195
|
+
try {
|
|
5196
|
+
metadata = await rpc("thread/read", { threadId, includeTurns: false });
|
|
5197
|
+
} catch {
|
|
5198
|
+
return null;
|
|
5199
|
+
}
|
|
5200
|
+
const record = asRecord(metadata);
|
|
5201
|
+
const thread = asRecord(record?.thread);
|
|
5202
|
+
if (!record || !thread) return null;
|
|
5203
|
+
return {
|
|
5204
|
+
result: { ...record, thread: { ...thread, turns } },
|
|
5205
|
+
startTurnIndex,
|
|
5206
|
+
hasMoreOlder
|
|
5207
|
+
};
|
|
5208
|
+
}
|
|
5209
|
+
async function readBoundedThreadTurnPage(deps, threadId, beforeTurnId, limit) {
|
|
5210
|
+
if (!threadId || !beforeTurnId) return null;
|
|
5211
|
+
const pageSize = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 1;
|
|
5212
|
+
const ids = await readThreadTurnIds(deps.rpc, threadId);
|
|
5213
|
+
if (!ids) return null;
|
|
5214
|
+
const beforeIndex = ids.indexOf(beforeTurnId);
|
|
5215
|
+
if (beforeIndex < 0) {
|
|
5216
|
+
return await buildThreadTurnPage(deps.rpc, threadId, [], 0, false);
|
|
5217
|
+
}
|
|
5218
|
+
const startTurnIndex = Math.max(0, beforeIndex - pageSize);
|
|
5219
|
+
const wanted = beforeIndex - startTurnIndex;
|
|
5220
|
+
let turns = [];
|
|
5221
|
+
let boundaryTurnId = "";
|
|
5222
|
+
let boundaryCursor = null;
|
|
5223
|
+
if (wanted > 0) {
|
|
5224
|
+
const cursor = await resolveChainCursor(deps, threadId, ids, beforeIndex);
|
|
5225
|
+
if (!cursor) return null;
|
|
5226
|
+
let page;
|
|
5227
|
+
try {
|
|
5228
|
+
page = await deps.rpc("thread/turns/list", {
|
|
5229
|
+
threadId,
|
|
5230
|
+
cursor,
|
|
5231
|
+
sortDirection: "desc",
|
|
5232
|
+
limit: wanted,
|
|
5233
|
+
itemsView: "full"
|
|
5234
|
+
});
|
|
5235
|
+
} catch (error) {
|
|
5236
|
+
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
5237
|
+
return null;
|
|
5238
|
+
}
|
|
5239
|
+
const record = asRecord(page);
|
|
5240
|
+
const data = Array.isArray(record?.data) ? record.data : null;
|
|
5241
|
+
if (!data || data.length !== wanted) return null;
|
|
5242
|
+
const ascending = [...data].reverse();
|
|
5243
|
+
if (readNonEmptyString(asRecord(ascending[0])?.id) !== ids[startTurnIndex]) return null;
|
|
5244
|
+
if (wanted > 1 && readNonEmptyString(asRecord(ascending[wanted - 1])?.id) !== ids[beforeIndex - 1]) {
|
|
5245
|
+
return null;
|
|
5246
|
+
}
|
|
5247
|
+
turns = ascending;
|
|
5248
|
+
boundaryTurnId = ids[startTurnIndex];
|
|
5249
|
+
boundaryCursor = record?.nextCursor ?? null;
|
|
5250
|
+
}
|
|
5251
|
+
const built = await buildThreadTurnPage(deps.rpc, threadId, turns, startTurnIndex, startTurnIndex > 0);
|
|
5252
|
+
if (!built) return null;
|
|
5253
|
+
if (boundaryTurnId) deps.chain.record(threadId, boundaryTurnId, boundaryCursor);
|
|
5254
|
+
return built;
|
|
5255
|
+
}
|
|
5256
|
+
async function resolveChainCursor(deps, threadId, ids, beforeIndex) {
|
|
5257
|
+
const anchorTurnId = ids[beforeIndex];
|
|
5258
|
+
if (!anchorTurnId) return null;
|
|
5259
|
+
const known = deps.chain.lookup(threadId, anchorTurnId);
|
|
5260
|
+
if (known) return known;
|
|
5261
|
+
const span = ids.length - beforeIndex;
|
|
5262
|
+
if (span < 1 || span > TURN_LIST_SERVER_PAGE_CLAMP) return null;
|
|
5263
|
+
let page;
|
|
5264
|
+
try {
|
|
5265
|
+
page = await deps.rpc("thread/turns/list", {
|
|
5266
|
+
threadId,
|
|
5267
|
+
limit: span,
|
|
5268
|
+
sortDirection: "desc",
|
|
5269
|
+
itemsView: "notLoaded"
|
|
5270
|
+
});
|
|
5271
|
+
} catch (error) {
|
|
5272
|
+
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
5273
|
+
return null;
|
|
5274
|
+
}
|
|
5275
|
+
const record = asRecord(page);
|
|
5276
|
+
const data = Array.isArray(record?.data) ? record.data : null;
|
|
5277
|
+
const oldestTurnId = data ? readNonEmptyString(asRecord(data[data.length - 1])?.id) : "";
|
|
5278
|
+
const cursor = readNonEmptyString(record?.nextCursor);
|
|
5279
|
+
if (!data || oldestTurnId !== anchorTurnId || !cursor) return null;
|
|
5280
|
+
deps.chain.record(threadId, anchorTurnId, cursor);
|
|
5281
|
+
return cursor;
|
|
5282
|
+
}
|
|
5283
|
+
|
|
5080
5284
|
// src/server/bridge/threadResumeTurnPage.ts
|
|
5081
5285
|
var TURN_COUNT_PAGE_SIZE = 1e4;
|
|
5082
5286
|
var TURN_COUNT_MAX_PAGES = 50;
|
|
@@ -5132,6 +5336,35 @@ async function readThreadTurnCount(rpc, threadId) {
|
|
|
5132
5336
|
}
|
|
5133
5337
|
return null;
|
|
5134
5338
|
}
|
|
5339
|
+
var TERMINAL_TURN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "declined", "interrupted", "error"]);
|
|
5340
|
+
function readTurnStatus(turn) {
|
|
5341
|
+
const record = asRecord(turn);
|
|
5342
|
+
const raw = record?.status;
|
|
5343
|
+
const text = readNonEmptyString(raw) || readNonEmptyString(asRecord(raw)?.type);
|
|
5344
|
+
return text.toLowerCase();
|
|
5345
|
+
}
|
|
5346
|
+
function dropTurnsMissingFromIndex(pageData, indexTurnIds) {
|
|
5347
|
+
const known = new Set(indexTurnIds);
|
|
5348
|
+
return pageData.filter((turn) => {
|
|
5349
|
+
const turnId = readNonEmptyString(asRecord(turn)?.id);
|
|
5350
|
+
if (!turnId || known.has(turnId)) return true;
|
|
5351
|
+
return !TERMINAL_TURN_STATUSES.has(readTurnStatus(turn));
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5354
|
+
async function readAuthoritativeTurnIds(rpc, threadId) {
|
|
5355
|
+
if (!threadId) return null;
|
|
5356
|
+
try {
|
|
5357
|
+
return await readThreadTurnIds(rpc, threadId);
|
|
5358
|
+
} catch {
|
|
5359
|
+
return null;
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
function withResumePageTurns(result, turns) {
|
|
5363
|
+
const record = asRecord(result);
|
|
5364
|
+
const page = asRecord(record?.initialTurnsPage);
|
|
5365
|
+
if (!record || !page) return result;
|
|
5366
|
+
return { ...record, initialTurnsPage: { ...page, data: turns } };
|
|
5367
|
+
}
|
|
5135
5368
|
function promoteResumeTurnPage(result, totalTurnCount) {
|
|
5136
5369
|
const record = asRecord(result);
|
|
5137
5370
|
const thread = asRecord(record?.thread);
|
|
@@ -5154,28 +5387,35 @@ async function resumeThreadWithTurnPage(deps, originalParams) {
|
|
|
5154
5387
|
if (!isThreadResumeTurnPageSupported(result)) {
|
|
5155
5388
|
return await deps.sendResume(originalParams);
|
|
5156
5389
|
}
|
|
5157
|
-
const
|
|
5390
|
+
const threadId = readNonEmptyString(asRecord(asRecord(result)?.thread)?.id);
|
|
5158
5391
|
const page = asRecord(asRecord(result)?.initialTurnsPage);
|
|
5392
|
+
const pageData = readPageData(result) ?? [];
|
|
5393
|
+
const indexTurnIds = await readAuthoritativeTurnIds(deps.rpc, threadId);
|
|
5159
5394
|
if (pageData.length === 0) {
|
|
5160
|
-
|
|
5161
|
-
const turnCount = await readThreadTurnCount(deps.rpc, threadId);
|
|
5162
|
-
if (turnCount === null || turnCount > 0) return await deps.sendResume(originalParams);
|
|
5395
|
+
if (indexTurnIds === null || indexTurnIds.length > 0) return await deps.sendResume(originalParams);
|
|
5163
5396
|
}
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
readNonEmptyString(
|
|
5170
|
-
|
|
5397
|
+
let promoted = result;
|
|
5398
|
+
let pageTurns = pageData;
|
|
5399
|
+
if (pageData.length > 0 && indexTurnIds !== null && indexTurnIds.length > 0) {
|
|
5400
|
+
const kept = dropTurnsMissingFromIndex(pageData, indexTurnIds);
|
|
5401
|
+
if (kept.length !== pageData.length) {
|
|
5402
|
+
const oldestKeptTurnId = readNonEmptyString(asRecord(kept[kept.length - 1])?.id);
|
|
5403
|
+
const anchored = kept.length > 0 && oldestKeptTurnId !== "" && indexTurnIds[indexTurnIds.length - kept.length] === oldestKeptTurnId;
|
|
5404
|
+
if (anchored) {
|
|
5405
|
+
promoted = withResumePageTurns(result, kept);
|
|
5406
|
+
pageTurns = kept;
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5171
5409
|
}
|
|
5172
|
-
const
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
const threadId = readNonEmptyString(asRecord(asRecord(result)?.thread)?.id);
|
|
5176
|
-
totalTurnCount = await readThreadTurnCount(deps.rpc, threadId);
|
|
5410
|
+
const oldestTurnId = readNonEmptyString(asRecord(pageTurns[pageTurns.length - 1])?.id);
|
|
5411
|
+
if (oldestTurnId && deps.onTurnPageBoundary) {
|
|
5412
|
+
deps.onTurnPageBoundary(threadId, oldestTurnId, readNonEmptyString(page?.nextCursor) || null);
|
|
5177
5413
|
}
|
|
5178
|
-
|
|
5414
|
+
const needsCount = pageTurns.length > 0 && readNonEmptyString(page?.nextCursor) !== "";
|
|
5415
|
+
let totalTurnCount = pageTurns.length;
|
|
5416
|
+
if (indexTurnIds !== null) totalTurnCount = indexTurnIds.length;
|
|
5417
|
+
else if (needsCount) totalTurnCount = await readThreadTurnCount(deps.rpc, threadId);
|
|
5418
|
+
return promoteResumeTurnPage(promoted, totalTurnCount);
|
|
5179
5419
|
}
|
|
5180
5420
|
|
|
5181
5421
|
// src/server/bridge/threadReadTurnPage.ts
|
|
@@ -12006,210 +12246,6 @@ async function runRpcResponsePipeline(deps, method, rpcResult) {
|
|
|
12006
12246
|
return method === "thread/list" ? overlayExternalSessionOnThreadList(externalSessionTracker, dedupedResult) : THREAD_METHODS_WITH_TURNS.has(method) ? overlayExternalSessionOnThreadResult(externalSessionTracker, dedupedResult) : dedupedResult;
|
|
12007
12247
|
}
|
|
12008
12248
|
|
|
12009
|
-
// src/server/bridge/threadTurnPage.ts
|
|
12010
|
-
var ThreadTurnPageUnsupportedError = class extends Error {
|
|
12011
|
-
constructor(cause) {
|
|
12012
|
-
super(`thread/turns/list is not supported by this app-server: ${getErrorMessage(cause, "unknown error")}`);
|
|
12013
|
-
this.name = "ThreadTurnPageUnsupportedError";
|
|
12014
|
-
}
|
|
12015
|
-
};
|
|
12016
|
-
var TURN_LIST_UNSUPPORTED_PATTERN = /-32601|not supported|not implemented|unknown variant|method not found|unknown method/i;
|
|
12017
|
-
function isTurnListUnsupportedError(error) {
|
|
12018
|
-
return TURN_LIST_UNSUPPORTED_PATTERN.test(getErrorMessage(error, ""));
|
|
12019
|
-
}
|
|
12020
|
-
var TURN_ID_PAGE_SIZE = 1e4;
|
|
12021
|
-
var TURN_ID_MAX_PAGES = 50;
|
|
12022
|
-
var TURN_LIST_SERVER_PAGE_CLAMP = 100;
|
|
12023
|
-
var ThreadTurnPageCursorChain = class {
|
|
12024
|
-
constructor(options = {}) {
|
|
12025
|
-
this.cursorByThreadId = /* @__PURE__ */ new Map();
|
|
12026
|
-
this.maxThreads = options.maxThreads ?? 64;
|
|
12027
|
-
this.maxTurnsPerThread = options.maxTurnsPerThread ?? 256;
|
|
12028
|
-
}
|
|
12029
|
-
/** Record that `olderCursor` reaches the turns just before `oldestTurnId`. */
|
|
12030
|
-
record(threadId, oldestTurnId, olderCursor) {
|
|
12031
|
-
if (!threadId || !oldestTurnId) return;
|
|
12032
|
-
const cursor = readNonEmptyString(olderCursor);
|
|
12033
|
-
if (!cursor) return;
|
|
12034
|
-
let perThread = this.cursorByThreadId.get(threadId);
|
|
12035
|
-
if (!perThread) {
|
|
12036
|
-
perThread = /* @__PURE__ */ new Map();
|
|
12037
|
-
this.cursorByThreadId.set(threadId, perThread);
|
|
12038
|
-
if (this.cursorByThreadId.size > this.maxThreads) {
|
|
12039
|
-
const oldestThreadId = this.cursorByThreadId.keys().next().value;
|
|
12040
|
-
if (typeof oldestThreadId === "string") this.cursorByThreadId.delete(oldestThreadId);
|
|
12041
|
-
}
|
|
12042
|
-
}
|
|
12043
|
-
perThread.delete(oldestTurnId);
|
|
12044
|
-
perThread.set(oldestTurnId, cursor);
|
|
12045
|
-
if (perThread.size > this.maxTurnsPerThread) {
|
|
12046
|
-
const oldestTurnId2 = perThread.keys().next().value;
|
|
12047
|
-
if (typeof oldestTurnId2 === "string") perThread.delete(oldestTurnId2);
|
|
12048
|
-
}
|
|
12049
|
-
}
|
|
12050
|
-
lookup(threadId, turnId) {
|
|
12051
|
-
if (!threadId || !turnId) return null;
|
|
12052
|
-
const perThread = this.cursorByThreadId.get(threadId);
|
|
12053
|
-
if (!perThread) return null;
|
|
12054
|
-
const cursor = perThread.get(turnId);
|
|
12055
|
-
if (cursor === void 0) return null;
|
|
12056
|
-
perThread.delete(turnId);
|
|
12057
|
-
perThread.set(turnId, cursor);
|
|
12058
|
-
return cursor;
|
|
12059
|
-
}
|
|
12060
|
-
clear(threadId) {
|
|
12061
|
-
if (threadId) this.cursorByThreadId.delete(threadId);
|
|
12062
|
-
else this.cursorByThreadId.clear();
|
|
12063
|
-
}
|
|
12064
|
-
/**
|
|
12065
|
-
* Restore a persisted snapshot (round-136). Entries go in through `record`,
|
|
12066
|
-
* so the caps and the least-recently-recorded eviction order apply to restored
|
|
12067
|
-
* anchors exactly as they do to fresh ones -- the caller therefore feeds the
|
|
12068
|
-
* snapshot oldest-first, and a snapshot older than the caps degrades to "the
|
|
12069
|
-
* newest entries survive" rather than to a rejected file.
|
|
12070
|
-
*/
|
|
12071
|
-
hydrate(state) {
|
|
12072
|
-
if (!state) return;
|
|
12073
|
-
for (const [threadId, perThread] of Object.entries(state)) {
|
|
12074
|
-
for (const [turnId, cursor] of Object.entries(perThread ?? {})) {
|
|
12075
|
-
this.record(threadId, turnId, cursor);
|
|
12076
|
-
}
|
|
12077
|
-
}
|
|
12078
|
-
}
|
|
12079
|
-
/** What is currently known, oldest registration first (the persist order). */
|
|
12080
|
-
snapshot() {
|
|
12081
|
-
const state = {};
|
|
12082
|
-
for (const [threadId, perThread] of this.cursorByThreadId) {
|
|
12083
|
-
const entries = {};
|
|
12084
|
-
for (const [turnId, cursor] of perThread) entries[turnId] = cursor;
|
|
12085
|
-
if (Object.keys(entries).length > 0) state[threadId] = entries;
|
|
12086
|
-
}
|
|
12087
|
-
return state;
|
|
12088
|
-
}
|
|
12089
|
-
};
|
|
12090
|
-
async function readThreadTurnIds(rpc, threadId) {
|
|
12091
|
-
if (!threadId) return null;
|
|
12092
|
-
const descending = [];
|
|
12093
|
-
let cursor = "";
|
|
12094
|
-
for (let page = 0; page < TURN_ID_MAX_PAGES; page += 1) {
|
|
12095
|
-
const params = {
|
|
12096
|
-
threadId,
|
|
12097
|
-
limit: TURN_ID_PAGE_SIZE,
|
|
12098
|
-
sortDirection: "desc",
|
|
12099
|
-
itemsView: "notLoaded"
|
|
12100
|
-
};
|
|
12101
|
-
if (cursor) params.cursor = cursor;
|
|
12102
|
-
let result;
|
|
12103
|
-
try {
|
|
12104
|
-
result = await rpc("thread/turns/list", params);
|
|
12105
|
-
} catch (error) {
|
|
12106
|
-
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
12107
|
-
return null;
|
|
12108
|
-
}
|
|
12109
|
-
const record = asRecord(result);
|
|
12110
|
-
if (!record || !Array.isArray(record.data)) return null;
|
|
12111
|
-
for (const turn of record.data) {
|
|
12112
|
-
const turnId = readNonEmptyString(asRecord(turn)?.id);
|
|
12113
|
-
if (!turnId) return null;
|
|
12114
|
-
descending.push(turnId);
|
|
12115
|
-
}
|
|
12116
|
-
const nextCursor = readNonEmptyString(record.nextCursor);
|
|
12117
|
-
if (!nextCursor) return descending.reverse();
|
|
12118
|
-
cursor = nextCursor;
|
|
12119
|
-
}
|
|
12120
|
-
return null;
|
|
12121
|
-
}
|
|
12122
|
-
async function buildThreadTurnPage(rpc, threadId, turns, startTurnIndex, hasMoreOlder) {
|
|
12123
|
-
let metadata;
|
|
12124
|
-
try {
|
|
12125
|
-
metadata = await rpc("thread/read", { threadId, includeTurns: false });
|
|
12126
|
-
} catch {
|
|
12127
|
-
return null;
|
|
12128
|
-
}
|
|
12129
|
-
const record = asRecord(metadata);
|
|
12130
|
-
const thread = asRecord(record?.thread);
|
|
12131
|
-
if (!record || !thread) return null;
|
|
12132
|
-
return {
|
|
12133
|
-
result: { ...record, thread: { ...thread, turns } },
|
|
12134
|
-
startTurnIndex,
|
|
12135
|
-
hasMoreOlder
|
|
12136
|
-
};
|
|
12137
|
-
}
|
|
12138
|
-
async function readBoundedThreadTurnPage(deps, threadId, beforeTurnId, limit) {
|
|
12139
|
-
if (!threadId || !beforeTurnId) return null;
|
|
12140
|
-
const pageSize = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 1;
|
|
12141
|
-
const ids = await readThreadTurnIds(deps.rpc, threadId);
|
|
12142
|
-
if (!ids) return null;
|
|
12143
|
-
const beforeIndex = ids.indexOf(beforeTurnId);
|
|
12144
|
-
if (beforeIndex < 0) {
|
|
12145
|
-
return await buildThreadTurnPage(deps.rpc, threadId, [], 0, false);
|
|
12146
|
-
}
|
|
12147
|
-
const startTurnIndex = Math.max(0, beforeIndex - pageSize);
|
|
12148
|
-
const wanted = beforeIndex - startTurnIndex;
|
|
12149
|
-
let turns = [];
|
|
12150
|
-
let boundaryTurnId = "";
|
|
12151
|
-
let boundaryCursor = null;
|
|
12152
|
-
if (wanted > 0) {
|
|
12153
|
-
const cursor = await resolveChainCursor(deps, threadId, ids, beforeIndex);
|
|
12154
|
-
if (!cursor) return null;
|
|
12155
|
-
let page;
|
|
12156
|
-
try {
|
|
12157
|
-
page = await deps.rpc("thread/turns/list", {
|
|
12158
|
-
threadId,
|
|
12159
|
-
cursor,
|
|
12160
|
-
sortDirection: "desc",
|
|
12161
|
-
limit: wanted,
|
|
12162
|
-
itemsView: "full"
|
|
12163
|
-
});
|
|
12164
|
-
} catch (error) {
|
|
12165
|
-
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
12166
|
-
return null;
|
|
12167
|
-
}
|
|
12168
|
-
const record = asRecord(page);
|
|
12169
|
-
const data = Array.isArray(record?.data) ? record.data : null;
|
|
12170
|
-
if (!data || data.length !== wanted) return null;
|
|
12171
|
-
const ascending = [...data].reverse();
|
|
12172
|
-
if (readNonEmptyString(asRecord(ascending[0])?.id) !== ids[startTurnIndex]) return null;
|
|
12173
|
-
if (wanted > 1 && readNonEmptyString(asRecord(ascending[wanted - 1])?.id) !== ids[beforeIndex - 1]) {
|
|
12174
|
-
return null;
|
|
12175
|
-
}
|
|
12176
|
-
turns = ascending;
|
|
12177
|
-
boundaryTurnId = ids[startTurnIndex];
|
|
12178
|
-
boundaryCursor = record?.nextCursor ?? null;
|
|
12179
|
-
}
|
|
12180
|
-
const built = await buildThreadTurnPage(deps.rpc, threadId, turns, startTurnIndex, startTurnIndex > 0);
|
|
12181
|
-
if (!built) return null;
|
|
12182
|
-
if (boundaryTurnId) deps.chain.record(threadId, boundaryTurnId, boundaryCursor);
|
|
12183
|
-
return built;
|
|
12184
|
-
}
|
|
12185
|
-
async function resolveChainCursor(deps, threadId, ids, beforeIndex) {
|
|
12186
|
-
const anchorTurnId = ids[beforeIndex];
|
|
12187
|
-
if (!anchorTurnId) return null;
|
|
12188
|
-
const known = deps.chain.lookup(threadId, anchorTurnId);
|
|
12189
|
-
if (known) return known;
|
|
12190
|
-
const span = ids.length - beforeIndex;
|
|
12191
|
-
if (span < 1 || span > TURN_LIST_SERVER_PAGE_CLAMP) return null;
|
|
12192
|
-
let page;
|
|
12193
|
-
try {
|
|
12194
|
-
page = await deps.rpc("thread/turns/list", {
|
|
12195
|
-
threadId,
|
|
12196
|
-
limit: span,
|
|
12197
|
-
sortDirection: "desc",
|
|
12198
|
-
itemsView: "notLoaded"
|
|
12199
|
-
});
|
|
12200
|
-
} catch (error) {
|
|
12201
|
-
if (isTurnListUnsupportedError(error)) throw new ThreadTurnPageUnsupportedError(error);
|
|
12202
|
-
return null;
|
|
12203
|
-
}
|
|
12204
|
-
const record = asRecord(page);
|
|
12205
|
-
const data = Array.isArray(record?.data) ? record.data : null;
|
|
12206
|
-
const oldestTurnId = data ? readNonEmptyString(asRecord(data[data.length - 1])?.id) : "";
|
|
12207
|
-
const cursor = readNonEmptyString(record?.nextCursor);
|
|
12208
|
-
if (!data || oldestTurnId !== anchorTurnId || !cursor) return null;
|
|
12209
|
-
deps.chain.record(threadId, anchorTurnId, cursor);
|
|
12210
|
-
return cursor;
|
|
12211
|
-
}
|
|
12212
|
-
|
|
12213
12249
|
// src/server/bridge/rollbackTurnContext.ts
|
|
12214
12250
|
async function readRollbackTurnContext(appServer, threadId) {
|
|
12215
12251
|
if (!threadId) return null;
|