@heixui/dsh-session-suite 0.5.0 → 0.5.1

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/client.js CHANGED
@@ -272,6 +272,7 @@ window.__ModuleLoader__.load({
272
272
  'memory.extractionNotRegistered': 'HOOK NOT REGISTERED',
273
273
  'memory.extractionNoTrace': 'no extraction trace recorded by this Host',
274
274
  'memory.diagTitle': 'Extraction diagnostics:',
275
+ 'memory.diagSteps': '{ok}/{total} hooks installed',
275
276
  'memory.accept': 'Keep',
276
277
  'memory.reject': 'Discard',
277
278
  'memory.accepted': 'Kept',
@@ -520,6 +521,7 @@ window.__ModuleLoader__.load({
520
521
  'memory.extractionNotRegistered': '钩子未注册',
521
522
  'memory.extractionNoTrace': '本次运行没有记录到提取痕迹',
522
523
  'memory.diagTitle': '提取诊断:',
524
+ 'memory.diagSteps': '{ok}/{total} 个钩子已装',
523
525
  'memory.accept': '保留',
524
526
  'memory.reject': '丢弃',
525
527
  'memory.accepted': '已保留',
@@ -1047,6 +1049,7 @@ window.__ModuleLoader__.load({
1047
1049
  font-weight: 600;
1048
1050
  color: var(--dsw-alias-label-primary, #1f1f1f);
1049
1051
  }
1052
+ .dss-diag-steps { color: var(--dsw-alias-label-secondary, #6b6b6b); }
1050
1053
  /* The summary charts. Two narrow panels for composition and health, one wide panel
1051
1054
  for tags: composition and health are short lists, tags can run long. Bars are divs,
1052
1055
  not SVG, so they inherit the theme and stay legible in both palettes. */
@@ -3449,6 +3452,10 @@ window.__ModuleLoader__.load({
3449
3452
  // unremarkable" from "every memory model failed" or "a filter ate the
3450
3453
  // result". When there is nothing pending, say why.
3451
3454
  const extraction = status?.extraction
3455
+ // Which optional hooks installed. Reported separately from the extraction trace
3456
+ // because a hook that never installed cannot write a trace — that is the whole
3457
+ // point: "no trace" has two causes and this separates them.
3458
+ const installStatus = Array.isArray(status?.install) ? status.install : []
3452
3459
  const pendingBlock =
3453
3460
  pending.length > 0
3454
3461
  ? h(
@@ -3565,6 +3572,23 @@ window.__ModuleLoader__.load({
3565
3572
  : t('memory.extractionNotRegistered'),
3566
3573
  )
3567
3574
  : null,
3575
+ // The registration ledger: which optional hooks installed and which
3576
+ // threw. This is the only way to see a step that never ran, because the
3577
+ // code that would have reported it is the code that failed.
3578
+ installStatus.length > 0
3579
+ ? h(
3580
+ 'span',
3581
+ { className: 'dss-diag-steps' },
3582
+ t('memory.diagSteps', {
3583
+ ok: installStatus.filter(step => step.ok).length,
3584
+ total: installStatus.length,
3585
+ }),
3586
+ installStatus
3587
+ .filter(step => !step.ok)
3588
+ .map(step => ` · ${step.label}: ${step.error ?? 'failed'}`)
3589
+ .join(''),
3590
+ )
3591
+ : null,
3568
3592
  ),
3569
3593
  )
3570
3594
 
package/cordis.patch.yml CHANGED
@@ -54,12 +54,19 @@
54
54
  # says otherwise.
55
55
  recallLimit: 6
56
56
  # How much of the conversation before the current message is folded into the
57
- # retrieval query. A follow-up like "那它为什么慢?" has no referent for "它", so
58
- # searching those characters alone cannot find the memory that answers it.
59
- # The previous user turns are appended BEFORE the current question, so the
60
- # question's own terms still dominate the score. 0 disables the behaviour and
61
- # searches the current message exactly as before.
62
- contextQueryChars: 160
57
+ # retrieval query. DEFAULT 0 = OFF.
58
+ #
59
+ # Why off by default: the model already receives the full conversation history,
60
+ # so it knows what 「它」 refers to. Folding earlier turns into the query only
61
+ # helps the RETRIEVAL step pick a better memory; it does not make the model
62
+ # understand the question better. That is a real but small gain, and it costs a
63
+ # little accuracy on self-contained questions that get padded unnecessarily.
64
+ #
65
+ # Turn it on when the prompt is short by design — a workflow that deliberately
66
+ # keeps requests small (an agent that works from a compact brief rather than a
67
+ # long transcript) benefits, because there the history is NOT in the prompt.
68
+ # 160 characters across 3 previous turns is a reasonable starting point.
69
+ contextQueryChars: 0
63
70
  contextQueryTurns: 3
64
71
  # How many of the most important memories ride along regardless of the
65
72
  # question. Without a resident layer, an ordinary follow-up like "继续"
package/index.js CHANGED
@@ -97,6 +97,10 @@ export function apply(ctx, config = {}) {
97
97
  try {
98
98
  const installed = installMemory({ ctx, root, config, log })
99
99
  service.longTerm = installed.engine
100
+ // Kept separately: the engine is the memory store, and this is the record of
101
+ // which optional registrations succeeded. Exposed through the status endpoint so
102
+ // a missing feature can be traced to a step that never installed.
103
+ service.memoryInstallStatus = installed.installStatus
100
104
  log('long-term memory engine ready')
101
105
  } catch (error) {
102
106
  log('long-term memory failed to install; continuing without it', error)
package/lib/api.js CHANGED
@@ -303,8 +303,14 @@ async function respond(service, deps, action, input) {
303
303
  })
304
304
  case 'longterm.maintain':
305
305
  return json({ ok: true, data: await service.longTerm.maintain(workspaceOf(input)) })
306
- case 'longterm.status':
307
- return json({ ok: true, data: await service.longTerm.status() })
306
+ case 'longterm.status': {
307
+ // The engine's own status plus the registration ledger. Two sources on purpose:
308
+ // the engine cannot report a hook that never installed, because the code that
309
+ // would have written it is the code that failed.
310
+ const status = await service.longTerm.status()
311
+ const install = typeof service.memoryInstallStatus === 'function' ? service.memoryInstallStatus() : []
312
+ return json({ ok: true, data: { ...status, install } })
313
+ }
308
314
  case 'longterm.workspaces':
309
315
  return json({ ok: true, data: await service.longTerm.workspaces() })
310
316
  case 'longterm.models':
@@ -91,13 +91,21 @@ export function installMemory({ ctx, root, config, log }) {
91
91
  * Run one optional registration; a failure is reported and skipped so a single
92
92
  * incompatible hook can never disable the rest of the bundle.
93
93
  *
94
+ * Every step records its outcome. The silent-catch was the reason a broken step was
95
+ * invisible: "the queue is empty" could mean the hook was never installed, and
96
+ * nothing on screen said so. The steps list is published through status() so the
97
+ * settings page can show which registrations succeeded.
98
+ *
94
99
  * @param {string} label what is being installed
95
100
  * @param {() => void} install registration body
96
101
  */
102
+ const steps = []
97
103
  const safe = (label, install) => {
98
104
  try {
99
105
  install()
106
+ steps.push({ label, ok: true })
100
107
  } catch (error) {
108
+ steps.push({ label, ok: false, error: error instanceof Error ? error.message : String(error) })
101
109
  log(`long-term memory: ${label} failed to install`, error)
102
110
  }
103
111
  }
@@ -410,6 +418,9 @@ export function installMemory({ ctx, root, config, log }) {
410
418
  return {
411
419
  engine,
412
420
  dispose: () => {},
421
+ // The outcome of every optional registration, so "the queue is empty" can be
422
+ // traced to a specific step that never installed.
423
+ installStatus: () => [...steps],
413
424
  // Exposed so the settings page and the status endpoint can report whether the
414
425
  // last recall rebuild failed. Without this the failure is silent: the model
415
426
  // simply acts as if the memory is not there.
@@ -535,8 +546,17 @@ function contextualQuery(messages, limits = {}) {
535
546
  const current = lastUserText(messages)
536
547
  if (!current) return ''
537
548
 
538
- const maxChars = limits.chars ?? 160
539
- const maxTurns = limits.turns ?? 3
549
+ // 0 is the default, and it means disabled.
550
+ //
551
+ // Why: the model already receives the conversation history, so it knows what a
552
+ // pronoun refers to. Folding earlier turns into the retrieval query only improves
553
+ // which MEMORY is picked — a real but small gain — and it costs a little accuracy
554
+ // on self-contained questions that get padded unnecessarily. It matters for a
555
+ // workflow that deliberately keeps the prompt small, where the history is not there
556
+ // to be read; that is the case this exists for, and it is opt-in.
557
+ const maxChars = typeof limits.chars === 'number' ? limits.chars : 0
558
+ const maxTurns = typeof limits.turns === 'number' ? limits.turns : 3
559
+ if (maxChars <= 0) return current
540
560
 
541
561
  // A self-contained question needs no help. The test is NOT length alone: a short
542
562
  // question that names its own subject ("导出的图片为什么断链") is self-contained,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heixui/dsh-session-suite",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Session checkpoints for DSH: rewind a conversation with its files and memory, permanent session deletion, and shareable exports",
5
5
  "type": "module",
6
6
  "main": "./index.js",