dsh-context 0.53.2 → 0.53.3

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/client.js CHANGED
@@ -6168,6 +6168,20 @@ window.__ModuleLoader__.load({
6168
6168
  Promise.resolve(sessions.refresh()).catch(() => {});
6169
6169
  } catch {}
6170
6170
  }
6171
+ const BACKFILL_ROUTE = "/api/dsh-context/backfill";
6172
+ /**
6173
+ * Summon the host's projection warm-up (host/backfill.ts): the dashboard is
6174
+ * the rows' only reader, so the host defers the corpus-wide cold reads until
6175
+ * this surface first opens (one pass per host process — later opens no-op
6176
+ * server-side, and the host answers at once). Fire-and-forget: an older host
6177
+ * without the route, or a transport hiccup, just leaves the panel on the
6178
+ * rows it already has.
6179
+ */
6180
+ function requestActivityBackfill() {
6181
+ try {
6182
+ fetch(BACKFILL_ROUTE, { method: "POST" }).catch(() => {});
6183
+ } catch {}
6184
+ }
6171
6185
  /**
6172
6186
  * The row's relative-activity label ("3m ago"), unit-stepped: under a
6173
6187
  * minute reads "just now", then minutes, hours, days. A future or invalid
@@ -6668,7 +6682,10 @@ window.__ModuleLoader__.load({
6668
6682
  const rows = (0, react.useMemo)(() => rowsOfSnapshot(snapshot, wsSnapshot), [snapshot, wsSnapshot]);
6669
6683
  const groups = (0, react.useMemo)(() => sessionGroupsOf(wsSnapshot), [wsSnapshot]);
6670
6684
  (0, react.useEffect)(() => {
6671
- if (open) refreshSessions(ctx);
6685
+ if (open) {
6686
+ requestActivityBackfill();
6687
+ refreshSessions(ctx);
6688
+ }
6672
6689
  }, [open]);
6673
6690
  (0, react.useEffect)(() => {
6674
6691
  setPage(0);
@@ -9475,7 +9492,7 @@ window.__ModuleLoader__.load({
9475
9492
  return function PluginInfo() {
9476
9493
  const [latest, setLatest] = (0, react.useState)(null);
9477
9494
  (0, react.useEffect)(() => {
9478
- if ("0.53.2".includes("-dev")) return;
9495
+ if ("0.53.3".includes("-dev")) return;
9479
9496
  let on = true;
9480
9497
  fetchLatestVersion().then((v) => {
9481
9498
  if (on && v) setLatest(v);
@@ -9484,8 +9501,8 @@ window.__ModuleLoader__.load({
9484
9501
  on = false;
9485
9502
  };
9486
9503
  }, []);
9487
- const update = latest !== null && isNewerVersion(latest, "0.53.2") ? latest : null;
9488
- const nameText = "dsh-context (v0.53.2)";
9504
+ const update = latest !== null && isNewerVersion(latest, "0.53.3") ? latest : null;
9505
+ const nameText = "dsh-context (v0.53.3)";
9489
9506
  const nameValue = [nameText];
9490
9507
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9491
9508
  className: "lc-pi-update",
package/lib/index.js CHANGED
@@ -870,7 +870,7 @@ function removeSurfaceSeqs(st, claimed, goneSeq) {
870
870
  * rather than through `deriveEventMessage`, whose 0.1.2-rc.1 generation knows
871
871
  * nothing of the V3 `system/message` variant. A malformed payload reads null.
872
872
  */
873
- function messageOf(data) {
873
+ function messageOf$1(data) {
874
874
  const message = data?.message;
875
875
  return message !== null && typeof message === "object" ? message : null;
876
876
  }
@@ -1230,7 +1230,7 @@ function applyTimeline(state, event, bounds) {
1230
1230
  pushSystem(s, {
1231
1231
  seq: event.seq,
1232
1232
  time: event.time,
1233
- tokens: estimateSystemContent(messageOf(data)?.content)
1233
+ tokens: estimateSystemContent(messageOf$1(data)?.content)
1234
1234
  });
1235
1235
  break;
1236
1236
  }
@@ -2058,8 +2058,17 @@ function createToolAttribution(ctx) {
2058
2058
  }
2059
2059
  //#endregion
2060
2060
  //#region src/host/backfill.ts
2061
- /** Inter-session pacing so a multi-hundred-session backfill never starve the host. */
2062
- const YIELD_MS = 10;
2061
+ /** The plugin's warm-up trigger route, under the authenticated `/api` fence. */
2062
+ const BACKFILL_ROUTE = "/api/dsh-context/backfill";
2063
+ /**
2064
+ * Inter-session pacing so a multi-hundred-session backfill never starve the
2065
+ * host: each cold read is a full durable-log decode, and a boot-time host
2066
+ * under sustained read pressure stalls its own client handshake (the
2067
+ * workspace sidebar rides it), so the pass breathes between sessions.
2068
+ */
2069
+ const YIELD_MS = 100;
2070
+ /** The `name` dsh raises for a durable log its migration surface refuses. */
2071
+ const UNSUPPORTED_MIGRATION = "SessionFormatUnsupportedMigrationError";
2063
2072
  function asRecord(value) {
2064
2073
  return value !== null && typeof value === "object" ? value : null;
2065
2074
  }
@@ -2114,11 +2123,63 @@ function isLive(sessions, id) {
2114
2123
  }
2115
2124
  }
2116
2125
  /**
2117
- * Arm the warm-up on whichever deployment composes the four cold-path
2118
- * services; returns the deferred inject's disposer (abort on unload).
2126
+ * Whether the cold read failed because the running harness refuses to
2127
+ * migrate this log — a permanent, source-side refusal (the artifact is left
2128
+ * unchanged, so retrying cannot succeed). Matched by the error's documented
2129
+ * `name`, so no dsh symbol needs importing and every supported baseline
2130
+ * classifies alike: an unknown error shape just falls to the warn path.
2131
+ */
2132
+ function isUnsupportedMigration(error) {
2133
+ try {
2134
+ return asRecord(error)?.name === UNSUPPORTED_MIGRATION;
2135
+ } catch {
2136
+ return false;
2137
+ }
2138
+ }
2139
+ /** The error's printable form — a hostile error may throw on its own toString. */
2140
+ function messageOf(error) {
2141
+ try {
2142
+ return String(error);
2143
+ } catch {
2144
+ return "unprintable error";
2145
+ }
2146
+ }
2147
+ /**
2148
+ * Arm the warm-up behind its trigger route. The route flips `requested`
2149
+ * when the dashboard first opens; the cold-path faces compose `launch`.
2150
+ * Either half may land first; the first request after both launches the one
2151
+ * pass, and every later request is a no-op. Returns the deferred injects'
2152
+ * disposer (abort on unload).
2119
2153
  */
2120
2154
  function watchActivityBackfill(ctx) {
2121
- const handle = ctx.inject([
2155
+ let requested = false;
2156
+ let started = false;
2157
+ let launch = null;
2158
+ const tryLaunch = () => {
2159
+ if (!requested || started || launch === null) return;
2160
+ started = true;
2161
+ launch();
2162
+ };
2163
+ const routeFiber = ctx.inject(["connection"], (c) => {
2164
+ const connection = c.get("connection");
2165
+ const register = typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
2166
+ if (register === void 0) return;
2167
+ try {
2168
+ c.effect(() => register({
2169
+ path: BACKFILL_ROUTE,
2170
+ methods: ["POST"],
2171
+ requestBody: "buffered",
2172
+ fetch: () => {
2173
+ requested = true;
2174
+ tryLaunch();
2175
+ return Response.json({ ok: true }, { headers: { "cache-control": "no-store" } });
2176
+ }
2177
+ }), "dsh-context: backfill route");
2178
+ } catch {
2179
+ return;
2180
+ }
2181
+ });
2182
+ const runnerFiber = ctx.inject([
2122
2183
  "sessionQuery",
2123
2184
  "sessionProjectionCache",
2124
2185
  "sessionPersistence",
@@ -2137,6 +2198,7 @@ function watchActivityBackfill(ctx) {
2137
2198
  const listed = await query.listSessions(abort.signal);
2138
2199
  if (!Array.isArray(listed)) return;
2139
2200
  let folded = 0;
2201
+ let refused = 0;
2140
2202
  for (const record of listed) {
2141
2203
  if (abort.signal.aborted) return;
2142
2204
  const header = headerOf(record);
@@ -2148,21 +2210,28 @@ function watchActivityBackfill(ctx) {
2148
2210
  cache.coldSnapshot(log.header, log.inheritedEventCount, log.events);
2149
2211
  folded++;
2150
2212
  } catch (error) {
2151
- ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${String(error)})`);
2213
+ if (isUnsupportedMigration(error)) {
2214
+ refused++;
2215
+ ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
2216
+ } else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
2152
2217
  }
2153
2218
  await new Promise((resolve) => setTimeout(resolve, YIELD_MS));
2154
2219
  }
2155
2220
  if (folded > 0) ctx.logger.info(`dsh-context: projection rows backfilled for ${folded} session(s)`);
2221
+ if (refused > 0) ctx.logger.info(`dsh-context: projection backfill skipped ${refused} session(s) (legacy log format is not migratable; sources left unchanged)`);
2156
2222
  };
2157
- run().catch((error) => {
2158
- if (!abort.signal.aborted) ctx.logger.warn(`dsh-context: projection backfill stopped early (${String(error)})`);
2159
- });
2223
+ launch = () => {
2224
+ run().catch((error) => {
2225
+ if (!abort.signal.aborted) ctx.logger.warn(`dsh-context: projection backfill stopped early (${messageOf(error)})`);
2226
+ });
2227
+ };
2228
+ tryLaunch();
2160
2229
  return () => {
2161
2230
  abort.abort();
2162
2231
  };
2163
2232
  });
2164
2233
  return () => {
2165
- handle?.dispose?.();
2234
+ for (const fiber of [routeFiber, runnerFiber]) fiber?.dispose?.();
2166
2235
  };
2167
2236
  }
2168
2237
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.53.2",
3
+ "version": "0.53.3",
4
4
  "description": "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.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {