@heixui/dsh-session-suite 0.6.2 → 0.7.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/client.js CHANGED
@@ -2937,8 +2937,18 @@ window.__ModuleLoader__.load({
2937
2937
  // settings page said something else entirely. 'all' now means every store.
2938
2938
  useEffect(() => {
2939
2939
  let alive = true
2940
+ // Whether the long-term memory engine is running at all.
2941
+ //
2942
+ // It is OFF by default because it collides with dsh-fish-memory: both inject at
2943
+ // `systemPrompt.context({ order: 900 })`, so two memory blocks per request and
2944
+ // double the tokens. When it is off the API answers LONGTERM_DISABLED and the
2945
+ // panel must show only the session half — a memory row that opens onto a
2946
+ // disabled engine is worse than no row.
2940
2947
  Promise.all([
2941
- call({ action: 'longterm.list', scope: 'all' }).catch(() => []),
2948
+ call({ action: 'longterm.list', scope: 'all' }).then(
2949
+ value => ({ enabled: true, value }),
2950
+ error => ({ enabled: error?.code !== 'LONGTERM_DISABLED', value: [] }),
2951
+ ),
2942
2952
  call({ action: 'list' }).catch(() => []),
2943
2953
  // Per-workspace breakdown, so the totals can be explained rather than
2944
2954
  // merely displayed. The counts above are sums; without this the user
@@ -2946,15 +2956,16 @@ window.__ModuleLoader__.load({
2946
2956
  // made the earlier 17/21/27/51 mismatch look like a bug.
2947
2957
  call({ action: 'longterm.workspaces' }).catch(() => []),
2948
2958
  ])
2949
- .then(([memories, sessions, workspaces]) => {
2959
+ .then(([memory, sessions, workspaces]) => {
2950
2960
  if (!alive) return
2951
2961
  const items = Array.isArray(sessions) ? sessions : []
2952
2962
  const stores = Array.isArray(workspaces) ? workspaces : []
2953
- const memoryRows = Array.isArray(memories) ? memories : []
2963
+ const memoryRows = Array.isArray(memory?.value) ? memory.value : []
2954
2964
  // The global store's size, so the breakdown adds up to the total instead
2955
2965
  // of leaving the difference unexplained.
2956
2966
  const globalRows = memoryRows.filter(row => row.scope === 'global').length
2957
2967
  setCounts({
2968
+ memoryEnabled: memory?.enabled === true,
2958
2969
  memories: memoryRows.length,
2959
2970
  memoryActive: memoryRows.filter(row => row.archived !== true).length,
2960
2971
  memoryArchived: memoryRows.filter(row => row.archived === true).length,
@@ -3042,8 +3053,14 @@ window.__ModuleLoader__.load({
3042
3053
  ...[
3043
3054
  ['panel.statSessions', counts?.sessions, 'panel.statSessionsHint'],
3044
3055
  ['panel.statSnapshots', counts?.snapshots, 'panel.statSnapshotsHint'],
3045
- ['panel.statMemories', counts?.memories, 'panel.statMemoriesHint'],
3046
- ['panel.statArchived', counts?.archived, 'panel.statArchivedHint'],
3056
+ // Memory figures only exist when the engine is on. Showing a zero here
3057
+ // would claim "you have no memories" when the truth is "memory is off".
3058
+ ...(counts?.memoryEnabled === true
3059
+ ? [
3060
+ ['panel.statMemories', counts?.memories, 'panel.statMemoriesHint'],
3061
+ ['panel.statArchived', counts?.archived, 'panel.statArchivedHint'],
3062
+ ]
3063
+ : [['panel.statArchived', counts?.archived, 'panel.statArchivedHint']]),
3047
3064
  ].map(([label, value, hint]) =>
3048
3065
  h(
3049
3066
  'div',
@@ -3055,7 +3072,8 @@ window.__ModuleLoader__.load({
3055
3072
  ),
3056
3073
  ),
3057
3074
  // The decomposition, so the total is checkable rather than a number to trust.
3058
- counts?.stores?.length
3075
+ // Only when memory is on: it explains the memory total.
3076
+ counts?.memoryEnabled === true && counts?.stores?.length
3059
3077
  ? h(
3060
3078
  'div',
3061
3079
  { className: 'dss-breakdown' },
@@ -3085,7 +3103,11 @@ window.__ModuleLoader__.load({
3085
3103
  'div',
3086
3104
  { className: 'dss-nav' },
3087
3105
  row('snapshots', t('panel.snapshots'), t('panel.snapshotsHint'), counts?.snapshots),
3088
- row('memory', t('panel.memory'), t('panel.memoryHint'), counts?.memories),
3106
+ // The memory screen is omitted entirely when the engine is off: it would
3107
+ // open onto a disabled backend and report LONGTERM_DISABLED in red.
3108
+ counts?.memoryEnabled === true
3109
+ ? row('memory', t('panel.memory'), t('panel.memoryHint'), counts?.memories)
3110
+ : null,
3089
3111
  row('archived', t('panel.archived'), t('panel.archivedHint'), counts?.archived),
3090
3112
  ),
3091
3113
  h(
package/cordis.patch.yml CHANGED
@@ -34,8 +34,19 @@
34
34
  stripStepTags: true
35
35
 
36
36
  # ---------- long-term memory ----------
37
- # Turn the whole memory engine on or off.
38
- longTermMemory: true
37
+ # OFF by default. The session half of this bundle (snapshots, rewind,
38
+ # delete, share, export, archive) does not need it and is always on.
39
+ #
40
+ # Why off: dsh-fish-memory registers the SAME injection point —
41
+ # `systemPrompt.context({ order: 900 })` — so with both plugins enabled
42
+ # every request carries two memory blocks. That doubles the injected
43
+ # tokens and hands the model two possibly-contradictory fact sets.
44
+ # fish-memory's implicit extraction demonstrably works; ours never
45
+ # produced a single suggestion across seven fix attempts and, in the
46
+ # last one, crashed the Host with a 500ms polling timer. The memory
47
+ # engine therefore stays available but dormant: set this to true only
48
+ # after disabling fish-memory's injection.
49
+ longTermMemory: false
39
50
  # Model chain used for implicit extraction (one cheap call per turn).
40
51
  # Tried in order until one answers; a 429 or an unreachable provider just
41
52
  # moves on to the next entry. All of these are free tiers.
package/lib/api.js CHANGED
@@ -10,6 +10,31 @@
10
10
  /** Absolute path Connection registers. */
11
11
  export const API_PATH = '/api/session-suite'
12
12
 
13
+ /**
14
+ * Every action that needs the long-term memory engine.
15
+ *
16
+ * Kept as an explicit set rather than an `action.startsWith('longterm.')` test so that a
17
+ * misspelled action still fails as an unknown action (400) instead of being reported as
18
+ * "memory is disabled" — the second answer would hide a typo.
19
+ */
20
+ export const LONGTERM_ACTIONS = new Set([
21
+ 'longterm.list',
22
+ 'longterm.remember',
23
+ 'longterm.update',
24
+ 'longterm.remove',
25
+ 'longterm.recall',
26
+ 'longterm.explain',
27
+ 'longterm.pending',
28
+ 'longterm.settle',
29
+ 'longterm.maintain',
30
+ 'longterm.status',
31
+ 'longterm.workspaces',
32
+ 'longterm.models',
33
+ 'longterm.stats',
34
+ 'longterm.settings',
35
+ 'longterm.export',
36
+ ])
37
+
13
38
  const MAX_BODY_BYTES = 256 * 1024
14
39
 
15
40
  /**
@@ -63,6 +88,30 @@ async function handle(service, deps, request) {
63
88
  * @returns {Promise<Response>} response
64
89
  */
65
90
  async function respond(service, deps, action, input) {
91
+ // The long-term memory engine is OPTIONAL and off by default.
92
+ //
93
+ // It is off because it collides with dsh-fish-memory: both register
94
+ // `systemPrompt.context({ order: 900 })`, so with both installed every request
95
+ // carries two memory blocks — double the tokens and two possibly-contradictory
96
+ // sets of facts. fish-memory's implicit extraction works and ours never did, so
97
+ // the memory half of this bundle is opt-in and the session half is always on.
98
+ //
99
+ // When the engine is absent every longterm.* action must answer cleanly rather
100
+ // than throw. `service.longTerm` is undefined in that case, and `await
101
+ // undefined.list(...)` would surface as an opaque 500 in the panel.
102
+ //
103
+ // The guard checks the action against a KNOWN list first: an action that does
104
+ // not exist is still a 400 (a typo must be loud), and only a real action is
105
+ // reported as disabled. Returning "disabled" for a nonexistent action would
106
+ // silently hide a wrong action name.
107
+ if (!service.longTerm && LONGTERM_ACTIONS.has(action)) {
108
+ return json({
109
+ ok: false,
110
+ error: 'long-term memory is disabled in this profile',
111
+ code: 'LONGTERM_DISABLED',
112
+ })
113
+ }
114
+
66
115
  switch (action) {
67
116
  case 'ping':
68
117
  return json({ ok: true, data: { ready: true, version: 1 } })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heixui/dsh-session-suite",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
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",