@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -2
  3. package/assets/openvulcan-icon.svg +33 -0
  4. package/cordis.patch.yml +17 -0
  5. package/node_modules/@openvulcan/dsh-decision-guard/LICENSE +21 -0
  6. package/node_modules/@openvulcan/dsh-decision-guard/README.md +14 -0
  7. package/node_modules/@openvulcan/dsh-decision-guard/assets/decision-guard-icon.png +0 -0
  8. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.d.ts +40 -0
  9. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.js +150 -0
  10. package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +2193 -0
  11. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.d.ts +90 -0
  12. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.js +108 -0
  13. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.d.ts +129 -0
  14. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.js +572 -0
  15. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.d.ts +22 -0
  16. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.js +77 -0
  17. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.d.ts +158 -0
  18. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.js +340 -0
  19. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.d.ts +133 -0
  20. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.js +186 -0
  21. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.d.ts +55 -0
  22. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.js +202 -0
  23. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.d.ts +62 -0
  24. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.js +155 -0
  25. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.d.ts +2 -0
  26. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.js +4 -0
  27. package/node_modules/@openvulcan/dsh-decision-guard/package.json +68 -0
  28. package/node_modules/@openvulcan/dsh-vulcan-account/LICENSE +21 -0
  29. package/node_modules/@openvulcan/dsh-vulcan-account/assets/account-icon.png +0 -0
  30. package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +565 -0
  31. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.d.ts +92 -0
  32. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.js +194 -0
  33. package/node_modules/@openvulcan/dsh-vulcan-account/package.json +57 -0
  34. package/node_modules/@openvulcan/dsh-vulcan-answers/LICENSE +21 -0
  35. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/answers-icon.png +0 -0
  36. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-interactive.md +79 -0
  37. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-standard.md +1 -0
  38. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.d.ts +258 -0
  39. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.js +698 -0
  40. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +671 -0
  41. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.d.ts +29 -0
  42. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.js +70 -0
  43. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.d.ts +19 -0
  44. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.js +52 -0
  45. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.d.ts +9 -0
  46. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.js +9 -0
  47. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.d.ts +22 -0
  48. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.js +39 -0
  49. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.d.ts +20 -0
  50. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.js +17 -0
  51. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.d.ts +66 -0
  52. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.js +118 -0
  53. package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +59 -0
  54. package/node_modules/@openvulcan/dsh-vulcan-luaskills/LICENSE +21 -0
  55. package/node_modules/@openvulcan/dsh-vulcan-luaskills/assets/luaskills-icon.svg +1 -0
  56. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +940 -0
  57. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.d.ts +46 -0
  58. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.js +51 -0
  59. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.d.ts +55 -0
  60. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.js +149 -0
  61. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.d.ts +49 -0
  62. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.js +116 -0
  63. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.d.ts +26 -0
  64. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.js +276 -0
  65. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.d.ts +65 -0
  66. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.js +2 -0
  67. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.d.ts +111 -0
  68. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.js +281 -0
  69. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.d.ts +26 -0
  70. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.js +87 -0
  71. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.d.ts +79 -0
  72. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.js +165 -0
  73. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.d.ts +34 -0
  74. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.js +61 -0
  75. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.d.ts +59 -0
  76. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.js +107 -0
  77. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.d.ts +10 -0
  78. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.js +140 -0
  79. package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +57 -0
  80. package/node_modules/@openvulcan/dsh-vulcan-memory/LICENSE +21 -0
  81. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +21 -0
  82. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +31 -0
  83. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-icon.png +0 -0
  84. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/client.js +1090 -0
  85. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.d.ts +30 -0
  86. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.js +240 -0
  87. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.d.ts +30 -0
  88. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.js +46 -0
  89. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.d.ts +23 -0
  90. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.js +25 -0
  91. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.d.ts +108 -0
  92. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.js +373 -0
  93. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +14 -0
  94. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +31 -0
  95. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +196 -0
  96. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +204 -0
  97. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.d.ts +92 -0
  98. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +472 -0
  99. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.d.ts +126 -0
  100. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.js +553 -0
  101. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.d.ts +56 -0
  102. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.js +182 -0
  103. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.d.ts +13 -0
  104. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.js +24 -0
  105. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.d.ts +65 -0
  106. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.js +227 -0
  107. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.d.ts +34 -0
  108. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.js +171 -0
  109. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.d.ts +72 -0
  110. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.js +250 -0
  111. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.d.ts +6 -0
  112. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.js +12 -0
  113. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.d.ts +8 -0
  114. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.js +38 -0
  115. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.d.ts +132 -0
  116. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.js +235 -0
  117. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.d.ts +14 -0
  118. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.js +27 -0
  119. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.d.ts +29 -0
  120. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.js +62 -0
  121. package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +63 -0
  122. package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
  123. package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
  124. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1061 -0
  125. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
  126. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
  127. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
  128. package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
  129. package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
  130. package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
  131. package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
  132. package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
  133. package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
  134. package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
  135. package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
  136. package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
  137. package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
  138. package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
  139. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
  140. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
  141. package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
  142. package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
  143. package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
  144. package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
  145. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
  146. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
  147. package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
  148. package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
  149. package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
  150. package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
  151. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
  152. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
  153. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
  154. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
  155. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
  156. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
  157. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
  158. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
  159. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
  160. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
  161. package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
  162. package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
  163. package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
  164. package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
  165. package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
  166. package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
  167. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
  168. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
  169. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
  170. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
  171. package/package.json +53 -4
@@ -0,0 +1,132 @@
1
+ /**
2
+ * What the memory page asks of the Host (`markdown_memory_services.rs`): the overview of one host
3
+ * project, writes as the user, whole-file editing, registry changes, settings, and export/import.
4
+ * Host projects are dsh workspaces; export packages travel through the browser as base64, since the
5
+ * page may run on another machine than the Host.
6
+ */
7
+ import type { Workspace } from '@deepseek-ai/dsh-workspace';
8
+ import { type ImportPreview, type ImportReport } from './markdown/package.js';
9
+ import { type MemoryScope } from './markdown/scope.js';
10
+ import type { MarkdownMemoryStore } from './markdown/store.js';
11
+ import { saveSettings, type InjectionBudget, type MemorySettings } from './settings.js';
12
+ /** A dsh workspace as the memory page sees it. */
13
+ export interface HostProject {
14
+ /** Host project id derived from the root, the same id sessions in it use. */
15
+ readonly id: string;
16
+ readonly path: string;
17
+ readonly title: string;
18
+ }
19
+ export interface MemorySettingsView extends MemorySettings {
20
+ readonly defaultBudget: InjectionBudget;
21
+ readonly maxPartTokens: number;
22
+ readonly warningPercent: number;
23
+ }
24
+ export interface NodeViewModel {
25
+ readonly id: string;
26
+ readonly level: number;
27
+ readonly path: readonly string[];
28
+ readonly section: string | null;
29
+ readonly revision: string;
30
+ readonly body: string;
31
+ readonly created: string | null;
32
+ readonly updated: string | null;
33
+ readonly session: string | null;
34
+ /** `ai` or `user`; `null` for nodes never written through memory. */
35
+ readonly source: string | null;
36
+ readonly descendants: number;
37
+ }
38
+ export interface ProjectView {
39
+ readonly id: string;
40
+ readonly name: string;
41
+ readonly hostProjectIds: readonly string[];
42
+ readonly paths: readonly string[];
43
+ readonly memoryEnabled: boolean;
44
+ /** No recorded path exists any more. */
45
+ readonly orphaned: boolean;
46
+ readonly updated: string;
47
+ }
48
+ export interface MemoryOverview {
49
+ readonly settings: MemorySettingsView;
50
+ readonly user: readonly NodeViewModel[];
51
+ readonly space: readonly NodeViewModel[];
52
+ readonly project: readonly NodeViewModel[];
53
+ /** Memory project shown in the project tab. */
54
+ readonly memoryProjectId: string | null;
55
+ /** Memory project bound to the host project. */
56
+ readonly boundMemoryProjectId: string | null;
57
+ readonly hostProjectId: string | null;
58
+ /** Orphans suggested for linking; empty once bound or dismissed. */
59
+ readonly orphanCandidates: readonly {
60
+ readonly memoryProjectId: string;
61
+ readonly reasons: readonly string[];
62
+ }[];
63
+ readonly projects: readonly ProjectView[];
64
+ readonly usage: readonly {
65
+ readonly part: string;
66
+ readonly tokens: number;
67
+ readonly budget: number;
68
+ readonly omitted: readonly string[];
69
+ }[];
70
+ readonly presetSections: readonly {
71
+ readonly scope: MemoryScope;
72
+ readonly key: string;
73
+ readonly title: string;
74
+ }[];
75
+ /** Every dsh workspace, for the project selector; VulcanCode's page reads them from its own store. */
76
+ readonly hostProjects: readonly HostProject[];
77
+ }
78
+ /** One registry change requested by the page. */
79
+ export type ProjectAction = {
80
+ readonly action: 'link';
81
+ readonly projectId: string;
82
+ readonly memoryProjectId: string;
83
+ } | {
84
+ readonly action: 'create';
85
+ readonly projectId: string;
86
+ } | {
87
+ readonly action: 'ignore';
88
+ readonly projectId: string;
89
+ } | {
90
+ readonly action: 'set_enabled';
91
+ readonly memoryProjectId: string;
92
+ readonly enabled: boolean;
93
+ } | {
94
+ readonly action: 'rename';
95
+ readonly memoryProjectId: string;
96
+ readonly name: string;
97
+ };
98
+ export declare function hostProjectOf(workspace: Pick<Workspace, 'path' | 'title'>): HostProject;
99
+ /** The host project of a page request; an unknown id is refused, as VulcanCode refuses it. */
100
+ export declare function resolveHost(hosts: readonly HostProject[], projectId: string | undefined): HostProject | undefined;
101
+ export declare function settingsView(settings: MemorySettings): MemorySettingsView;
102
+ export declare function loadOverview(store: MarkdownMemoryStore, hosts: readonly HostProject[], host: HostProject | undefined, memoryProjectId: string | undefined): MemoryOverview;
103
+ /** Write as the user; the first project write may create and bind a memory project. Disabled projects stay editable. */
104
+ export declare function writeAsUser(store: MarkdownMemoryStore, hosts: readonly HostProject[], host: HostProject | undefined, memoryProjectId: string | undefined, operations: unknown): MemoryOverview;
105
+ export declare function applyProjectAction(store: MarkdownMemoryStore, host: HostProject | undefined, action: ProjectAction): ProjectView[];
106
+ /** The host project a registry action names; only link, create and ignore name one. */
107
+ export declare function actionHostId(action: ProjectAction): string | undefined;
108
+ export declare function readFile(store: MarkdownMemoryStore, scope: MemoryScope, memoryProjectId: string | undefined): {
109
+ path: string;
110
+ text: string;
111
+ };
112
+ export declare function replaceFile(store: MarkdownMemoryStore, scope: MemoryScope, memoryProjectId: string | undefined, expected: string, text: string): {
113
+ path: string;
114
+ text: string;
115
+ };
116
+ /** An object holding only the listed fields. */
117
+ export declare function fields(value: unknown, allowed: readonly string[]): Record<string, unknown>;
118
+ export declare function text(input: Record<string, unknown>, field: string): string;
119
+ export declare function optionalText(input: Record<string, unknown>, field: string): string | undefined;
120
+ export declare function scopeParam(value: unknown): MemoryScope;
121
+ export declare function parseProjectAction(value: unknown): ProjectAction;
122
+ export declare function parseSettingsSave(value: unknown): MemorySettings;
123
+ /** Archive bytes sent by the page as base64. */
124
+ export declare function archiveBytes(input: Record<string, unknown>): Uint8Array;
125
+ export declare function exportRequest(store: MarkdownMemoryStore, value: unknown): {
126
+ fileName: string;
127
+ archive: string;
128
+ sha256: string;
129
+ };
130
+ export declare function previewRequest(store: MarkdownMemoryStore, value: unknown): ImportPreview;
131
+ export declare function importRequest(store: MarkdownMemoryStore, value: unknown): ImportReport;
132
+ export { saveSettings };
@@ -0,0 +1,235 @@
1
+ /**
2
+ * What the memory page asks of the Host (`markdown_memory_services.rs`): the overview of one host
3
+ * project, writes as the user, whole-file editing, registry changes, settings, and export/import.
4
+ * Host projects are dsh workspaces; export packages travel through the browser as base64, since the
5
+ * page may run on another machine than the Host.
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { MAX_PART_TOKENS, BUDGET_WARNING_PERCENT } from './markdown/constants.js';
9
+ import { MemoryError } from './markdown/errors.js';
10
+ import { parseWriteOperation, writeMemory } from './markdown/operations.js';
11
+ import { applyImport, exportPackage, parseMappings, parseResolutions, previewImport, } from './markdown/package.js';
12
+ import { isRecord } from './markdown/registry.js';
13
+ import { SCOPES, isScope, presetSections } from './markdown/scope.js';
14
+ import { buildInjectionSnapshot } from './markdown/snapshot.js';
15
+ import { nowUtcMinute } from './markdown/timestamp.js';
16
+ import { hostProjectId, normalizeRoot } from './project.js';
17
+ import { DEFAULT_BUDGET, loadSettings, saveSettings, validateSettings } from './settings.js';
18
+ export function hostProjectOf(workspace) {
19
+ const root = normalizeRoot(workspace.path);
20
+ return { id: hostProjectId(root), path: root, title: workspace.title };
21
+ }
22
+ /** The host project of a page request; an unknown id is refused, as VulcanCode refuses it. */
23
+ export function resolveHost(hosts, projectId) {
24
+ const id = projectId?.trim();
25
+ if (id === undefined || id === '')
26
+ return undefined;
27
+ const host = hosts.find(item => item.id === id);
28
+ if (host === undefined)
29
+ throw MemoryError.invalid(`Unknown project: ${id}`);
30
+ return host;
31
+ }
32
+ export function settingsView(settings) {
33
+ return { ...settings, defaultBudget: DEFAULT_BUDGET, maxPartTokens: MAX_PART_TOKENS, warningPercent: BUDGET_WARNING_PERCENT };
34
+ }
35
+ export function loadOverview(store, hosts, host, memoryProjectId) {
36
+ const settings = loadSettings(store.memoryDirectory);
37
+ const registry = store.registry();
38
+ const bound = host === undefined ? undefined : registry.projectForHost(host.id)?.id;
39
+ const shownId = memoryProjectId ?? bound;
40
+ let documents = { user: store.load('user', undefined), space: store.load('space', undefined) };
41
+ if (shownId !== undefined) {
42
+ const project = registry.project(shownId);
43
+ if (project === undefined)
44
+ throw new MemoryError('project_unavailable', `Memory project \`${shownId}\` does not exist.`);
45
+ documents = { ...documents, project: { project, document: store.load('project', project.id) } };
46
+ }
47
+ const orphans = host === undefined ? [] : registry.orphanCandidates(host.id, host.path, undefined, existsSync);
48
+ return {
49
+ settings: settingsView(settings),
50
+ user: nodeViews(documents.user.views('user')),
51
+ space: nodeViews(documents.space.views('space')),
52
+ project: documents.project === undefined ? [] : nodeViews(documents.project.document.views('project')),
53
+ memoryProjectId: shownId ?? null,
54
+ boundMemoryProjectId: bound ?? null,
55
+ hostProjectId: host?.id ?? null,
56
+ orphanCandidates: orphans.map(candidate => ({ memoryProjectId: candidate.project.id, reasons: candidate.reasons })),
57
+ projects: registry.projects.map(projectView),
58
+ usage: buildInjectionSnapshot(documents, settings.budget).usage.map(usage => ({ ...usage })),
59
+ presetSections: SCOPES.flatMap(scope => presetSections(scope).map(([key, title]) => ({ scope, key, title }))),
60
+ hostProjects: hosts,
61
+ };
62
+ }
63
+ /** Write as the user; the first project write may create and bind a memory project. Disabled projects stay editable. */
64
+ export function writeAsUser(store, hosts, host, memoryProjectId, operations) {
65
+ if (!Array.isArray(operations))
66
+ throw MemoryError.invalid('`operations` must be an array.');
67
+ const parsed = operations.map((operation, index) => {
68
+ try {
69
+ return parseWriteOperation(operation);
70
+ }
71
+ catch (error) {
72
+ throw error instanceof MemoryError ? MemoryError.invalid(`Operation ${index} is invalid: ${error.message}`) : error;
73
+ }
74
+ });
75
+ const context = {
76
+ ...host === undefined ? {} : { hostProjectId: host.id, rootPath: host.path },
77
+ ...memoryProjectId === undefined ? {} : { memoryProjectId },
78
+ };
79
+ const receipt = writeMemory(store, context, { session: undefined, source: 'user' }, loadSettings(store.memoryDirectory).budget, parsed);
80
+ return loadOverview(store, hosts, host, memoryProjectId ?? receipt.createdProject);
81
+ }
82
+ export function applyProjectAction(store, host, action) {
83
+ const now = nowUtcMinute();
84
+ const hostPath = () => {
85
+ if (host === undefined)
86
+ throw new MemoryError('project_unavailable', 'The host project is unknown.');
87
+ return host.path;
88
+ };
89
+ return store.updateRegistry((registry) => {
90
+ switch (action.action) {
91
+ case 'link':
92
+ registry.link(action.memoryProjectId, action.projectId, hostPath(), now);
93
+ break;
94
+ case 'create':
95
+ registry.createForHost(action.projectId, hostPath(), undefined, now);
96
+ break;
97
+ case 'ignore':
98
+ registry.ignoreHost(action.projectId);
99
+ break;
100
+ case 'set_enabled':
101
+ registry.setEnabled(action.memoryProjectId, action.enabled, now);
102
+ break;
103
+ case 'rename':
104
+ registry.rename(action.memoryProjectId, action.name, now);
105
+ break;
106
+ }
107
+ return registry.projects.map(projectView);
108
+ });
109
+ }
110
+ /** The host project a registry action names; only link, create and ignore name one. */
111
+ export function actionHostId(action) {
112
+ return action.action === 'link' || action.action === 'create' || action.action === 'ignore' ? action.projectId : undefined;
113
+ }
114
+ export function readFile(store, scope, memoryProjectId) {
115
+ return { path: store.scopePath(scope, memoryProjectId), text: store.readFileText(scope, memoryProjectId) };
116
+ }
117
+ export function replaceFile(store, scope, memoryProjectId, expected, text) {
118
+ store.replaceFileText(scope, memoryProjectId, expected, text);
119
+ return { path: store.scopePath(scope, memoryProjectId), text };
120
+ }
121
+ function projectView(project) {
122
+ return {
123
+ id: project.id,
124
+ name: project.name,
125
+ hostProjectIds: [...project.hostProjectIds],
126
+ paths: [...project.paths],
127
+ memoryEnabled: project.memoryEnabled,
128
+ orphaned: project.paths.length > 0 && !project.paths.some(existsSync),
129
+ updated: project.updated,
130
+ };
131
+ }
132
+ function nodeViews(views) {
133
+ return views.map(view => ({
134
+ id: view.id,
135
+ level: view.level,
136
+ path: view.path,
137
+ section: view.section ?? null,
138
+ revision: view.revision,
139
+ body: view.body,
140
+ created: view.meta?.created ?? null,
141
+ updated: view.meta?.updated ?? null,
142
+ session: view.meta?.session ?? null,
143
+ source: view.meta?.source ?? null,
144
+ descendants: view.descendants,
145
+ }));
146
+ }
147
+ // ---- Request parsing: unknown fields are refused, as serde's `deny_unknown_fields` does. ----
148
+ /** An object holding only the listed fields. */
149
+ export function fields(value, allowed) {
150
+ if (!isRecord(value))
151
+ throw MemoryError.invalid('The request body must be a JSON object.');
152
+ const unknown = Object.keys(value).find(key => !allowed.includes(key));
153
+ if (unknown !== undefined)
154
+ throw MemoryError.invalid(`Unknown field \`${unknown}\`.`);
155
+ return value;
156
+ }
157
+ export function text(input, field) {
158
+ const value = input[field];
159
+ if (typeof value !== 'string')
160
+ throw MemoryError.invalid(`\`${field}\` must be a string.`);
161
+ return value;
162
+ }
163
+ export function optionalText(input, field) {
164
+ const value = input[field];
165
+ if (value === undefined || value === null)
166
+ return undefined;
167
+ if (typeof value !== 'string')
168
+ throw MemoryError.invalid(`\`${field}\` must be a string.`);
169
+ return value;
170
+ }
171
+ function flag(input, field) {
172
+ const value = input[field];
173
+ if (typeof value !== 'boolean')
174
+ throw MemoryError.invalid(`\`${field}\` must be true or false.`);
175
+ return value;
176
+ }
177
+ export function scopeParam(value) {
178
+ if (!isScope(value))
179
+ throw MemoryError.invalid(`\`${String(value)}\` is not a memory scope.`);
180
+ return value;
181
+ }
182
+ export function parseProjectAction(value) {
183
+ if (!isRecord(value))
184
+ throw MemoryError.invalid('The request body must be a JSON object.');
185
+ switch (value['action']) {
186
+ case 'link': {
187
+ const input = fields(value, ['action', 'projectId', 'memoryProjectId']);
188
+ return { action: 'link', projectId: text(input, 'projectId'), memoryProjectId: text(input, 'memoryProjectId') };
189
+ }
190
+ case 'create':
191
+ case 'ignore': {
192
+ const input = fields(value, ['action', 'projectId']);
193
+ return { action: value['action'], projectId: text(input, 'projectId') };
194
+ }
195
+ case 'set_enabled': {
196
+ const input = fields(value, ['action', 'memoryProjectId', 'enabled']);
197
+ return { action: 'set_enabled', memoryProjectId: text(input, 'memoryProjectId'), enabled: flag(input, 'enabled') };
198
+ }
199
+ case 'rename': {
200
+ const input = fields(value, ['action', 'memoryProjectId', 'name']);
201
+ return { action: 'rename', memoryProjectId: text(input, 'memoryProjectId'), name: text(input, 'name') };
202
+ }
203
+ default: throw MemoryError.invalid('`action` must be link, create, ignore, set_enabled or rename.');
204
+ }
205
+ }
206
+ export function parseSettingsSave(value) {
207
+ const input = fields(value, ['mode', 'budget']);
208
+ const settings = { mode: input['mode'], budget: input['budget'] };
209
+ validateSettings(settings);
210
+ return settings;
211
+ }
212
+ /** Archive bytes sent by the page as base64. */
213
+ export function archiveBytes(input) {
214
+ const encoded = text(input, 'archive');
215
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(encoded))
216
+ throw MemoryError.invalid('`archive` must be base64.');
217
+ return new Uint8Array(Buffer.from(encoded, 'base64'));
218
+ }
219
+ export function exportRequest(store, value) {
220
+ const input = fields(value, ['user', 'space', 'memoryProjectIds']);
221
+ const ids = input['memoryProjectIds'] ?? [];
222
+ if (!Array.isArray(ids) || !ids.every(id => typeof id === 'string'))
223
+ throw MemoryError.invalid('`memoryProjectIds` must be an array of strings.');
224
+ const exported = exportPackage(store, { user: flag(input, 'user'), space: flag(input, 'space'), memoryProjectIds: ids });
225
+ return { fileName: exported.fileName, archive: Buffer.from(exported.bytes).toString('base64'), sha256: exported.sha256 };
226
+ }
227
+ export function previewRequest(store, value) {
228
+ const input = fields(value, ['archive', 'name', 'projects']);
229
+ return previewImport(store, archiveBytes(input), text(input, 'name'), parseMappings(input['projects']));
230
+ }
231
+ export function importRequest(store, value) {
232
+ const input = fields(value, ['archive', 'name', 'expectedSha256', 'projects', 'resolutions']);
233
+ return applyImport(store, archiveBytes(input), text(input, 'name'), text(input, 'expectedSha256'), parseMappings(input['projects']), parseResolutions(input['resolutions']));
234
+ }
235
+ export { saveSettings };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The project a dsh session belongs to, for memory. A session is in the project of the directory
3
+ * it was created in (`SessionHeader.cwd`); the host project id is derived from that path, so
4
+ * every session opened in the same directory shares one project memory, and a moved checkout is
5
+ * offered for linking by the registry's orphan candidates.
6
+ */
7
+ import type { ProjectContext } from './markdown/store.js';
8
+ export declare const HOST_PROJECT_PREFIX = "dshproject-";
9
+ /** The absolute directory without a trailing separator. */
10
+ export declare function normalizeRoot(directory: string): string;
11
+ /** Stable host project id of a root; Windows paths are case-insensitive, so they hash lowercased. */
12
+ export declare function hostProjectId(root: string, platform?: NodeJS.Platform): string;
13
+ /** The memory project context of a session created in `cwd`; none when the session has no directory. */
14
+ export declare function projectContext(cwd: string | undefined): ProjectContext;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The project a dsh session belongs to, for memory. A session is in the project of the directory
3
+ * it was created in (`SessionHeader.cwd`); the host project id is derived from that path, so
4
+ * every session opened in the same directory shares one project memory, and a moved checkout is
5
+ * offered for linking by the registry's orphan candidates.
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import path from 'node:path';
9
+ export const HOST_PROJECT_PREFIX = 'dshproject-';
10
+ /** The absolute directory without a trailing separator. */
11
+ export function normalizeRoot(directory) {
12
+ const resolved = path.resolve(directory);
13
+ const root = path.parse(resolved).root;
14
+ return resolved.length > root.length ? resolved.replace(/[/\\]+$/, '') : resolved;
15
+ }
16
+ /** Stable host project id of a root; Windows paths are case-insensitive, so they hash lowercased. */
17
+ export function hostProjectId(root, platform = process.platform) {
18
+ const key = platform === 'win32' ? root.toLowerCase() : root;
19
+ return `${HOST_PROJECT_PREFIX}${createHash('sha256').update(key).digest('hex').slice(0, 16)}`;
20
+ }
21
+ /** The memory project context of a session created in `cwd`; none when the session has no directory. */
22
+ export function projectContext(cwd) {
23
+ if (cwd === undefined || cwd.trim() === '')
24
+ return {};
25
+ const rootPath = normalizeRoot(cwd);
26
+ return { hostProjectId: hostProjectId(rootPath), rootPath };
27
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `<dshHome>/vulcan-memory/settings.json`: the memory mode and the Markdown injection budget, the
3
+ * same shape as VulcanCode's `settings.rs`. A missing file means Markdown mode with the default
4
+ * budget and is never written back; only an explicit save writes it.
5
+ */
6
+ /** `vmm` is a recognised mode with no implementation yet: nothing is registered or injected in it. */
7
+ export type MemoryMode = 'off' | 'markdown' | 'vmm';
8
+ export interface InjectionBudget {
9
+ readonly user: number;
10
+ readonly spaceBase: number;
11
+ readonly projectBase: number;
12
+ readonly spaceCatalog: number;
13
+ readonly projectCatalog: number;
14
+ }
15
+ export interface MemorySettings {
16
+ readonly mode: MemoryMode;
17
+ readonly budget: InjectionBudget;
18
+ }
19
+ export declare const DEFAULT_BUDGET: InjectionBudget;
20
+ export declare const DEFAULT_SETTINGS: MemorySettings;
21
+ export declare const SETTINGS_FILE = "settings.json";
22
+ export declare function loadSettings(memoryDirectory: string): MemorySettings;
23
+ export declare function saveSettings(memoryDirectory: string, settings: MemorySettings): void;
24
+ export declare function parseSettings(text: string, file: string): MemorySettings;
25
+ /** Check the shape and that every budget part is a whole number of at most {@link MAX_PART_TOKENS}. */
26
+ export declare function validateSettings(settings: {
27
+ mode: unknown;
28
+ budget: unknown;
29
+ }, file?: string): asserts settings is MemorySettings;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `<dshHome>/vulcan-memory/settings.json`: the memory mode and the Markdown injection budget, the
3
+ * same shape as VulcanCode's `settings.rs`. A missing file means Markdown mode with the default
4
+ * budget and is never written back; only an explicit save writes it.
5
+ */
6
+ import { readFileSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { MAX_PART_TOKENS, SETTINGS_FORMAT_VERSION } from './markdown/constants.js';
9
+ import { MemoryError } from './markdown/errors.js';
10
+ import { isRecord } from './markdown/registry.js';
11
+ import { writeAtomically } from './markdown/store.js';
12
+ export const DEFAULT_BUDGET = { user: 500, spaceBase: 400, projectBase: 600, spaceCatalog: 1_000, projectCatalog: 1_500 };
13
+ export const DEFAULT_SETTINGS = { mode: 'markdown', budget: DEFAULT_BUDGET };
14
+ export const SETTINGS_FILE = 'settings.json';
15
+ const BUDGET_KEYS = ['user', 'spaceBase', 'projectBase', 'spaceCatalog', 'projectCatalog'];
16
+ export function loadSettings(memoryDirectory) {
17
+ const file = path.join(memoryDirectory, SETTINGS_FILE);
18
+ let text;
19
+ try {
20
+ text = readFileSync(file, 'utf8');
21
+ }
22
+ catch (error) {
23
+ if (error.code === 'ENOENT')
24
+ return DEFAULT_SETTINGS;
25
+ throw MemoryError.io(file, 'read', error);
26
+ }
27
+ return parseSettings(text, file);
28
+ }
29
+ export function saveSettings(memoryDirectory, settings) {
30
+ validateSettings(settings);
31
+ writeAtomically(path.join(memoryDirectory, SETTINGS_FILE), `${JSON.stringify({ version: SETTINGS_FORMAT_VERSION, ...settings }, null, 2)}\n`);
32
+ }
33
+ export function parseSettings(text, file) {
34
+ let value;
35
+ try {
36
+ value = JSON.parse(text);
37
+ }
38
+ catch (error) {
39
+ throw MemoryError.json(file, error instanceof Error ? error.message : String(error));
40
+ }
41
+ if (!isRecord(value) || value['version'] !== SETTINGS_FORMAT_VERSION) {
42
+ throw new MemoryError('unsupported_format', `Memory settings \`${file}\` must declare format version ${SETTINGS_FORMAT_VERSION}.`);
43
+ }
44
+ if (Object.keys(value).some(key => !['version', 'mode', 'budget'].includes(key)))
45
+ throw MemoryError.json(file, 'unknown settings field');
46
+ const settings = { mode: value['mode'], budget: value['budget'] };
47
+ validateSettings(settings, file);
48
+ return settings;
49
+ }
50
+ /** Check the shape and that every budget part is a whole number of at most {@link MAX_PART_TOKENS}. */
51
+ export function validateSettings(settings, file = SETTINGS_FILE) {
52
+ if (settings.mode !== 'off' && settings.mode !== 'markdown' && settings.mode !== 'vmm')
53
+ throw MemoryError.json(file, '`mode` must be off, markdown or vmm');
54
+ const budget = settings.budget;
55
+ if (!isRecord(budget) || Object.keys(budget).length !== BUDGET_KEYS.length
56
+ || !BUDGET_KEYS.every(key => Number.isSafeInteger(budget[key]) && budget[key] >= 0)) {
57
+ throw MemoryError.json(file, '`budget` must hold whole token counts for user, spaceBase, projectBase, spaceCatalog and projectCatalog');
58
+ }
59
+ if (BUDGET_KEYS.some(key => budget[key] > MAX_PART_TOKENS)) {
60
+ throw MemoryError.invalid(`Each memory injection budget part must be at most ${MAX_PART_TOKENS} tokens.`);
61
+ }
62
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@openvulcan/dsh-vulcan-memory",
3
+ "version": "0.4.0",
4
+ "description": "Long-term Markdown memory for DeepSeek Harness, ported from VulcanCode: memory tools for the model, a per-conversation memory snapshot, the memory-curator skill and a memory page with import and export.",
5
+ "license": "MIT",
6
+ "icon": "./assets/memory-icon.png",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/OpenVulcan/dsh-vulcancode.git",
10
+ "directory": "packages/dsh-vulcan-memory"
11
+ },
12
+ "type": "module",
13
+ "main": "lib/index.js",
14
+ "types": "lib/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./lib/index.d.ts",
18
+ "default": "./lib/index.js"
19
+ },
20
+ "./client": "./lib/client.js",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "lib",
25
+ "!lib/**/*.test.*",
26
+ "assets"
27
+ ],
28
+ "engines": {
29
+ "dsh": ">=0.2.1-rc.2 <0.3.0"
30
+ },
31
+ "dsh": {
32
+ "client": {
33
+ "platform": "web",
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-locale",
36
+ "@deepseek-ai/dsh-client-ui-layout",
37
+ "@deepseek-ai/dsh-client-ui-sidebar"
38
+ ]
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "fflate": "^0.8.3",
43
+ "mdast-util-from-markdown": "^2.0.2",
44
+ "zod": "4.4.3"
45
+ },
46
+ "peerDependencies": {
47
+ "@deepseek-ai/cordis": "^4.0.5",
48
+ "@deepseek-ai/dsh-agent": "^0.2.1-rc.2",
49
+ "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.2",
50
+ "@deepseek-ai/dsh-compaction": "^0.2.1-rc.2",
51
+ "@deepseek-ai/dsh-home-paths": "^0.2.1-rc.2",
52
+ "@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
53
+ "@deepseek-ai/dsh-plan-mode": "^0.2.1-rc.2",
54
+ "@deepseek-ai/dsh-session": "^0.2.1-rc.2",
55
+ "@deepseek-ai/dsh-session-projection": "^0.2.1-rc.2",
56
+ "@deepseek-ai/dsh-skill": "^0.2.1-rc.2",
57
+ "@deepseek-ai/dsh-tools": "^0.2.1-rc.2",
58
+ "@deepseek-ai/dsh-workspace": "^0.2.1-rc.2"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ }
63
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenVulcan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.