@yadsh/dsh-sleev 0.0.2 → 0.0.4

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 CHANGED
@@ -10,7 +10,7 @@ Sleev routing observability for [DeepSeek Harness](https://github.com/deepseek-a
10
10
 
11
11
  `dsh-sleev` observes provider routes that pass through the external Sleev context-optimization gateway. The current observer does not rewrite prompts, implement compaction, or route traffic itself: routing remains a normal `@deepseek-ai/dsh-llm-pi-ai` provider configuration.
12
12
 
13
- [简体中文](README.zh-CN.md) · [Specification](dsh-sleev-spec-v0.1.md) · [Development guide](docs/development.md) · [Compatibility notes](docs/compatibility.md)
13
+ [简体中文](README.zh-CN.md) · [Specification](SPEC.md) · [Development guide](docs/development.md) · [Compatibility notes](docs/compatibility.md)
14
14
 
15
15
  ## Installation
16
16
 
package/README.zh-CN.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  `dsh-sleev` 用于观测经由外部 Sleev 上下文优化网关的提供商路由。当前观测器不会改写提示词、实现压缩或自行路由流量;路由仍由普通的 `@deepseek-ai/dsh-llm-pi-ai` 提供商配置负责。
12
12
 
13
- [English](README.md) · [规范](dsh-sleev-spec-v0.1.md) · [开发指南](docs/development.md) · [兼容性说明](docs/compatibility.md)
13
+ [English](README.md) · [规范](SPEC.md) · [开发指南](docs/development.md) · [兼容性说明](docs/compatibility.md)
14
14
 
15
15
  ## 安装
16
16
 
@@ -0,0 +1,16 @@
1
+ {
2
+ "deepseekHarness": {
3
+ "channel": "next",
4
+ "range": ">=0.1.1-rc.2 <0.2.0",
5
+ "testedReleases": [
6
+ "0.1.1-rc.2"
7
+ ],
8
+ "requiredHostFeatures": [
9
+ "llm/stream"
10
+ ],
11
+ "requiredClientFeatures": [
12
+ "settings.plugin.item"
13
+ ]
14
+ },
15
+ "node": "^22.19.0 || >=24.0.0"
16
+ }
@@ -3,7 +3,7 @@
3
3
  ## Prerequisites
4
4
 
5
5
  - Node.js 22.19+ or 24+
6
- - pnpm 11
6
+ - pnpm 10.4.1
7
7
  - DeepSeek Harness 0.1.1-rc.2
8
8
  - Sleev CLI 1.7.7 for a real gateway test
9
9
 
package/lib/client.js CHANGED
@@ -4,9 +4,110 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
7
8
  let react = require("react");
8
9
  let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
9
- let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region ../../packages/plugin-kit/lib/client/plugin-card-css.js
11
+ /**
12
+ * Canonical DSH settings-plugin card shell CSS — the single source of truth
13
+ * for the shared `dsh-plugin-card` outer shell (see AGENTS.md). Plugins must
14
+ * inject this CSS unchanged and may only append rules for their own controls
15
+ * inside the card body; plugin-specific shell styling is forbidden.
16
+ */
17
+ const PLUGIN_CARD_SHELL_CSS = `
18
+ .dsh-plugin-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
19
+ .dsh-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed)}
20
+ .dsh-plugin-card--open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
21
+ .dsh-plugin-card__header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
22
+ .dsh-plugin-card__header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
23
+ .dsh-plugin-card__head-text{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
24
+ .dsh-plugin-card__name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
25
+ .dsh-plugin-card__description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
26
+ .dsh-plugin-card__badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
27
+ .dsh-plugin-card__chevron{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}
28
+ .dsh-plugin-card--open .dsh-plugin-card__chevron{transform:rotate(180deg)}
29
+ .dsh-plugin-card__body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
30
+ `;
31
+ //#endregion
32
+ //#region ../../packages/plugin-kit/lib/client/chevron.js
33
+ /**
34
+ * The standard disclosure chevron of the shared settings-card shell: a
35
+ * 14x14 inline SVG stroked with `currentColor` (round caps and joins).
36
+ * Font glyphs must not be used for this shape — their appearance and
37
+ * baseline vary by font and encoding.
38
+ */
39
+ function ChevronDown() {
40
+ return (0, react_jsx_runtime.jsx)("svg", {
41
+ className: "dsh-plugin-card__chevron",
42
+ viewBox: "0 0 14 14",
43
+ fill: "none",
44
+ "aria-hidden": "true",
45
+ children: (0, react_jsx_runtime.jsx)("path", {
46
+ d: "m3.5 5.25 3.5 3.5 3.5-3.5",
47
+ stroke: "currentColor",
48
+ strokeLinecap: "round",
49
+ strokeLinejoin: "round"
50
+ })
51
+ });
52
+ }
53
+ //#endregion
54
+ //#region ../../packages/plugin-kit/lib/client/card-shell.js
55
+ /**
56
+ * Shared outer shell of a plugin configuration card (AGENTS.md contract): a
57
+ * direct `<li>` child of the host list with a full-width header button
58
+ * (`type="button"`, `aria-expanded`, accessible show/hide label, the
59
+ * title/description stack, an optional status badge, then the chevron) and a
60
+ * body rendered only while open. Plugin-specific controls belong inside the
61
+ * body and must not restyle the shell.
62
+ */
63
+ function CardShell(props) {
64
+ const [open, setOpen] = (0, react.useState)(false);
65
+ const body = props.bodyClassName === void 0 ? "dsh-plugin-card__body" : `dsh-plugin-card__body ${props.bodyClassName}`;
66
+ return (0, react_jsx_runtime.jsxs)("li", {
67
+ className: open ? "dsh-plugin-card dsh-plugin-card--open" : "dsh-plugin-card",
68
+ children: [(0, react_jsx_runtime.jsxs)("button", {
69
+ type: "button",
70
+ className: "dsh-plugin-card__header",
71
+ "aria-expanded": open,
72
+ "aria-label": props.label(open),
73
+ onClick: () => {
74
+ setOpen(!open);
75
+ },
76
+ children: [
77
+ (0, react_jsx_runtime.jsxs)("span", {
78
+ className: "dsh-plugin-card__head-text",
79
+ children: [(0, react_jsx_runtime.jsx)("span", {
80
+ className: "dsh-plugin-card__name",
81
+ children: props.title
82
+ }), (0, react_jsx_runtime.jsx)("span", {
83
+ className: "dsh-plugin-card__description",
84
+ children: props.description
85
+ })]
86
+ }),
87
+ props.badge ?? null,
88
+ (0, react_jsx_runtime.jsx)(ChevronDown, {})
89
+ ]
90
+ }), open ? (0, react_jsx_runtime.jsx)("div", {
91
+ className: body,
92
+ children: props.children
93
+ }) : null]
94
+ });
95
+ }
96
+ /**
97
+ * Register the card component into the shared settings-plugins slot. Returns
98
+ * the registration disposer. Use this variant when the plugin drives the
99
+ * slot's inject-factory itself (e.g. to dispose a form controller alongside).
100
+ */
101
+ function registerSettingsSlot(host, options) {
102
+ const slotOptions = {
103
+ name: options.slotName ?? "settings.plugin.item",
104
+ key: options.key,
105
+ ...options.locale === void 0 ? {} : { locale: options.locale },
106
+ ...options.inject === void 0 ? {} : { inject: options.inject }
107
+ };
108
+ return host.slots.register(slotOptions, options.component);
109
+ }
110
+ //#endregion
10
111
  //#region src/client/settings-controller.ts
11
112
  const DEFAULTS = {
12
113
  routes: [],
@@ -239,18 +340,8 @@ window.__ModuleLoader__.load({
239
340
  save: "保存",
240
341
  saving: "保存中…"
241
342
  };
242
- const CARD_STYLES = `
243
- .dsh-sleev-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);transition:border-color .16s ease,background .16s ease}
244
- .dsh-sleev-card:hover{border-color:var(--dsw-alias-border-label-dimmed)}
245
- .dsh-sleev-card.dsh-sleev-card-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-label-dimmed)}
246
- .dsh-sleev-header{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:12px}
247
- .dsh-sleev-header:focus-visible,.dsh-sleev-button:focus-visible,.dsh-sleev-reset:focus-visible,.dsh-sleev-input:focus-visible{outline:2px solid var(--dsw-alias-border-brand);outline-offset:2px}
248
- .dsh-sleev-head-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
249
- .dsh-sleev-name{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}
250
- .dsh-sleev-description{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
251
- .dsh-sleev-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s ease;font-size:16px;line-height:1}
252
- .dsh-sleev-card-open .dsh-sleev-chevron{transform:rotate(180deg)}
253
- .dsh-sleev-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
343
+ const CARD_STYLES = `${PLUGIN_CARD_SHELL_CSS}
344
+ .dsh-sleev-button:focus-visible,.dsh-sleev-reset:focus-visible,.dsh-sleev-input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
254
345
  .dsh-sleev-read-only{margin:12px 0 0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
255
346
  .dsh-sleev-pill{border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px;font-weight:500;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);white-space:nowrap}
256
347
  .dsh-sleev-field{display:flex;flex-direction:column;gap:6px;padding:12px 0}
@@ -312,7 +403,6 @@ textarea.dsh-sleev-input{height:64px;min-height:48px;padding:8px 12px;resize:ver
312
403
  }
313
404
  /** Settings card contributed to the official Plugins → Plugin configuration tab. */
314
405
  function SleevSettingsCard(props) {
315
- const [open, setOpen] = (0, react.useState)(false);
316
406
  const state = props.useSleevSettings((snapshot) => snapshot);
317
407
  if (!state.available) return null;
318
408
  const blocked = !state.dirty || state.invalid || state.saving || !state.writable;
@@ -324,140 +414,116 @@ textarea.dsh-sleev-input{height:64px;min-height:48px;padding:8px 12px;resize:ver
324
414
  resetLabel: props.t("reset"),
325
415
  onReset: () => props.resetField(field)
326
416
  });
327
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
328
- className: `dsh-sleev-card${open ? " dsh-sleev-card-open" : ""}`,
329
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
330
- type: "button",
331
- className: "dsh-sleev-header",
332
- "aria-expanded": open,
333
- "aria-label": `${props.t(open ? "collapse" : "expand")}: Sleev`,
334
- onClick: () => setOpen(!open),
335
- children: [
336
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
337
- className: "dsh-sleev-head-text",
338
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
339
- className: "dsh-sleev-name",
340
- children: props.t("title")
341
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
342
- className: "dsh-sleev-description",
343
- children: props.t("description")
344
- })]
345
- }),
346
- state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
347
- className: "dsh-sleev-pill",
348
- children: props.t("unsaved")
349
- }) : null,
350
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
351
- className: "dsh-sleev-chevron",
352
- "aria-hidden": "true",
353
- children: "⌄"
354
- })
355
- ]
356
- }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
357
- className: "dsh-sleev-body",
358
- children: [
359
- !state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
360
- className: "dsh-sleev-read-only",
361
- role: "status",
362
- children: props.t("readOnly")
363
- }) : null,
364
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
417
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CardShell, {
418
+ title: props.t("title"),
419
+ description: props.t("description"),
420
+ label: (open) => `${props.t(open ? "collapse" : "expand")}: Sleev`,
421
+ badge: state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
422
+ className: "dsh-plugin-card__badge",
423
+ children: props.t("unsaved")
424
+ }) : void 0,
425
+ children: [
426
+ !state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
427
+ className: "dsh-sleev-read-only",
428
+ role: "status",
429
+ children: props.t("readOnly")
430
+ }) : null,
431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
432
+ id: "sleev-routes",
433
+ label: props.t("routes"),
434
+ hint: props.t("routesHint"),
435
+ ...common("routes"),
436
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
365
437
  id: "sleev-routes",
366
- label: props.t("routes"),
367
- hint: props.t("routesHint"),
368
- ...common("routes"),
369
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
370
- id: "sleev-routes",
371
- className: "dsh-sleev-input",
372
- value: state.routes.text,
373
- disabled: !state.writable,
374
- onChange: edit("routes")
375
- })
376
- }),
377
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
438
+ className: "dsh-sleev-input",
439
+ value: state.routes.text,
440
+ disabled: !state.writable,
441
+ onChange: edit("routes")
442
+ })
443
+ }),
444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
445
+ id: "sleev-route-prefixes",
446
+ label: props.t("routePrefixes"),
447
+ hint: props.t("routePrefixesHint"),
448
+ ...common("routePrefixes"),
449
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
378
450
  id: "sleev-route-prefixes",
379
- label: props.t("routePrefixes"),
380
- hint: props.t("routePrefixesHint"),
381
- ...common("routePrefixes"),
382
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
383
- id: "sleev-route-prefixes",
384
- className: "dsh-sleev-input",
385
- value: state.routePrefixes.text,
386
- disabled: !state.writable,
387
- onChange: edit("routePrefixes")
388
- })
389
- }),
390
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
451
+ className: "dsh-sleev-input",
452
+ value: state.routePrefixes.text,
453
+ disabled: !state.writable,
454
+ onChange: edit("routePrefixes")
455
+ })
456
+ }),
457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
458
+ id: "sleev-max-recent-calls",
459
+ label: props.t("maxRecentCalls"),
460
+ hint: props.t("maxRecentCallsHint"),
461
+ invalidLabel: props.t("invalidNumber"),
462
+ ...common("maxRecentCalls"),
463
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
391
464
  id: "sleev-max-recent-calls",
392
- label: props.t("maxRecentCalls"),
393
- hint: props.t("maxRecentCallsHint"),
394
- invalidLabel: props.t("invalidNumber"),
395
- ...common("maxRecentCalls"),
396
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
397
- id: "sleev-max-recent-calls",
398
- className: "dsh-sleev-input",
399
- type: "number",
400
- min: 1,
401
- step: 1,
402
- value: state.maxRecentCalls.text,
403
- disabled: !state.writable,
404
- "aria-invalid": state.maxRecentCalls.invalid,
405
- onChange: edit("maxRecentCalls")
406
- })
407
- }),
408
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
465
+ className: "dsh-sleev-input",
466
+ type: "number",
467
+ min: 1,
468
+ step: 1,
469
+ value: state.maxRecentCalls.text,
470
+ disabled: !state.writable,
471
+ "aria-invalid": state.maxRecentCalls.invalid,
472
+ onChange: edit("maxRecentCalls")
473
+ })
474
+ }),
475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsField, {
476
+ id: "sleev-log-level",
477
+ label: props.t("logLevel"),
478
+ hint: props.t("logLevelHint"),
479
+ ...common("logLevel"),
480
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
409
481
  id: "sleev-log-level",
410
- label: props.t("logLevel"),
411
- hint: props.t("logLevelHint"),
412
- ...common("logLevel"),
413
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
414
- id: "sleev-log-level",
415
- className: "dsh-sleev-input",
416
- value: state.logLevel.text,
417
- disabled: !state.writable,
418
- onChange: edit("logLevel"),
419
- children: [
420
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
421
- value: "off",
422
- children: props.t("logOff")
423
- }),
424
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
425
- value: "info",
426
- children: props.t("logInfo")
427
- }),
428
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
429
- value: "debug",
430
- children: props.t("logDebug")
431
- })
432
- ]
433
- })
434
- }),
435
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
436
- className: "dsh-sleev-footer",
482
+ className: "dsh-sleev-input",
483
+ value: state.logLevel.text,
484
+ disabled: !state.writable,
485
+ onChange: edit("logLevel"),
437
486
  children: [
438
- state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
439
- className: "dsh-sleev-save-error",
440
- role: "status",
441
- children: props.t("saveFailed")
442
- }) : null,
443
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
444
- type: "button",
445
- className: "dsh-sleev-button dsh-sleev-discard",
446
- disabled: !state.dirty || state.saving,
447
- onClick: props.discard,
448
- children: props.t("discard")
487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
488
+ value: "off",
489
+ children: props.t("logOff")
449
490
  }),
450
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
451
- type: "button",
452
- className: "dsh-sleev-button dsh-sleev-save",
453
- disabled: blocked,
454
- onClick: props.save,
455
- children: props.t(state.saving ? "saving" : "save")
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
492
+ value: "info",
493
+ children: props.t("logInfo")
494
+ }),
495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
496
+ value: "debug",
497
+ children: props.t("logDebug")
456
498
  })
457
499
  ]
458
500
  })
459
- ]
460
- }) : null]
501
+ }),
502
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
503
+ className: "dsh-sleev-footer",
504
+ children: [
505
+ state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
506
+ className: "dsh-sleev-save-error",
507
+ role: "status",
508
+ children: props.t("saveFailed")
509
+ }) : null,
510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
511
+ type: "button",
512
+ className: "dsh-sleev-button dsh-sleev-discard",
513
+ disabled: !state.dirty || state.saving,
514
+ onClick: props.discard,
515
+ children: props.t("discard")
516
+ }),
517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
518
+ type: "button",
519
+ className: "dsh-sleev-button dsh-sleev-save",
520
+ disabled: blocked,
521
+ onClick: props.save,
522
+ children: props.t(state.saving ? "saving" : "save")
523
+ })
524
+ ]
525
+ })
526
+ ]
461
527
  });
462
528
  }
463
529
  const inject = [
@@ -480,12 +546,12 @@ textarea.dsh-sleev-input{height:64px;min-height:48px;padding:8px 12px;resize:ver
480
546
  }), "dsh-sleev: settings dictionaries");
481
547
  const controller = new SleevSettingsController(ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }));
482
548
  ctx.slots.inject("settings.plugin.item", () => {
483
- const unregister = ctx.slots.register({
484
- name: "settings.plugin.item",
549
+ const unregister = registerSettingsSlot(ctx, {
485
550
  key: SETTINGS_NAMESPACE,
486
551
  locale: LOCALE_NAMESPACE,
552
+ component: SleevSettingsCard,
487
553
  inject: () => controller.inject()
488
- }, SleevSettingsCard);
554
+ });
489
555
  return () => {
490
556
  controller.dispose();
491
557
  unregister();
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Service } from "@deepseek-ai/cordis";
2
2
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
3
+ import { createHostLoggerSink, getPluginLogger } from "@yadsh/dsh-plugin-log";
3
4
  import z from "@deepseek-ai/schemastery";
4
5
  import { isAgentLoopRequest } from "@deepseek-ai/dsh-llm";
5
6
  import { randomUUID } from "node:crypto";
@@ -141,13 +142,12 @@ var CallTelemetryStore = class {
141
142
  const startedAt = this.now();
142
143
  let usage;
143
144
  let finished = false;
144
- if (startConfig.logLevel === "debug") this.logger.debug(`dsh-sleev: call-start ${JSON.stringify({
145
+ if (startConfig.logLevel === "debug") this.logger.debug("sleev.call.start", {
145
146
  callId,
146
147
  provider: options.provider,
147
148
  model: options.model,
148
- kind,
149
- sessionId: options.sessionId
150
- })}`);
149
+ kind
150
+ });
151
151
  return {
152
152
  observeUsage: (value) => {
153
153
  usage = normalizeUsage(value);
@@ -176,9 +176,20 @@ var CallTelemetryStore = class {
176
176
  this.completed.push(telemetry);
177
177
  const finishConfig = this.reconfigure();
178
178
  if (finishConfig.logLevel !== "off") {
179
- const message = `dsh-sleev: call-end ${JSON.stringify(telemetry)}`;
180
- if (finishConfig.logLevel === "debug") this.logger.debug(message);
181
- else this.logger.info(message);
179
+ const fields = {
180
+ callId: telemetry.callId,
181
+ provider: telemetry.provider,
182
+ model: telemetry.model,
183
+ kind: telemetry.kind,
184
+ durationMs: telemetry.durationMs,
185
+ ...telemetry.providerUsage === void 0 ? {} : {
186
+ providerUsage: telemetry.providerUsage,
187
+ derived: telemetry.derived
188
+ },
189
+ result: telemetry.result
190
+ };
191
+ if (finishConfig.logLevel === "debug") this.logger.debug("sleev.call.end", fields);
192
+ else this.logger.info("sleev.call.end", fields);
182
193
  }
183
194
  }
184
195
  };
@@ -197,6 +208,9 @@ var CallTelemetryStore = class {
197
208
  //#endregion
198
209
  //#region src/index.ts
199
210
  const name = "dsh-sleev";
211
+ function pluginLogLevel(level) {
212
+ return level === "off" ? "silent" : level;
213
+ }
200
214
  /** User-editable settings section rendered by the browser client card. */
201
215
  const SLEEV_SETTINGS_NAMESPACE = settingsNamespace(SLEEV_SETTINGS_NAMESPACE_ID);
202
216
  /** Host-side observer service. Routing itself remains owned by llm-pi-ai. */
@@ -204,6 +218,7 @@ var SleevIntegrationService = class extends Service {
204
218
  static inject = ["llm"];
205
219
  static Config = ConfigSchema;
206
220
  telemetry;
221
+ logger;
207
222
  constructor(ctx, input = {}) {
208
223
  super(ctx, "sleev");
209
224
  const resolvedEntry = resolveConfig(input);
@@ -214,12 +229,26 @@ var SleevIntegrationService = class extends Service {
214
229
  logLevel: resolvedEntry.logLevel
215
230
  };
216
231
  let configSource = () => entry;
217
- this.telemetry = new CallTelemetryStore(ctx.logger, () => resolveConfig(configSource()));
232
+ this.logger = getPluginLogger({
233
+ pluginId: "dsh-sleev",
234
+ level: pluginLogLevel(resolvedEntry.logLevel),
235
+ console: "trace",
236
+ consoleSink: createHostLoggerSink(ctx.logger)
237
+ });
238
+ ctx.effect(() => async () => this.logger.close(), "dsh-sleev.logger");
239
+ this.telemetry = new CallTelemetryStore(this.logger.child("telemetry"), () => resolveConfig(configSource()));
218
240
  installSettingsSection(ctx, SLEEV_SETTINGS_NAMESPACE, ConfigSchema, entry, {
219
241
  setSource: (current) => {
220
242
  configSource = current;
221
243
  },
222
- onChange: () => this.telemetry.reconfigure()
244
+ onChange: () => {
245
+ const config = this.telemetry.reconfigure();
246
+ this.logger.setLevel(pluginLogLevel(config.logLevel));
247
+ this.logger.info("telemetry.config.updated", {
248
+ logLevel: config.logLevel,
249
+ maxRecentCalls: config.maxRecentCalls
250
+ });
251
+ }
223
252
  });
224
253
  ctx.on("llm/stream", (options, next) => {
225
254
  const config = resolveConfig(configSource());
@@ -227,10 +256,10 @@ var SleevIntegrationService = class extends Service {
227
256
  const handle = this.telemetry.begin(options, classifyRequest(options));
228
257
  return observeStream(next(), handle);
229
258
  }, { global: true });
230
- if (resolvedEntry.logLevel !== "off") ctx.logger.info(`dsh-sleev: observer active ${JSON.stringify({
259
+ if (resolvedEntry.logLevel !== "off") this.logger.info("plugin.ready", {
231
260
  routes: resolvedEntry.routes,
232
261
  routePrefixes: resolvedEntry.routePrefixes
233
- })}`);
262
+ });
234
263
  }
235
264
  /** Bounded completed-call snapshot; no prompts, headers, or credentials. */
236
265
  listRecentCalls() {
@@ -3,8 +3,8 @@ import type { ResolvedConfig } from "../shared/config.js";
3
3
  import type { CallResult, OptimizerCallTelemetry, RequestKind } from "../shared/telemetry.js";
4
4
  /** Minimal logger face used by Cordis and isolated unit tests. */
5
5
  export interface TelemetryLogger {
6
- debug(message: string): void;
7
- info(message: string): void;
6
+ debug(event: string, fields?: Record<string, unknown>): void;
7
+ info(event: string, fields?: Record<string, unknown>): void;
8
8
  }
9
9
  /** Mutable handle kept only for the duration of a streaming call. */
10
10
  export interface CallHandle {
@@ -14,6 +14,7 @@ export declare class SleevIntegrationService extends Service {
14
14
  static inject: string[];
15
15
  static Config: import("@deepseek-ai/schemastery").default<Config>;
16
16
  private readonly telemetry;
17
+ private readonly logger;
17
18
  constructor(ctx: Context, input?: Config);
18
19
  /** Bounded completed-call snapshot; no prompts, headers, or credentials. */
19
20
  listRecentCalls(): readonly OptimizerCallTelemetry[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yadsh/dsh-sleev",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Sleev routing observability for DeepSeek Harness",
5
5
  "repository": {
6
6
  "type": "git",
@@ -50,6 +50,7 @@
50
50
  "lib/**/*.js",
51
51
  "lib/types/**/*.d.ts",
52
52
  "cordis.patch.yml",
53
+ "compatibility.json",
53
54
  "docs/*",
54
55
  "README.md",
55
56
  "README.zh-CN.md",
@@ -79,10 +80,11 @@
79
80
  "@deepseek-ai/dsh-settings": ">=0.1.1-rc.2 <0.2.0",
80
81
  "react": "^18.2.0"
81
82
  },
82
- "dependencies": {},
83
+ "dependencies": {
84
+ "@yadsh/dsh-plugin-log": "^0.2.1"
85
+ },
83
86
  "devDependencies": {
84
87
  "@deepseek-ai/cordis": "4.0.1",
85
- "@deepseek-ai/schemastery": "3.18.1",
86
88
  "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
87
89
  "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
88
90
  "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
@@ -91,15 +93,18 @@
91
93
  "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
92
94
  "@deepseek-ai/dsh-llm-pi-ai": "0.1.1-rc.2",
93
95
  "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
96
+ "@deepseek-ai/schemastery": "3.18.1",
94
97
  "@types/node": "^26.4.0",
95
98
  "@types/react": "18.3.28",
96
99
  "eslint": "10.9.1",
97
100
  "prettier": "^3.6.2",
98
101
  "react": "18.3.1",
99
102
  "tsdown": "^0.22.14",
100
- "tsx": "4.22.4",
103
+ "tsx": "^4.22.4",
101
104
  "typescript": "^7.0.2",
102
- "vitest": "^4.1.11"
105
+ "vitest": "^4.1.11",
106
+ "@yadsh/dsh-plugin-kit": "^0.1.0",
107
+ "@yadsh/dsh-config": "^0.0.0"
103
108
  },
104
109
  "publishConfig": {
105
110
  "access": "public",