@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.3

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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/openvulcan-icon.svg +33 -0
  4. package/cordis.patch.yml +19 -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 +2250 -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 +573 -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 +949 -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-reasoning/LICENSE +21 -0
  123. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
  124. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
  125. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
  126. package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
  127. package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
  128. package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
  129. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1121 -0
  130. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
  131. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
  132. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
  133. package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
  134. package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
  135. package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
  136. package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
  137. package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
  138. package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
  139. package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
  140. package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
  141. package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
  142. package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
  143. package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
  144. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
  145. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
  146. package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
  147. package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
  148. package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
  149. package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
  150. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
  151. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
  152. package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
  153. package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
  154. package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
  155. package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
  156. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
  157. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
  158. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
  159. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
  160. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
  161. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
  162. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
  163. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
  164. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
  165. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
  166. package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
  167. package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
  168. package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
  169. package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
  170. package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
  171. package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
  172. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
  173. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
  174. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
  175. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
  176. package/package.json +55 -4
@@ -0,0 +1,373 @@
1
+ /**
2
+ * One memory file parsed into heading nodes (`document.rs`). Parsing never fails and rendering
3
+ * reproduces the source bytes exactly; only nodes that were written regenerate their heading or
4
+ * metadata line. Headings are found with a CommonMark parser, so `#` lines inside fences, quotes
5
+ * and lists stay body text.
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { fromMarkdown } from 'mdast-util-from-markdown';
9
+ import { MAX_NODE_LEVEL } from './constants.js';
10
+ const META_PREFIX = '<!-- mem ';
11
+ const META_VALUE = /^[A-Za-z0-9\-_.:]+$/;
12
+ /** Whether a value can be stored in a metadata line and read back. */
13
+ export function isMetaValue(value) {
14
+ return META_VALUE.test(value);
15
+ }
16
+ /** Parse one metadata line; malformed lines are ordinary body text. */
17
+ export function parseMetaLine(line) {
18
+ const trimmed = line.trim();
19
+ if (!trimmed.startsWith(META_PREFIX) || !trimmed.endsWith('-->'))
20
+ return undefined;
21
+ const inner = trimmed.slice(META_PREFIX.length, trimmed.length - 3).trim();
22
+ const fields = {};
23
+ let source;
24
+ for (const pair of inner.split(/\s+/).filter(part => part !== '')) {
25
+ const equals = pair.indexOf('=');
26
+ if (equals < 0)
27
+ return undefined;
28
+ const key = pair.slice(0, equals);
29
+ const value = pair.slice(equals + 1);
30
+ if (!META_VALUE.test(value))
31
+ return undefined;
32
+ switch (key) {
33
+ case 'id':
34
+ case 'section':
35
+ case 'created':
36
+ case 'updated':
37
+ case 'session':
38
+ fields[key] = value;
39
+ break;
40
+ case 'source':
41
+ if (value !== 'ai' && value !== 'user')
42
+ return undefined;
43
+ source = value;
44
+ break;
45
+ }
46
+ }
47
+ if (fields.id === undefined || fields.created === undefined || fields.updated === undefined || source === undefined)
48
+ return undefined;
49
+ return {
50
+ id: fields.id,
51
+ ...fields.section === undefined ? {} : { section: fields.section },
52
+ created: fields.created,
53
+ updated: fields.updated,
54
+ ...fields.session === undefined ? {} : { session: fields.session },
55
+ source,
56
+ };
57
+ }
58
+ /** Render a metadata comment without a line terminator. */
59
+ export function renderMeta(meta) {
60
+ let line = `${META_PREFIX}id=${meta.id}`;
61
+ if (meta.section !== undefined)
62
+ line += ` section=${meta.section}`;
63
+ line += ` created=${meta.created} updated=${meta.updated}`;
64
+ if (meta.session !== undefined)
65
+ line += ` session=${meta.session}`;
66
+ return `${line} source=${meta.source} -->`;
67
+ }
68
+ /** One heading node with its own body and child nodes. */
69
+ export class MemoryNode {
70
+ level;
71
+ title;
72
+ meta;
73
+ body;
74
+ /** Original heading bytes including the terminator; `undefined` regenerates it. */
75
+ headingRaw;
76
+ /** Original metadata line including the terminator; `undefined` regenerates it. */
77
+ metaRaw;
78
+ children = [];
79
+ constructor(level, title, meta,
80
+ /** Own body bytes up to the next node heading. */
81
+ body, raw) {
82
+ this.level = level;
83
+ this.title = title;
84
+ this.meta = meta;
85
+ this.body = body;
86
+ this.headingRaw = raw?.heading;
87
+ this.metaRaw = raw?.meta;
88
+ }
89
+ /** Copy without children for another document: lines regenerate at `level`, the body uses `eol`. */
90
+ detached(level, eol) {
91
+ return new MemoryNode(level, this.title, this.meta, storedBody(this.bodyText(), eol));
92
+ }
93
+ rawBody() {
94
+ return this.body;
95
+ }
96
+ /** The own body with normalized line endings and no surrounding blank lines. */
97
+ bodyText() {
98
+ return normalizeNewlines(this.body).replace(/^\n+|\n+$/g, '').trimEnd();
99
+ }
100
+ setBody(body) {
101
+ this.body = body;
102
+ }
103
+ setTitle(title) {
104
+ this.title = title;
105
+ this.headingRaw = undefined;
106
+ }
107
+ setMeta(meta) {
108
+ this.meta = meta;
109
+ this.metaRaw = undefined;
110
+ }
111
+ /** Shift the subtree to a new root level; returns the deepest level reached when it does not fit. */
112
+ relevel(level) {
113
+ const deepest = level + this.subtreeDepth();
114
+ if (level === 0 || deepest > MAX_NODE_LEVEL)
115
+ return deepest;
116
+ this.shiftLevels(level - this.level);
117
+ return undefined;
118
+ }
119
+ subtreeDepth() {
120
+ return Math.max(0, ...this.children.map(child => Math.max(0, child.level - this.level) + child.subtreeDepth()));
121
+ }
122
+ shiftLevels(delta) {
123
+ if (delta !== 0) {
124
+ this.level += delta;
125
+ this.headingRaw = undefined;
126
+ }
127
+ for (const child of this.children)
128
+ child.shiftLevels(delta);
129
+ }
130
+ /** Revision token of the node's own title and body. */
131
+ revision() {
132
+ return createHash('sha256').update(this.title.trim()).update(Buffer.from([0])).update(this.bodyText()).digest('hex').slice(0, 8);
133
+ }
134
+ descendantCount() {
135
+ return this.children.reduce((count, child) => count + 1 + child.descendantCount(), 0);
136
+ }
137
+ /** Append this subtree's text, reusing original bytes wherever they are still valid. */
138
+ render(eol, output) {
139
+ ensureLineStart(output, eol);
140
+ output.push(this.headingRaw ?? `${'#'.repeat(this.level)} ${this.title}${eol}`);
141
+ if (this.meta !== undefined) {
142
+ ensureLineStart(output, eol);
143
+ output.push(this.metaRaw ?? `${renderMeta(this.meta)}${eol}`);
144
+ }
145
+ if (this.body !== '') {
146
+ ensureLineStart(output, eol);
147
+ output.push(this.body);
148
+ }
149
+ for (const child of this.children)
150
+ child.render(eol, output);
151
+ }
152
+ }
153
+ /** The last title of a view's path. */
154
+ export function viewTitle(view) {
155
+ return view.path[view.path.length - 1] ?? '';
156
+ }
157
+ /** One parsed memory file. */
158
+ export class MemoryDocument {
159
+ eol;
160
+ preamble;
161
+ roots;
162
+ constructor(
163
+ /** Line terminator for generated lines, taken from the source. */
164
+ eol,
165
+ /** Bytes before the first node heading. */
166
+ preamble, roots) {
167
+ this.eol = eol;
168
+ this.preamble = preamble;
169
+ this.roots = roots;
170
+ }
171
+ static empty() {
172
+ return new MemoryDocument('\n', '', []);
173
+ }
174
+ static parse(text) {
175
+ const eol = text.includes('\r\n') ? '\r\n' : '\n';
176
+ const headings = scanHeadings(text);
177
+ const first = headings[0];
178
+ if (first === undefined)
179
+ return new MemoryDocument(eol, text, []);
180
+ const flat = headings.map((heading, index) => {
181
+ const ownEnd = headings[index + 1]?.start ?? text.length;
182
+ const headingRaw = text.slice(heading.start, heading.after);
183
+ let bodyStart = heading.after;
184
+ let meta;
185
+ let metaRaw;
186
+ if (bodyStart < ownEnd) {
187
+ const newline = text.slice(bodyStart, ownEnd).indexOf('\n');
188
+ const lineEnd = newline < 0 ? ownEnd : bodyStart + newline + 1;
189
+ meta = parseMetaLine(text.slice(bodyStart, lineEnd));
190
+ if (meta !== undefined) {
191
+ metaRaw = text.slice(bodyStart, lineEnd);
192
+ bodyStart = lineEnd;
193
+ }
194
+ }
195
+ return new MemoryNode(heading.level, headingTitle(headingRaw, heading.level), meta, text.slice(bodyStart, ownEnd), { heading: headingRaw, meta: metaRaw });
196
+ });
197
+ return new MemoryDocument(eol, text.slice(0, first.start), buildTree(flat));
198
+ }
199
+ render() {
200
+ const output = [this.preamble];
201
+ for (const node of this.roots)
202
+ node.render(this.eol, output);
203
+ return output.join('');
204
+ }
205
+ /** Views of every node in document order. */
206
+ views(scope) {
207
+ const views = [];
208
+ const occurrences = new Map();
209
+ this.roots.forEach((node, index) => {
210
+ const section = node.level === 1 ? node.meta?.section : undefined;
211
+ collectViews(node, scope, [], [index], section, occurrences, views);
212
+ });
213
+ return views;
214
+ }
215
+ node(locator) {
216
+ const [first, ...rest] = locator;
217
+ if (first === undefined)
218
+ return undefined;
219
+ let node = this.roots[first];
220
+ for (const index of rest)
221
+ node = node?.children[index];
222
+ return node;
223
+ }
224
+ /** Remove and return the subtree at a locator. */
225
+ remove(locator) {
226
+ const last = locator[locator.length - 1];
227
+ if (last === undefined)
228
+ return undefined;
229
+ const parent = locator.slice(0, -1);
230
+ const siblings = parent.length === 0 ? this.roots : this.node(parent)?.children;
231
+ if (siblings === undefined || last >= siblings.length)
232
+ return undefined;
233
+ return siblings.splice(last, 1)[0];
234
+ }
235
+ /** Append a subtree as the last child of a parent, or as the last top-level node. */
236
+ append(parent, node) {
237
+ if (parent === undefined) {
238
+ this.roots.push(node);
239
+ return true;
240
+ }
241
+ const target = this.node(parent);
242
+ if (target === undefined)
243
+ return false;
244
+ target.children.push(node);
245
+ return true;
246
+ }
247
+ }
248
+ /** Convert written text into stored body bytes: the file's terminators and one trailing blank line. */
249
+ export function storedBody(text, eol) {
250
+ const trimmed = normalizeNewlines(text).replace(/^\n+|\n+$/g, '').trimEnd();
251
+ if (trimmed === '')
252
+ return eol;
253
+ return `${trimmed.replaceAll('\n', eol)}${eol}${eol}`;
254
+ }
255
+ /** Levels of node-forming headings in text written as a body. */
256
+ export function nodeHeadingLevels(text) {
257
+ return scanHeadings(text).map(heading => heading.level);
258
+ }
259
+ /** Whether text renders as exactly one ATX heading with the given title. */
260
+ export function titleRoundTrips(level, title) {
261
+ const line = `${'#'.repeat(level)} ${title}\n`;
262
+ const headings = scanHeadings(line);
263
+ const [only] = headings;
264
+ return headings.length === 1 && only !== undefined && only.level === level
265
+ && headingTitle(line.slice(only.start, only.after), level) === title;
266
+ }
267
+ function normalizeNewlines(text) {
268
+ return text.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
269
+ }
270
+ /** Top-level H1–H4 headings; a heading inside a quote, list or fence is not top level. */
271
+ function scanHeadings(text) {
272
+ const headings = [];
273
+ for (const child of fromMarkdown(text).children) {
274
+ if (child.type !== 'heading' || child.depth > MAX_NODE_LEVEL)
275
+ continue;
276
+ const start = child.position?.start.offset;
277
+ const end = child.position?.end.offset;
278
+ if (start === undefined || end === undefined)
279
+ continue;
280
+ const last = Math.max(end - 1, start);
281
+ const newline = text.indexOf('\n', last);
282
+ headings.push({ start, after: newline < 0 ? text.length : newline + 1, level: child.depth });
283
+ }
284
+ return headings;
285
+ }
286
+ /** Rust `str::lines`: split on `\n`, drop one trailing empty line, strip a `\r` ending each line. */
287
+ export function lines(text) {
288
+ const parts = text.split('\n');
289
+ if (parts[parts.length - 1] === '')
290
+ parts.pop();
291
+ return parts.map(line => line.endsWith('\r') ? line.slice(0, -1) : line);
292
+ }
293
+ /** The raw title text of an ATX or setext heading's source. */
294
+ function headingTitle(raw, level) {
295
+ const all = lines(raw);
296
+ const first = all[0] ?? '';
297
+ const stripped = first.replace(/^ +/, '');
298
+ const marker = '#'.repeat(level);
299
+ if (first.length - stripped.length <= 3 && stripped.startsWith(marker)) {
300
+ const rest = stripped.slice(marker.length);
301
+ if (rest === '' || rest.startsWith(' ') || rest.startsWith('\t')) {
302
+ const content = rest.trim();
303
+ const withoutClosing = content.replace(/#+$/, '');
304
+ if (withoutClosing === '')
305
+ return '';
306
+ return /[ \t]$/.test(withoutClosing) ? withoutClosing.trimEnd() : content;
307
+ }
308
+ }
309
+ return all.slice(0, Math.max(0, all.length - 1)).map(line => line.trim()).join(' ');
310
+ }
311
+ /** Nest a flat heading list into a tree by level. */
312
+ function buildTree(flat) {
313
+ const roots = [];
314
+ const stack = [];
315
+ const attach = (node) => {
316
+ const parent = stack[stack.length - 1];
317
+ if (parent === undefined)
318
+ roots.push(node);
319
+ else
320
+ parent.children.push(node);
321
+ };
322
+ for (const node of flat) {
323
+ while ((stack[stack.length - 1]?.level ?? 0) >= node.level)
324
+ attach(stack.pop());
325
+ stack.push(node);
326
+ }
327
+ while (stack.length > 0)
328
+ attach(stack.pop());
329
+ return roots;
330
+ }
331
+ function ensureLineStart(output, eol) {
332
+ const last = output.findLast(part => part !== '');
333
+ if (last !== undefined && !last.endsWith('\n'))
334
+ output.push(eol);
335
+ }
336
+ function collectViews(node, scope, path, locator, section, occurrences, views) {
337
+ path.push(node.title);
338
+ let id = node.meta?.id;
339
+ if (id === undefined) {
340
+ const key = JSON.stringify(path);
341
+ const occurrence = (occurrences.get(key) ?? 0) + 1;
342
+ occurrences.set(key, occurrence);
343
+ id = derivedNodeId(scope, path, occurrence);
344
+ }
345
+ views.push({
346
+ id,
347
+ scope,
348
+ level: node.level,
349
+ path: [...path],
350
+ locator: [...locator],
351
+ section,
352
+ revision: node.revision(),
353
+ meta: node.meta,
354
+ body: node.bodyText(),
355
+ children: node.children.map(child => child.title),
356
+ descendants: node.descendantCount(),
357
+ });
358
+ node.children.forEach((child, index) => {
359
+ locator.push(index);
360
+ collectViews(child, scope, path, locator, section, occurrences, views);
361
+ locator.pop();
362
+ });
363
+ path.pop();
364
+ }
365
+ /** Stable id of a node without metadata, from its scope, title path and occurrence. */
366
+ function derivedNodeId(scope, path, occurrence) {
367
+ const hash = createHash('sha256').update(scope);
368
+ const separator = Buffer.from([0x1f]);
369
+ for (const title of path)
370
+ hash.update(separator).update(title);
371
+ hash.update(separator).update(String(occurrence));
372
+ return `h-${hash.digest('hex').slice(0, 10)}`;
373
+ }
@@ -0,0 +1,14 @@
1
+ /** Failures of the Markdown memory store; every message is English and safe to return to the model (`error.rs`). */
2
+ export type MemoryErrorKind = 'invalid' | 'not_found' | 'revision_mismatch' | 'project_unavailable' | 'unsupported_format' | 'concurrent_modification' | 'rollback_incomplete' | 'io' | 'json';
3
+ export declare class MemoryError extends Error {
4
+ readonly kind: MemoryErrorKind;
5
+ constructor(kind: MemoryErrorKind, message: string);
6
+ static invalid(message: string): MemoryError;
7
+ static notFound(id: string): MemoryError;
8
+ static revisionMismatch(id: string, expected: string, current: string): MemoryError;
9
+ static concurrent(path: string): MemoryError;
10
+ static io(path: string, action: string, cause: unknown): MemoryError;
11
+ static json(path: string, message: string): MemoryError;
12
+ /** One operation of a multi-operation write failed; nothing was written. */
13
+ static operation(index: number, cause: MemoryError): MemoryError;
14
+ }
@@ -0,0 +1,31 @@
1
+ /** Failures of the Markdown memory store; every message is English and safe to return to the model (`error.rs`). */
2
+ export class MemoryError extends Error {
3
+ kind;
4
+ constructor(kind, message) {
5
+ super(message);
6
+ this.kind = kind;
7
+ this.name = 'MemoryError';
8
+ }
9
+ static invalid(message) {
10
+ return new MemoryError('invalid', message);
11
+ }
12
+ static notFound(id) {
13
+ return new MemoryError('not_found', `Memory node \`${id}\` was not found; run memory_outline or memory_search to get current ids.`);
14
+ }
15
+ static revisionMismatch(id, expected, current) {
16
+ return new MemoryError('revision_mismatch', `Memory node \`${id}\` changed (expected revision ${expected}, current ${current}); read it again before writing.`);
17
+ }
18
+ static concurrent(path) {
19
+ return new MemoryError('concurrent_modification', `Memory file \`${path}\` changed on disk while the write was prepared; retry the write.`);
20
+ }
21
+ static io(path, action, cause) {
22
+ return new MemoryError('io', `Memory file \`${path}\` could not be ${action}: ${cause instanceof Error ? cause.message : String(cause)}`);
23
+ }
24
+ static json(path, message) {
25
+ return new MemoryError('json', `Memory file \`${path}\` is not valid JSON: ${message}`);
26
+ }
27
+ /** One operation of a multi-operation write failed; nothing was written. */
28
+ static operation(index, cause) {
29
+ return new MemoryError(cause.kind, `Operation #${index} (counting from 1) failed, so no change was written: ${cause.message}`);
30
+ }
31
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Markdown memory as the plugin uses it: resolve whether memory is active for a session's project,
3
+ * run the three tools, and build the injection snapshot. Everything Markdown-specific stays below
4
+ * this module; the plugin entry only calls these functions.
5
+ */
6
+ import { type WriteAuthor } from './operations.js';
7
+ import { type MemoryScope } from './scope.js';
8
+ import { type OutlineRequest, type SearchRequest } from './search.js';
9
+ import { MarkdownMemoryStore, type ProjectContext } from './store.js';
10
+ import type { InjectionBudget, MemorySettings } from '../settings.js';
11
+ export { MarkdownMemoryStore } from './store.js';
12
+ export type { ProjectContext } from './store.js';
13
+ /** Memory reachable from one session: the store, its project and the budget. */
14
+ export interface MarkdownMemoryAccess {
15
+ readonly store: MarkdownMemoryStore;
16
+ readonly context: ProjectContext;
17
+ readonly budget: InjectionBudget;
18
+ }
19
+ /**
20
+ * Memory of a project, or `undefined` when the mode is not `markdown` or the user turned memory
21
+ * off for this project (which also hides the user and space scopes), as VulcanCode resolves it.
22
+ */
23
+ export declare function resolveAccess(store: MarkdownMemoryStore, settings: MemorySettings, context: ProjectContext): MarkdownMemoryAccess | undefined;
24
+ export declare const NOT_ACTIVE_MESSAGE = "Markdown memory is not active for this conversation; the memory mode changed or memory is off for this project.";
25
+ export declare function outline(access: MarkdownMemoryAccess, args: unknown): string;
26
+ export declare function search(access: MarkdownMemoryAccess, args: unknown): string;
27
+ export declare function write(access: MarkdownMemoryAccess, author: WriteAuthor, args: unknown): string;
28
+ /** The snapshot text injected into a conversation; empty when every scope is empty. */
29
+ export declare function snapshot(access: MarkdownMemoryAccess): string;
30
+ /** Model-facing names, descriptions and schemas, the same text VulcanCode registers. */
31
+ export declare const TOOL_SPECS: {
32
+ readonly outline: {
33
+ readonly name: "memory_outline";
34
+ readonly description: "List the heading tree of long-term memory: the user profile, space memory (rules for every project) and this project's memory, with node ids, revisions, update dates and body sizes. Use it to see what is remembered and where a new fact belongs before memory_write.";
35
+ readonly parameters: {
36
+ readonly type: "object";
37
+ readonly additionalProperties: false;
38
+ readonly properties: {
39
+ readonly scopes: {
40
+ readonly type: "array";
41
+ readonly uniqueItems: true;
42
+ readonly items: {
43
+ type: string;
44
+ enum: MemoryScope[];
45
+ };
46
+ readonly description: "Scopes to list; defaults to all.";
47
+ };
48
+ readonly section: {
49
+ readonly type: "string";
50
+ readonly description: "Section key or H1 title to list.";
51
+ };
52
+ readonly pathPrefix: {
53
+ readonly type: "string";
54
+ readonly description: "Title path prefix, segments separated by `/`.";
55
+ };
56
+ readonly depth: {
57
+ readonly type: "integer";
58
+ readonly minimum: 1;
59
+ readonly maximum: 4;
60
+ readonly description: "Deepest heading level listed; defaults to all.";
61
+ };
62
+ };
63
+ };
64
+ };
65
+ readonly search: {
66
+ readonly name: "memory_search";
67
+ readonly description: "Search long-term memory nodes with several patterns combined with OR (regex unless fixedStrings; case-insensitive by default), optionally limited to scopes, a section or a title path prefix; or read nodes by id. Each result is a whole node: scope, title path, id, revision, update time and body, with matching lines prefixed by `>> `. Catalog lines injected at session start are titles only: read the node before relying on it or changing it.";
68
+ readonly parameters: {
69
+ readonly type: "object";
70
+ readonly additionalProperties: false;
71
+ readonly properties: {
72
+ readonly patterns: {
73
+ readonly type: "array";
74
+ readonly maxItems: 12;
75
+ readonly items: {
76
+ readonly type: "string";
77
+ readonly minLength: 1;
78
+ };
79
+ readonly description: "Patterns combined with OR; include synonyms and other languages. Mutually exclusive with ids.";
80
+ };
81
+ readonly fixedStrings: {
82
+ readonly type: "boolean";
83
+ readonly description: "Match patterns as literal text.";
84
+ };
85
+ readonly ignoreCase: {
86
+ readonly type: "boolean";
87
+ readonly description: "Case-insensitive matching; defaults to true.";
88
+ };
89
+ readonly scopes: {
90
+ readonly type: "array";
91
+ readonly uniqueItems: true;
92
+ readonly items: {
93
+ type: string;
94
+ enum: MemoryScope[];
95
+ };
96
+ readonly description: "Scopes to search; defaults to all.";
97
+ };
98
+ readonly section: {
99
+ readonly type: "string";
100
+ readonly description: "Section key or H1 title filter.";
101
+ };
102
+ readonly pathPrefix: {
103
+ readonly type: "string";
104
+ readonly description: "Title path prefix filter, segments separated by `/`.";
105
+ };
106
+ readonly ids: {
107
+ readonly type: "array";
108
+ readonly items: {
109
+ readonly type: "string";
110
+ };
111
+ readonly description: "Node ids to read whole. Mutually exclusive with patterns.";
112
+ };
113
+ readonly limit: {
114
+ readonly type: "integer";
115
+ readonly minimum: 1;
116
+ readonly maximum: 20;
117
+ readonly description: "Nodes per page.";
118
+ };
119
+ readonly cursor: {
120
+ readonly type: "string";
121
+ readonly description: "Cursor from a previous result: the next result page, or the next body page of a single id.";
122
+ };
123
+ };
124
+ };
125
+ };
126
+ readonly write: {
127
+ readonly name: "memory_write";
128
+ readonly description: "Change long-term memory with 1 to 16 operations applied all-or-nothing: create, replace, append, rename, move (across scopes and sections too) and delete. create needs a scope and exactly one of parentId, section or newSection. Every action except create needs the node's current expectedRevision from memory_search or memory_outline. Scopes: `user` describes who the user is; `space` holds rules and environment that hold for every project; `project` holds facts, decisions and constraints of this project only. The `base` section of space and project is injected in full every session, so keep it to a few critical, stable rules and put the rest in other sections. Write when the user asks you to remember something, corrects your approach, or a decision is made that the repository does not show. Make titles state the fact itself; bodies cannot contain H1-H4 headings.";
129
+ readonly parameters: {
130
+ readonly type: "object";
131
+ readonly additionalProperties: false;
132
+ readonly properties: {
133
+ readonly operations: {
134
+ readonly type: "array";
135
+ readonly minItems: 1;
136
+ readonly maxItems: 16;
137
+ readonly items: {
138
+ readonly type: "object";
139
+ readonly additionalProperties: false;
140
+ readonly properties: {
141
+ readonly action: {
142
+ readonly type: "string";
143
+ readonly enum: readonly ["create", "replace", "append", "rename", "move", "delete"];
144
+ };
145
+ readonly id: {
146
+ readonly type: "string";
147
+ readonly description: "Node id; every action except create.";
148
+ };
149
+ readonly expectedRevision: {
150
+ readonly type: "string";
151
+ readonly description: "Revision last read; every action except create.";
152
+ };
153
+ readonly scope: {
154
+ readonly type: "string";
155
+ readonly enum: readonly MemoryScope[];
156
+ readonly description: "Destination scope; required for create, optional for move.";
157
+ };
158
+ readonly parentId: {
159
+ readonly type: "string";
160
+ readonly description: "create/move: become the last child of this node.";
161
+ };
162
+ readonly section: {
163
+ readonly type: "string";
164
+ readonly description: `create/move: become a child of this section, by key or H1 title. New files start with the preset sections: ${string}.`;
165
+ };
166
+ readonly newSection: {
167
+ readonly type: "boolean";
168
+ readonly description: "create/move: become a new H1 section. Give exactly one of parentId, section, newSection.";
169
+ };
170
+ readonly title: {
171
+ readonly type: "string";
172
+ readonly description: "create/rename: heading text.";
173
+ };
174
+ readonly body: {
175
+ readonly type: "string";
176
+ readonly description: "create/replace: Markdown body. replace keeps the title; change it with rename.";
177
+ };
178
+ readonly text: {
179
+ readonly type: "string";
180
+ readonly description: "append: Markdown appended after a blank line.";
181
+ };
182
+ readonly withChildren: {
183
+ readonly type: "boolean";
184
+ readonly description: "delete: also delete child nodes.";
185
+ };
186
+ };
187
+ readonly required: readonly ["action"];
188
+ };
189
+ };
190
+ };
191
+ readonly required: readonly ["operations"];
192
+ };
193
+ };
194
+ };
195
+ export declare function parseOutlineRequest(args: unknown): OutlineRequest;
196
+ export declare function parseSearchRequest(args: unknown): SearchRequest;