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/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.69.1
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-12T00:44:06+00:00
8
- * @commit f6ab4a29ff9d
9
- * @commit-date 2026-10-12T08:40:43+08:00
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 { getHeapStatistics } from "node:v8";
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 += Math.ceil((block.text || "").length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD;
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: 27
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 YIELD_MS = 100;
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
- if (await coldReads.admit(async () => {
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 true;
2469
- }) === void 0) {
2793
+ return "done";
2794
+ });
2795
+ if (admitted === void 0) {
2470
2796
  paused = true;
2471
2797
  break;
2472
2798
  }
2473
- folded++;
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 new Promise((resolve) => setTimeout(resolve, YIELD_MS));
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
- for (const ev of events) state = applyTimeline(state, ev, bounds);
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));