dsh-context 0.69.1 → 0.70.0
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 +3 -3
- package/lib/client.js +11 -11
- package/lib/index.d.ts +6 -0
- package/lib/index.js +430 -68
- package/package.json +3 -3
package/lib/index.d.ts
CHANGED
|
@@ -57,6 +57,9 @@ interface TimelineState {
|
|
|
57
57
|
provider?: string;
|
|
58
58
|
lastModel?: string;
|
|
59
59
|
contextWindow?: number;
|
|
60
|
+
/** The newest `request/header` envelope's routed output cap (dsh materializes the adapter default
|
|
61
|
+
* into it). Last-wins: a header omitting the cap clears it. */
|
|
62
|
+
maxOutputTokens?: number;
|
|
60
63
|
requests: RequestRecord[];
|
|
61
64
|
/** The number of turn runs in `requests` (a run = consecutive records sharing one `turn`). Maintained
|
|
62
65
|
* incrementally at the single append site and recomputed when a trim replaces the array, so the retention
|
|
@@ -249,6 +252,9 @@ interface Snapshot {
|
|
|
249
252
|
model?: string;
|
|
250
253
|
provider?: string;
|
|
251
254
|
contextWindow?: number;
|
|
255
|
+
/** The newest request envelope's routed output cap, which auto-compaction reserves before pricing
|
|
256
|
+
* its pressure trigger. Absent = no cap in the log (or a row folded before the field existed). */
|
|
257
|
+
maxOutputTokens?: number;
|
|
252
258
|
current: {
|
|
253
259
|
system: number;
|
|
254
260
|
tools: number;
|
package/lib/index.js
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dsh-context v0.
|
|
2
|
+
* dsh-context v0.70.0
|
|
3
3
|
* A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.
|
|
4
4
|
* @author bowenliang123
|
|
5
5
|
* @license Apache-2.0
|
|
6
6
|
* @homepage https://github.com/bowenliang123/dsh-context
|
|
7
|
-
* @built 2026-10-
|
|
8
|
-
* @commit
|
|
9
|
-
* @commit-date 2026-10-
|
|
7
|
+
* @built 2026-10-13T23:50:32+00:00
|
|
8
|
+
* @commit 854ce14aad18
|
|
9
|
+
* @commit-date 2026-10-14T07:45:49+08:00
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { deriveEventMessage, interruptedTurnClosers } from "@deepseek-ai/dsh-session";
|
|
15
15
|
import z$1 from "@deepseek-ai/schemastery";
|
|
16
|
+
import { copyFile, link, mkdir } from "node:fs/promises";
|
|
17
|
+
import { basename, dirname, isAbsolute, join, normalize, relative } from "node:path";
|
|
18
|
+
import { getHeapStatistics } from "node:v8";
|
|
16
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
-
import { dirname, isAbsolute, join, normalize, relative } from "node:path";
|
|
18
20
|
import { fileURLToPath } from "node:url";
|
|
19
|
-
import {
|
|
21
|
+
import { performance } from "node:perf_hooks";
|
|
20
22
|
import { randomUUID } from "node:crypto";
|
|
21
23
|
//#region src/shared/estimate.ts
|
|
22
24
|
const CHARS_PER_TOKEN$1 = 4;
|
|
23
25
|
const ROLE_OVERHEAD$1 = 4;
|
|
26
|
+
const BLOCK_OVERHEAD$1 = 4;
|
|
24
27
|
function estimateSystemTokens(text) {
|
|
25
28
|
if (typeof text !== "string" || text.length === 0) return 0;
|
|
26
29
|
return Math.ceil(text.length / CHARS_PER_TOKEN$1) + ROLE_OVERHEAD$1;
|
|
27
30
|
}
|
|
31
|
+
function estimateTextTokens(text) {
|
|
32
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN$1) + BLOCK_OVERHEAD$1;
|
|
33
|
+
}
|
|
28
34
|
function estimateSystemContent(blocks) {
|
|
29
35
|
if (!Array.isArray(blocks) || blocks.length === 0) return 0;
|
|
30
36
|
let characters = 0;
|
|
@@ -161,7 +167,7 @@ function estimateBlocks(blocks) {
|
|
|
161
167
|
switch (block.type) {
|
|
162
168
|
case "text":
|
|
163
169
|
case "reasoning":
|
|
164
|
-
tokens +=
|
|
170
|
+
tokens += estimateTextTokens(block.text || "");
|
|
165
171
|
break;
|
|
166
172
|
case "tool-call":
|
|
167
173
|
tokens += Math.ceil((block.name || "").length / CHARS_PER_TOKEN) + Math.ceil((block.arguments || "").length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD;
|
|
@@ -763,6 +769,7 @@ function headFieldsOf(state) {
|
|
|
763
769
|
...state.model !== void 0 ? { model: state.model } : {},
|
|
764
770
|
...state.provider !== void 0 ? { provider: state.provider } : {},
|
|
765
771
|
...state.contextWindow !== void 0 ? { contextWindow: state.contextWindow } : {},
|
|
772
|
+
...state.maxOutputTokens !== void 0 ? { maxOutputTokens: state.maxOutputTokens } : {},
|
|
766
773
|
current: {
|
|
767
774
|
system: state.systemTokens,
|
|
768
775
|
tools: state.toolsTokens,
|
|
@@ -1096,6 +1103,9 @@ function applyTimeline(state, event, bounds) {
|
|
|
1096
1103
|
s.toolsTokens = estimateToolsTotal(tools);
|
|
1097
1104
|
if (header.config && typeof header.config.model === "string") s.model = header.config.model;
|
|
1098
1105
|
if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
|
|
1106
|
+
const maxOutputTokens = header.config?.maxTokens;
|
|
1107
|
+
if (typeof maxOutputTokens === "number" && Number.isFinite(maxOutputTokens) && maxOutputTokens > 0) s.maxOutputTokens = maxOutputTokens;
|
|
1108
|
+
else delete s.maxOutputTokens;
|
|
1099
1109
|
if ((data?.reason === "change" || data?.reason === "resume") && s.model && s.lastModel && s.model !== s.lastModel) {
|
|
1100
1110
|
s.events.push({
|
|
1101
1111
|
seq: event.seq,
|
|
@@ -1748,6 +1758,7 @@ const contextTimelineSchema = z.object({
|
|
|
1748
1758
|
model: z.string().optional(),
|
|
1749
1759
|
provider: z.string().optional(),
|
|
1750
1760
|
contextWindow: z.number().optional(),
|
|
1761
|
+
maxOutputTokens: z.number().positive().optional(),
|
|
1751
1762
|
current: currentSchema,
|
|
1752
1763
|
images: z.number().int().nonnegative().optional(),
|
|
1753
1764
|
toolCalls: z.number().int().nonnegative().optional(),
|
|
@@ -1787,6 +1798,7 @@ const timelineStateSchema = z.object({
|
|
|
1787
1798
|
provider: z.string().optional(),
|
|
1788
1799
|
lastModel: z.string().optional(),
|
|
1789
1800
|
contextWindow: z.number().optional(),
|
|
1801
|
+
maxOutputTokens: z.number().positive().optional(),
|
|
1790
1802
|
requests: z.array(requestRecordSchema),
|
|
1791
1803
|
turnRuns: z.number().int().nonnegative().optional(),
|
|
1792
1804
|
events: z.array(contextEventSchema),
|
|
@@ -1833,7 +1845,7 @@ function createContextTimelineDefinition(config, slim) {
|
|
|
1833
1845
|
},
|
|
1834
1846
|
init: () => createTimelineState(),
|
|
1835
1847
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
1836
|
-
stateVersion:
|
|
1848
|
+
stateVersion: 28
|
|
1837
1849
|
};
|
|
1838
1850
|
}
|
|
1839
1851
|
//#endregion
|
|
@@ -2027,6 +2039,277 @@ function createContextActivityDefinition() {
|
|
|
2027
2039
|
stateVersion: 4
|
|
2028
2040
|
};
|
|
2029
2041
|
}
|
|
2042
|
+
//#endregion
|
|
2043
|
+
//#region src/shared/attachment.ts
|
|
2044
|
+
const EXTENSION_IMAGE = new Set([
|
|
2045
|
+
"png",
|
|
2046
|
+
"jpg",
|
|
2047
|
+
"jpeg",
|
|
2048
|
+
"gif",
|
|
2049
|
+
"webp",
|
|
2050
|
+
"bmp",
|
|
2051
|
+
"ico",
|
|
2052
|
+
"svg"
|
|
2053
|
+
]);
|
|
2054
|
+
function leafNameOf(name) {
|
|
2055
|
+
return name.slice(Math.max(name.lastIndexOf("/"), name.lastIndexOf("\\")) + 1);
|
|
2056
|
+
}
|
|
2057
|
+
function attachmentExtension(name) {
|
|
2058
|
+
const base = leafNameOf(name);
|
|
2059
|
+
const dot = base.lastIndexOf(".");
|
|
2060
|
+
return dot < 0 ? "" : base.slice(dot + 1).toLowerCase();
|
|
2061
|
+
}
|
|
2062
|
+
function isImageName(name) {
|
|
2063
|
+
return EXTENSION_IMAGE.has(attachmentExtension(name));
|
|
2064
|
+
}
|
|
2065
|
+
function numOf(value) {
|
|
2066
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2067
|
+
}
|
|
2068
|
+
function dimensionsOf(value) {
|
|
2069
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
2070
|
+
const dims = value;
|
|
2071
|
+
const width = numOf(dims.width);
|
|
2072
|
+
const height = numOf(dims.height);
|
|
2073
|
+
return width !== void 0 && height !== void 0 ? {
|
|
2074
|
+
width,
|
|
2075
|
+
height
|
|
2076
|
+
} : void 0;
|
|
2077
|
+
}
|
|
2078
|
+
function attachmentOf(block) {
|
|
2079
|
+
if (block === null || typeof block !== "object") return null;
|
|
2080
|
+
const b = block;
|
|
2081
|
+
const spelling = typeof b.type === "string" ? b.type : b.kind;
|
|
2082
|
+
if (spelling !== "image" && spelling !== "file") return null;
|
|
2083
|
+
if (b.attachment === null || typeof b.attachment !== "object") return null;
|
|
2084
|
+
const a = b.attachment;
|
|
2085
|
+
if (typeof a.attachmentId !== "string" || a.attachmentId === "") return null;
|
|
2086
|
+
const name = typeof a.name === "string" && a.name !== "" ? a.name : void 0;
|
|
2087
|
+
const bytes = numOf(a.bytes);
|
|
2088
|
+
if (spelling === "file") return {
|
|
2089
|
+
kind: "file",
|
|
2090
|
+
attachmentId: a.attachmentId,
|
|
2091
|
+
...name !== void 0 ? { name } : {},
|
|
2092
|
+
...bytes !== void 0 ? { bytes } : {}
|
|
2093
|
+
};
|
|
2094
|
+
const origDims = dimensionsOf(a.originalDimensions);
|
|
2095
|
+
const width = numOf(a.width);
|
|
2096
|
+
const height = numOf(a.height);
|
|
2097
|
+
const mediaType = typeof a.mediaType === "string" && a.mediaType !== "" ? a.mediaType : void 0;
|
|
2098
|
+
return {
|
|
2099
|
+
kind: "image",
|
|
2100
|
+
attachmentId: a.attachmentId,
|
|
2101
|
+
...name !== void 0 ? { name } : {},
|
|
2102
|
+
...mediaType !== void 0 ? { mediaType } : {},
|
|
2103
|
+
...bytes !== void 0 ? { bytes } : {},
|
|
2104
|
+
...width !== void 0 ? { width } : {},
|
|
2105
|
+
...height !== void 0 ? { height } : {},
|
|
2106
|
+
...origDims !== void 0 ? { originalDimensions: origDims } : {}
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
//#endregion
|
|
2110
|
+
//#region src/host/coldRead.ts
|
|
2111
|
+
const HIGH_WATER = 2 / 3;
|
|
2112
|
+
function v8Reading() {
|
|
2113
|
+
const stats = getHeapStatistics();
|
|
2114
|
+
return {
|
|
2115
|
+
used: stats.used_heap_size,
|
|
2116
|
+
limit: stats.heap_size_limit
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
const noop = () => {};
|
|
2120
|
+
function makeColdReadGate(reading = v8Reading) {
|
|
2121
|
+
const clear = () => {
|
|
2122
|
+
try {
|
|
2123
|
+
const { used, limit } = reading();
|
|
2124
|
+
if (!Number.isFinite(used) || !Number.isFinite(limit) || limit <= 0) return true;
|
|
2125
|
+
return used < limit * HIGH_WATER;
|
|
2126
|
+
} catch {
|
|
2127
|
+
return true;
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
let tail = Promise.resolve();
|
|
2131
|
+
return { admit(read) {
|
|
2132
|
+
const run = tail.then(() => clear() ? read() : void 0);
|
|
2133
|
+
tail = run.then(noop, noop);
|
|
2134
|
+
return run;
|
|
2135
|
+
} };
|
|
2136
|
+
}
|
|
2137
|
+
//#endregion
|
|
2138
|
+
//#region src/host/connection.ts
|
|
2139
|
+
function fetchRouteRegistrar(connection) {
|
|
2140
|
+
return typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
2141
|
+
}
|
|
2142
|
+
function registerPostRoute(c, connection, path, fetch, label) {
|
|
2143
|
+
const register = fetchRouteRegistrar(connection);
|
|
2144
|
+
if (register === void 0) return false;
|
|
2145
|
+
try {
|
|
2146
|
+
c.effect(() => register({
|
|
2147
|
+
path,
|
|
2148
|
+
methods: ["POST"],
|
|
2149
|
+
requestBody: "buffered",
|
|
2150
|
+
fetch
|
|
2151
|
+
}), label);
|
|
2152
|
+
} catch {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2155
|
+
return true;
|
|
2156
|
+
}
|
|
2157
|
+
//#endregion
|
|
2158
|
+
//#region src/host/attachment.ts
|
|
2159
|
+
const ATTACHMENT_ROUTE = "/api/dsh-context/attachment";
|
|
2160
|
+
const IMAGE_MEDIA_EXTENSION = {
|
|
2161
|
+
"image/png": "png",
|
|
2162
|
+
"image/jpeg": "jpg",
|
|
2163
|
+
"image/webp": "webp",
|
|
2164
|
+
"image/gif": "gif"
|
|
2165
|
+
};
|
|
2166
|
+
function reply$2(value) {
|
|
2167
|
+
return Response.json(value, { headers: { "cache-control": "no-store" } });
|
|
2168
|
+
}
|
|
2169
|
+
function failure$2(code, message) {
|
|
2170
|
+
return reply$2({
|
|
2171
|
+
ok: false,
|
|
2172
|
+
error: {
|
|
2173
|
+
code,
|
|
2174
|
+
message
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
function asRecord$2(value) {
|
|
2179
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2180
|
+
}
|
|
2181
|
+
const WALK_MAX_DEPTH = 8;
|
|
2182
|
+
function attachmentIn(value, id, depth) {
|
|
2183
|
+
if (depth > WALK_MAX_DEPTH) return null;
|
|
2184
|
+
if (Array.isArray(value)) {
|
|
2185
|
+
for (const item of value) {
|
|
2186
|
+
const hit = attachmentIn(item, id, depth + 1);
|
|
2187
|
+
if (hit !== null) return hit;
|
|
2188
|
+
}
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
if (value === null || typeof value !== "object") return null;
|
|
2192
|
+
const ref = attachmentOf(value);
|
|
2193
|
+
if (ref !== null && ref.attachmentId === id) return ref;
|
|
2194
|
+
for (const child of Object.values(value)) {
|
|
2195
|
+
const hit = attachmentIn(child, id, depth + 1);
|
|
2196
|
+
if (hit !== null) return hit;
|
|
2197
|
+
}
|
|
2198
|
+
return null;
|
|
2199
|
+
}
|
|
2200
|
+
function referencedAttachmentOf(events, attachmentId) {
|
|
2201
|
+
for (const entry of events) try {
|
|
2202
|
+
const hit = attachmentIn(entry, attachmentId, 0);
|
|
2203
|
+
if (hit !== null) return hit;
|
|
2204
|
+
} catch {}
|
|
2205
|
+
return null;
|
|
2206
|
+
}
|
|
2207
|
+
function imagePreviewNameOf(ref) {
|
|
2208
|
+
const leaf = leafNameOf(ref.name ?? "");
|
|
2209
|
+
if (leaf !== "" && isImageName(leaf)) return leaf;
|
|
2210
|
+
const extension = ref.mediaType !== void 0 ? IMAGE_MEDIA_EXTENSION[ref.mediaType] : void 0;
|
|
2211
|
+
return extension === void 0 ? null : `attachment.${extension}`;
|
|
2212
|
+
}
|
|
2213
|
+
function previewAliasDirOf(objectPath) {
|
|
2214
|
+
const root = dirname(dirname(dirname(objectPath)));
|
|
2215
|
+
if (basename(root) !== "v1" || basename(dirname(root)) !== "attachments") return null;
|
|
2216
|
+
return join(dirname(dirname(root)), "cache", "dsh-context", "previews");
|
|
2217
|
+
}
|
|
2218
|
+
function filePathOf(store, ref) {
|
|
2219
|
+
if (typeof store?.fileHostPath !== "function") return null;
|
|
2220
|
+
try {
|
|
2221
|
+
const path = store.fileHostPath({
|
|
2222
|
+
attachmentId: ref.attachmentId,
|
|
2223
|
+
name: ref.name ?? "",
|
|
2224
|
+
bytes: ref.bytes ?? 0
|
|
2225
|
+
});
|
|
2226
|
+
return typeof path === "string" && path !== "" ? path : null;
|
|
2227
|
+
} catch {
|
|
2228
|
+
return null;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
async function imageAliasOf(store, ref) {
|
|
2232
|
+
if (typeof store?.imageHostPath !== "function") return null;
|
|
2233
|
+
let objectPath;
|
|
2234
|
+
try {
|
|
2235
|
+
objectPath = store.imageHostPath({
|
|
2236
|
+
attachmentId: ref.attachmentId,
|
|
2237
|
+
...ref.mediaType !== void 0 ? { mediaType: ref.mediaType } : {},
|
|
2238
|
+
...ref.bytes !== void 0 ? { bytes: ref.bytes } : {},
|
|
2239
|
+
...ref.width !== void 0 ? { width: ref.width } : {},
|
|
2240
|
+
...ref.height !== void 0 ? { height: ref.height } : {},
|
|
2241
|
+
...ref.name !== void 0 ? { name: ref.name } : {}
|
|
2242
|
+
});
|
|
2243
|
+
} catch {
|
|
2244
|
+
return null;
|
|
2245
|
+
}
|
|
2246
|
+
if (typeof objectPath !== "string" || objectPath === "") return null;
|
|
2247
|
+
const name = imagePreviewNameOf(ref);
|
|
2248
|
+
if (name === null) return null;
|
|
2249
|
+
const directory = previewAliasDirOf(objectPath);
|
|
2250
|
+
if (directory === null) return null;
|
|
2251
|
+
const alias = join(directory, basename(dirname(objectPath)), basename(objectPath), name);
|
|
2252
|
+
try {
|
|
2253
|
+
await mkdir(dirname(alias), { recursive: true });
|
|
2254
|
+
try {
|
|
2255
|
+
await link(objectPath, alias);
|
|
2256
|
+
} catch (err) {
|
|
2257
|
+
if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) await copyFile(objectPath, alias);
|
|
2258
|
+
}
|
|
2259
|
+
return alias;
|
|
2260
|
+
} catch {
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
async function previewPathOf(store, ref) {
|
|
2265
|
+
return ref.kind === "file" ? filePathOf(store, ref) : await imageAliasOf(store, ref);
|
|
2266
|
+
}
|
|
2267
|
+
function watchAttachmentChannel(ctx, coldReads = makeColdReadGate()) {
|
|
2268
|
+
ctx.inject(["connection"], (c) => {
|
|
2269
|
+
const handler = async (request) => {
|
|
2270
|
+
let sessionId;
|
|
2271
|
+
let attachmentId;
|
|
2272
|
+
try {
|
|
2273
|
+
const body = asRecord$2(await request.json());
|
|
2274
|
+
sessionId = body?.sessionId;
|
|
2275
|
+
attachmentId = body?.attachmentId;
|
|
2276
|
+
} catch {
|
|
2277
|
+
return failure$2("dsh-context/bad-request", "body is not JSON");
|
|
2278
|
+
}
|
|
2279
|
+
if (typeof sessionId !== "string" || sessionId === "") return failure$2("dsh-context/bad-request", "missing sessionId");
|
|
2280
|
+
if (typeof attachmentId !== "string" || attachmentId === "") return failure$2("dsh-context/bad-request", "missing attachmentId");
|
|
2281
|
+
try {
|
|
2282
|
+
const query = ctx.get("sessionQuery");
|
|
2283
|
+
const observe = typeof query?.observeSession === "function" ? query.observeSession.bind(query) : void 0;
|
|
2284
|
+
if (observe === void 0) return reply$2({
|
|
2285
|
+
ok: true,
|
|
2286
|
+
value: null
|
|
2287
|
+
});
|
|
2288
|
+
const store = ctx.get("attachments");
|
|
2289
|
+
return reply$2({
|
|
2290
|
+
ok: true,
|
|
2291
|
+
value: await coldReads.admit(async () => {
|
|
2292
|
+
const observation = await observe(sessionId, { projectionMode: "none" });
|
|
2293
|
+
try {
|
|
2294
|
+
const events = observation?.events;
|
|
2295
|
+
if (!Array.isArray(events)) return null;
|
|
2296
|
+
const ref = referencedAttachmentOf(events, attachmentId);
|
|
2297
|
+
if (ref === null) return null;
|
|
2298
|
+
const path = await previewPathOf(store, ref);
|
|
2299
|
+
return path === null ? null : { path };
|
|
2300
|
+
} finally {
|
|
2301
|
+
const dispose = observation?.[Symbol.dispose];
|
|
2302
|
+
if (typeof dispose === "function") dispose.call(observation);
|
|
2303
|
+
}
|
|
2304
|
+
}) ?? null
|
|
2305
|
+
});
|
|
2306
|
+
} catch (err) {
|
|
2307
|
+
return failure$2("gateway/internal", err instanceof Error ? err.message : String(err));
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
registerPostRoute(c, c.get("connection"), ATTACHMENT_ROUTE, handler, "dsh-context: attachment route");
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2030
2313
|
function mcpServerOf(name) {
|
|
2031
2314
|
if (!name.startsWith("mcp__")) return void 0;
|
|
2032
2315
|
const cut = name.lastIndexOf("__");
|
|
@@ -2192,26 +2475,6 @@ function asRecord$1(value) {
|
|
|
2192
2475
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2193
2476
|
}
|
|
2194
2477
|
//#endregion
|
|
2195
|
-
//#region src/host/connection.ts
|
|
2196
|
-
function fetchRouteRegistrar(connection) {
|
|
2197
|
-
return typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
2198
|
-
}
|
|
2199
|
-
function registerPostRoute(c, connection, path, fetch, label) {
|
|
2200
|
-
const register = fetchRouteRegistrar(connection);
|
|
2201
|
-
if (register === void 0) return false;
|
|
2202
|
-
try {
|
|
2203
|
-
c.effect(() => register({
|
|
2204
|
-
path,
|
|
2205
|
-
methods: ["POST"],
|
|
2206
|
-
requestBody: "buffered",
|
|
2207
|
-
fetch
|
|
2208
|
-
}), label);
|
|
2209
|
-
} catch {
|
|
2210
|
-
return false;
|
|
2211
|
-
}
|
|
2212
|
-
return true;
|
|
2213
|
-
}
|
|
2214
|
-
//#endregion
|
|
2215
2478
|
//#region src/host/balance.ts
|
|
2216
2479
|
const BALANCE_ROUTE = "/api/dsh-context/balance";
|
|
2217
2480
|
const DEEPSEEK_SETTINGS_NS = "llm-deepseek";
|
|
@@ -2329,37 +2592,17 @@ function watchBalanceChannel(ctx) {
|
|
|
2329
2592
|
});
|
|
2330
2593
|
}
|
|
2331
2594
|
//#endregion
|
|
2332
|
-
//#region src/host/coldRead.ts
|
|
2333
|
-
const HIGH_WATER = 2 / 3;
|
|
2334
|
-
function v8Reading() {
|
|
2335
|
-
const stats = getHeapStatistics();
|
|
2336
|
-
return {
|
|
2337
|
-
used: stats.used_heap_size,
|
|
2338
|
-
limit: stats.heap_size_limit
|
|
2339
|
-
};
|
|
2340
|
-
}
|
|
2341
|
-
const noop = () => {};
|
|
2342
|
-
function makeColdReadGate(reading = v8Reading) {
|
|
2343
|
-
const clear = () => {
|
|
2344
|
-
try {
|
|
2345
|
-
const { used, limit } = reading();
|
|
2346
|
-
if (!Number.isFinite(used) || !Number.isFinite(limit) || limit <= 0) return true;
|
|
2347
|
-
return used < limit * HIGH_WATER;
|
|
2348
|
-
} catch {
|
|
2349
|
-
return true;
|
|
2350
|
-
}
|
|
2351
|
-
};
|
|
2352
|
-
let tail = Promise.resolve();
|
|
2353
|
-
return { admit(read) {
|
|
2354
|
-
const run = tail.then(() => clear() ? read() : void 0);
|
|
2355
|
-
tail = run.then(noop, noop);
|
|
2356
|
-
return run;
|
|
2357
|
-
} };
|
|
2358
|
-
}
|
|
2359
|
-
//#endregion
|
|
2360
2595
|
//#region src/host/backfill.ts
|
|
2361
2596
|
const BACKFILL_ROUTE = "/api/dsh-context/backfill";
|
|
2362
|
-
const
|
|
2597
|
+
const MIN_REST_MS = 25;
|
|
2598
|
+
const SLICE_EVENTS = 2e3;
|
|
2599
|
+
const DEFAULT_PACING = {
|
|
2600
|
+
now: () => performance.now(),
|
|
2601
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
2602
|
+
minRestMs: MIN_REST_MS,
|
|
2603
|
+
workBudgetMs: 8e3,
|
|
2604
|
+
roundPauseMs: 2e3
|
|
2605
|
+
};
|
|
2363
2606
|
const UNSUPPORTED_FORMAT = "SessionFormatUnsupported";
|
|
2364
2607
|
function headerOf(record) {
|
|
2365
2608
|
const header = asRecord$1(asRecord$1(record)?.header);
|
|
@@ -2403,6 +2646,49 @@ function isLive(sessions, id) {
|
|
|
2403
2646
|
return true;
|
|
2404
2647
|
}
|
|
2405
2648
|
}
|
|
2649
|
+
async function coldSnapshotSliced(ctx, faces, sessions, log, signal, rest, now) {
|
|
2650
|
+
const { header, inheritedEventCount, events } = log;
|
|
2651
|
+
let rows = {};
|
|
2652
|
+
let workMs = 0;
|
|
2653
|
+
for (let end = SLICE_EVENTS;; end += SLICE_EVENTS) {
|
|
2654
|
+
const last = end >= events.length;
|
|
2655
|
+
const started = now();
|
|
2656
|
+
rows = asRecord$1(faces.restore(rows, last ? events : events.slice(0, end), 0, header, inheritedEventCount))?.checkpoint ?? {};
|
|
2657
|
+
const sliceCost = now() - started;
|
|
2658
|
+
workMs += sliceCost;
|
|
2659
|
+
if (last) break;
|
|
2660
|
+
await rest(sliceCost);
|
|
2661
|
+
if (signal.aborted) return {
|
|
2662
|
+
outcome: "aborted",
|
|
2663
|
+
workMs
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
let prepared;
|
|
2667
|
+
try {
|
|
2668
|
+
prepared = faces.prepare(header.id, {
|
|
2669
|
+
seed: events,
|
|
2670
|
+
meta: structuredClone(header),
|
|
2671
|
+
inheritedEventCount,
|
|
2672
|
+
eventState: "shared-frozen"
|
|
2673
|
+
});
|
|
2674
|
+
} catch (error) {
|
|
2675
|
+
if (isLive(sessions, header.id)) return {
|
|
2676
|
+
outcome: "live",
|
|
2677
|
+
workMs
|
|
2678
|
+
};
|
|
2679
|
+
throw error;
|
|
2680
|
+
}
|
|
2681
|
+
faces.hydrate(prepared, rows, events, 0);
|
|
2682
|
+
try {
|
|
2683
|
+
await faces.write(prepared);
|
|
2684
|
+
} catch (error) {
|
|
2685
|
+
ctx.logger.warn(`dsh-context: projection backfill write-back for "${header.id}" failed (cache stays stale): ${messageOf$1(error)}`);
|
|
2686
|
+
}
|
|
2687
|
+
return {
|
|
2688
|
+
outcome: "done",
|
|
2689
|
+
workMs
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2406
2692
|
function isUnsupportedFormat(error) {
|
|
2407
2693
|
try {
|
|
2408
2694
|
return String(asRecord$1(error)?.name).startsWith(UNSUPPORTED_FORMAT);
|
|
@@ -2417,7 +2703,7 @@ function messageOf$1(error) {
|
|
|
2417
2703
|
return "unprintable error";
|
|
2418
2704
|
}
|
|
2419
2705
|
}
|
|
2420
|
-
function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
|
|
2706
|
+
function watchActivityBackfill(ctx, coldReads = makeColdReadGate(), pacing = DEFAULT_PACING) {
|
|
2421
2707
|
let requested = false;
|
|
2422
2708
|
let started = false;
|
|
2423
2709
|
let launch = null;
|
|
@@ -2448,6 +2734,13 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
|
|
|
2448
2734
|
if (cache === null || typeof cache.cachedSnapshot !== "function" || typeof cache.coldSnapshot !== "function") return;
|
|
2449
2735
|
if (persistence === null || typeof persistence.open !== "function") return;
|
|
2450
2736
|
const sessions = asRecord$1(injected.sessions);
|
|
2737
|
+
const registry = asRecord$1(ctx.sessionProjections);
|
|
2738
|
+
const sliceFaces = registry !== null && typeof registry.restore === "function" && typeof registry.hydrate === "function" && sessions !== null && typeof sessions.prepare === "function" && typeof cache.write === "function" ? {
|
|
2739
|
+
restore: registry.restore.bind(registry),
|
|
2740
|
+
hydrate: registry.hydrate.bind(registry),
|
|
2741
|
+
prepare: sessions.prepare.bind(sessions),
|
|
2742
|
+
write: cache.write.bind(cache)
|
|
2743
|
+
} : null;
|
|
2451
2744
|
const abort = new AbortController();
|
|
2452
2745
|
const run = async () => {
|
|
2453
2746
|
const listed = await query.listSessions(abort.signal);
|
|
@@ -2455,29 +2748,63 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
|
|
|
2455
2748
|
let folded = 0;
|
|
2456
2749
|
let refused = 0;
|
|
2457
2750
|
let paused = false;
|
|
2751
|
+
let roundWork = 0;
|
|
2752
|
+
const sleep = async (ms) => {
|
|
2753
|
+
if (abort.signal.aborted) return;
|
|
2754
|
+
let onAbort;
|
|
2755
|
+
try {
|
|
2756
|
+
await Promise.race([pacing.sleep(ms), new Promise((resolve) => {
|
|
2757
|
+
onAbort = () => {
|
|
2758
|
+
resolve();
|
|
2759
|
+
};
|
|
2760
|
+
abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
2761
|
+
})]);
|
|
2762
|
+
} finally {
|
|
2763
|
+
abort.signal.removeEventListener("abort", onAbort);
|
|
2764
|
+
}
|
|
2765
|
+
};
|
|
2766
|
+
const rest = async (costMs) => {
|
|
2767
|
+
roundWork += costMs;
|
|
2768
|
+
if (roundWork >= pacing.workBudgetMs) {
|
|
2769
|
+
roundWork = 0;
|
|
2770
|
+
ctx.logger.info(`dsh-context: projection backfill pausing after ${Math.round(pacing.workBudgetMs / 1e3)}s of fold work; resuming in ${Math.round(pacing.roundPauseMs / 1e3)}s`);
|
|
2771
|
+
await sleep(pacing.roundPauseMs);
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
await sleep(Math.max(pacing.minRestMs, costMs));
|
|
2775
|
+
};
|
|
2458
2776
|
for (const record of listed) {
|
|
2459
2777
|
if (abort.signal.aborted) return;
|
|
2460
2778
|
const header = headerOf(record);
|
|
2461
2779
|
if (header === null || typeof header.cwd !== "string") continue;
|
|
2462
2780
|
if (isLive(sessions, header.id)) continue;
|
|
2463
2781
|
if (servesRows(cache, header)) continue;
|
|
2782
|
+
const started = pacing.now();
|
|
2783
|
+
let slicedWorkMs = 0;
|
|
2464
2784
|
try {
|
|
2465
|
-
|
|
2785
|
+
const admitted = await coldReads.admit(async () => {
|
|
2466
2786
|
const log = await readColdLog(persistence, header.id, abort.signal);
|
|
2787
|
+
if (sliceFaces !== null && log.events.length > SLICE_EVENTS) {
|
|
2788
|
+
const sliced = await coldSnapshotSliced(ctx, sliceFaces, sessions, log, abort.signal, rest, pacing.now);
|
|
2789
|
+
slicedWorkMs = sliced.workMs;
|
|
2790
|
+
return sliced.outcome;
|
|
2791
|
+
}
|
|
2467
2792
|
cache.coldSnapshot(log.header, log.inheritedEventCount, log.events);
|
|
2468
|
-
return
|
|
2469
|
-
})
|
|
2793
|
+
return "done";
|
|
2794
|
+
});
|
|
2795
|
+
if (admitted === void 0) {
|
|
2470
2796
|
paused = true;
|
|
2471
2797
|
break;
|
|
2472
2798
|
}
|
|
2473
|
-
|
|
2799
|
+
if (admitted === "aborted") return;
|
|
2800
|
+
if (admitted === "done") folded++;
|
|
2474
2801
|
} catch (error) {
|
|
2475
2802
|
if (isUnsupportedFormat(error)) {
|
|
2476
2803
|
refused++;
|
|
2477
2804
|
ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf$1(error)})`);
|
|
2478
2805
|
} else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf$1(error)})`);
|
|
2479
2806
|
}
|
|
2480
|
-
await
|
|
2807
|
+
await rest(Math.max(0, pacing.now() - started - slicedWorkMs));
|
|
2481
2808
|
}
|
|
2482
2809
|
if (folded > 0) ctx.logger.info(`dsh-context: projection rows backfilled for ${folded} session(s)`);
|
|
2483
2810
|
if (paused) ctx.logger.info("dsh-context: projection backfill stopped early under heap pressure (remaining sessions refold on their next live activity)");
|
|
@@ -2497,6 +2824,12 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
|
|
|
2497
2824
|
for (const fiber of [routeFiber, runnerFiber]) fiber?.dispose?.();
|
|
2498
2825
|
};
|
|
2499
2826
|
}
|
|
2827
|
+
const FOLD_YIELD_EVENTS = 1e3;
|
|
2828
|
+
const PAYLOAD_CACHE_MAX = 4;
|
|
2829
|
+
function revisionOf(observation) {
|
|
2830
|
+
const revision = observation?.revision;
|
|
2831
|
+
return typeof revision === "string" && revision !== "" ? revision : void 0;
|
|
2832
|
+
}
|
|
2500
2833
|
function reply$1(value) {
|
|
2501
2834
|
return Response.json(value, { headers: { "cache-control": "no-store" } });
|
|
2502
2835
|
}
|
|
@@ -2511,6 +2844,7 @@ function failure$1(code, message) {
|
|
|
2511
2844
|
}
|
|
2512
2845
|
function watchDetailChannel(ctx, bounds, coldReads = makeColdReadGate()) {
|
|
2513
2846
|
const gate = { live: false };
|
|
2847
|
+
const payloads = new Map();
|
|
2514
2848
|
ctx.inject(["connection", "sessions"], (c) => {
|
|
2515
2849
|
const sessions = c.get("sessions");
|
|
2516
2850
|
const getSession = typeof sessions?.get === "function" ? sessions.get.bind(sessions) : void 0;
|
|
@@ -2548,16 +2882,43 @@ function watchDetailChannel(ctx, bounds, coldReads = makeColdReadGate()) {
|
|
|
2548
2882
|
ok: true,
|
|
2549
2883
|
value: await coldReads.admit(async () => {
|
|
2550
2884
|
const observation = await observe(sessionId, { projectionMode: "none" });
|
|
2551
|
-
const events = observation?.events;
|
|
2552
|
-
if (!Array.isArray(events)) return null;
|
|
2553
|
-
let state = createTimelineState();
|
|
2554
2885
|
try {
|
|
2555
|
-
|
|
2886
|
+
const persistence = ctx.get("sessionPersistence");
|
|
2887
|
+
const revision = revisionOf(observation);
|
|
2888
|
+
if (revision !== void 0) {
|
|
2889
|
+
const hit = payloads.get(sessionId);
|
|
2890
|
+
if (hit !== void 0 && hit.revision === revision && hit.persistence === persistence) {
|
|
2891
|
+
payloads.delete(sessionId);
|
|
2892
|
+
payloads.set(sessionId, hit);
|
|
2893
|
+
return hit.value;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
const events = observation?.events;
|
|
2897
|
+
if (!Array.isArray(events)) return null;
|
|
2898
|
+
let state = createTimelineState();
|
|
2899
|
+
let folded = 0;
|
|
2900
|
+
for (const ev of events) {
|
|
2901
|
+
state = applyTimeline(state, ev, bounds);
|
|
2902
|
+
if (++folded % FOLD_YIELD_EVENTS === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2903
|
+
}
|
|
2904
|
+
const detail = buildTimelineDetail(state, bounds);
|
|
2905
|
+
if (revision !== void 0) {
|
|
2906
|
+
payloads.delete(sessionId);
|
|
2907
|
+
payloads.set(sessionId, {
|
|
2908
|
+
persistence,
|
|
2909
|
+
revision,
|
|
2910
|
+
value: detail
|
|
2911
|
+
});
|
|
2912
|
+
for (const oldest of payloads.keys()) {
|
|
2913
|
+
if (payloads.size <= PAYLOAD_CACHE_MAX) break;
|
|
2914
|
+
payloads.delete(oldest);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return detail;
|
|
2556
2918
|
} finally {
|
|
2557
2919
|
const dispose = observation?.[Symbol.dispose];
|
|
2558
2920
|
if (typeof dispose === "function") dispose.call(observation);
|
|
2559
2921
|
}
|
|
2560
|
-
return buildTimelineDetail(state, bounds);
|
|
2561
2922
|
}) ?? null
|
|
2562
2923
|
});
|
|
2563
2924
|
} catch (err) {
|
|
@@ -3267,6 +3628,7 @@ function apply(ctx, config) {
|
|
|
3267
3628
|
watchBalanceChannel(ctx);
|
|
3268
3629
|
watchSkillCatalog(ctx);
|
|
3269
3630
|
watchFleetChannel(ctx, coldReads);
|
|
3631
|
+
watchAttachmentChannel(ctx, coldReads);
|
|
3270
3632
|
const harnessVersion = detectHarnessVersion(ctx);
|
|
3271
3633
|
if (harnessVersion !== void 0 && !meetsBaseline(harnessVersion)) {
|
|
3272
3634
|
ctx.sessionProjections.register(createFallbackTimelineDefinition(harnessVersion));
|