dsh-context 0.41.0 → 0.41.2

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/lib/client.js +70 -10
  3. package/package.json +15 -1
package/README.md CHANGED
@@ -140,7 +140,7 @@ In **Settings → Plugins → Plugin configuration**, the **Context** card holds
140
140
  ## Good to know
141
141
 
142
142
  - **Estimates vs actuals** — category figures use dsh's own fixed-density heuristic (the same one as its built-in token meter); the pinned trend details and Token/Timing rings show provider-reported actuals next to them.
143
- - **Compatibility** — works on `@deepseek-ai/dsh` **0.1.1-rc2+** and **0.1.2-alpha2+**.
143
+ - **Compatibility** — works on `@deepseek-ai/dsh` **0.1.1-rc2+** and **0.1.2-alpha2+**. The per-release matrix and how it is verified: [docs/compatibility.md](docs/compatibility.md).
144
144
  - **I18n** — UI in English and 简体中文.
145
145
 
146
146
  ## Like it?
package/lib/client.js CHANGED
@@ -1272,6 +1272,63 @@ window.__ModuleLoader__.load({
1272
1272
  return;
1273
1273
  }
1274
1274
  }
1275
+ /**
1276
+ * Walk a (possibly traced/proxied) service value down a plain-property path,
1277
+ * degrading at the FIRST throw. cordis's undeclared-service proxies are one
1278
+ * hostile object class — any accessor backed by host state can throw too —
1279
+ * and the no-white-screen contract needs the whole chain guarded, not just
1280
+ * the `ctx.get` call.
1281
+ */
1282
+ function readKeysOf(value, ...keys) {
1283
+ let current = value;
1284
+ for (const key of keys) {
1285
+ if (current === null || typeof current !== "object") return void 0;
1286
+ try {
1287
+ current = current[key];
1288
+ } catch {
1289
+ return;
1290
+ }
1291
+ }
1292
+ return current;
1293
+ }
1294
+ /** The `page` verb of a history face, re-proved and bound to its owner. */
1295
+ function readPageOf(face) {
1296
+ const fn = readKeysOf(face, "page");
1297
+ return typeof fn === "function" ? fn.bind(face) : void 0;
1298
+ }
1299
+ /** The `history` verb of the legacy api client face, re-proved and bound. */
1300
+ function readHistoryOf(face) {
1301
+ const sessions = readKeysOf(face, "api", "sessions");
1302
+ const fn = readKeysOf(sessions, "history");
1303
+ return typeof fn === "function" ? fn.bind(sessions) : void 0;
1304
+ }
1305
+ /**
1306
+ * The 0.1.2+ gateway history page verb, resolved through the DECLARED inject
1307
+ * (see {@link watchHistoryFaces}) and bound up front: a method extracted
1308
+ * unbound loses `this`, and the traced `remote` proxy that hands it out
1309
+ * requires the inject to resolve at all.
1310
+ */
1311
+ let declaredPage;
1312
+ /**
1313
+ * Register the plugin's 0.1.2+ history faces with the harness through the
1314
+ * DECLARED inject — both `remote` AND `remote.session` (the ui-chat idiom)
1315
+ * must be in one fiber's requirement list, because the traced `remote`
1316
+ * proxy resolves `.session` through the context and each name needs the
1317
+ * other's declaration. Pre-0.1.2 hosts never provide either name, so the
1318
+ * callback simply never fires and the legacy `connection` face carries the
1319
+ * reads. The callback re-runs on every unload/remount, so it owns the
1320
+ * slot's lifetime. The face itself is re-proven (a never-fired or hostile
1321
+ * invocation leaves the slot unset — nothing here can throw).
1322
+ */
1323
+ function watchHistoryFaces(ctx) {
1324
+ ctx.inject(["remote", "remote.session"], (c) => {
1325
+ const session = c.remote?.session;
1326
+ declaredPage = session !== void 0 ? readPageOf(session) : void 0;
1327
+ return () => {
1328
+ declaredPage = void 0;
1329
+ };
1330
+ });
1331
+ }
1275
1332
  /** The rows array of a history/page response, under every served envelope. */
1276
1333
  function rowsOf(response) {
1277
1334
  let payload = response;
@@ -1295,15 +1352,17 @@ window.__ModuleLoader__.load({
1295
1352
  * pre-0.1.2 api client verb (`connection.api.sessions.history`) beneath it.
1296
1353
  * Both cut message-aligned pages, so the returned read covers `seq` whenever
1297
1354
  * the durable log still holds it: the newer face pins the inclusive cut to
1298
- * the seq itself, the legacy reader uses the exclusive bound one past it.
1299
- * Undefined when no face exists (older hosts) callers keep their static
1300
- * degradation.
1355
+ * the seq itself, the legacy reader uses the exclusive bound one past it
1356
+ * and because a non-declared read of the traced remote proxy throws
1357
+ * ("cannot get property … without inject"), the page face prefers the
1358
+ * injection-resolved slot and only then tries the reflect read. Every
1359
+ * service property access degrades at its own guard instead of taking the
1360
+ * view down. Undefined when no face exists (older hosts) — callers keep
1361
+ * their static degradation.
1301
1362
  */
1302
1363
  function pageReadersOf(ctx, sessionId) {
1303
- const pageFace = serviceOf(ctx, "remote.session");
1304
- const page = pageFace !== void 0 && typeof pageFace.page === "function" ? pageFace.page.bind(pageFace) : void 0;
1305
- const historyFace = serviceOf(ctx, "connection")?.api?.sessions;
1306
- const history = historyFace !== void 0 && typeof historyFace.history === "function" ? historyFace.history.bind(historyFace) : void 0;
1364
+ const page = declaredPage ?? readPageOf(serviceOf(ctx, "remote.session"));
1365
+ const history = readHistoryOf(serviceOf(ctx, "connection"));
1307
1366
  if (page === void 0 && history === void 0) return void 0;
1308
1367
  return (seq) => {
1309
1368
  if (page !== void 0) return page({
@@ -5063,7 +5122,7 @@ window.__ModuleLoader__.load({
5063
5122
  return function PluginInfo() {
5064
5123
  const [latest, setLatest] = React.useState(null);
5065
5124
  React.useEffect(() => {
5066
- if ("0.41.0".includes("-dev")) return;
5125
+ if ("0.41.2".includes("-dev")) return;
5067
5126
  let on = true;
5068
5127
  fetchLatestVersion().then((v) => {
5069
5128
  if (on && v) setLatest(v);
@@ -5072,8 +5131,8 @@ window.__ModuleLoader__.load({
5072
5131
  on = false;
5073
5132
  };
5074
5133
  }, []);
5075
- const update = latest !== null && isNewerVersion(latest, "0.41.0") ? latest : null;
5076
- const nameText = "dsh-context (v0.41.0)";
5134
+ const update = latest !== null && isNewerVersion(latest, "0.41.2") ? latest : null;
5135
+ const nameText = "dsh-context (v0.41.2)";
5077
5136
  const nameValue = [nameText];
5078
5137
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
5079
5138
  key: "update",
@@ -6636,6 +6695,7 @@ window.__ModuleLoader__.load({
6636
6695
  }, "dsh-context: dictionaries");
6637
6696
  const t = ctx.locale.bind(NS);
6638
6697
  const kit = makeViewKit(t);
6698
+ watchHistoryFaces(ctx);
6639
6699
  const settings = createContextSettings();
6640
6700
  const ContextView = makeContextView(ctx, kit, settings);
6641
6701
  ctx.slots.inject("conversation.view", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.41.0",
3
+ "version": "0.41.2",
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": {
@@ -12,6 +12,9 @@
12
12
  "url": "https://github.com/bowenliang123/dsh-context/issues"
13
13
  },
14
14
  "type": "module",
15
+ "engines": {
16
+ "node": "^22.19.0 || >=24.0.0"
17
+ },
15
18
  "main": "lib/index.js",
16
19
  "types": "lib/index.d.ts",
17
20
  "exports": {
@@ -49,12 +52,23 @@
49
52
  },
50
53
  "client": {
51
54
  "inject": [
55
+ "@deepseek-ai/dsh-api-remotes",
52
56
  "@deepseek-ai/dsh-client-connection",
53
57
  "@deepseek-ai/dsh-client-locale",
54
58
  "@deepseek-ai/dsh-client-ui-conversation",
55
59
  "@deepseek-ai/dsh-client-ui-settings"
56
60
  ],
57
61
  "platform": "web"
62
+ },
63
+ "compatibility": {
64
+ "dshReleases": {
65
+ "0.1.1-rc.2": "compatible",
66
+ "0.1.2-alpha.2": "compatible",
67
+ "0.1.2-alpha.3": "compatible",
68
+ "0.1.2-alpha.4": "compatible",
69
+ "0.1.2-alpha.5": "compatible",
70
+ "0.1.2-rc.1": "compatible"
71
+ }
58
72
  }
59
73
  },
60
74
  "keywords": [