@openvulcan/dsh-vulcancode 0.4.0 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -38
- package/cordis.patch.yml +4 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +133 -38
- package/node_modules/@openvulcan/dsh-decision-guard/package.json +2 -2
- package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +55 -47
- package/node_modules/@openvulcan/dsh-vulcan-account/package.json +2 -2
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +30 -30
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.d.ts +18 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.js +62 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +14 -5
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +7 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +11 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +22 -3
- package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +67 -3
- package/node_modules/@openvulcan/dsh-vulcan-style/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/client.js +1567 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.d.ts +39 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.js +187 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.d.ts +58 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.js +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.d.ts +49 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.js +204 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/package.json +46 -0
- package/node_modules/@openvulcan/openvulcan-account/package.json +1 -1
- package/node_modules/@openvulcan/vulcan-decision/package.json +2 -2
- package/package.json +14 -10
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React links each committed fiber to its work-in-progress alternate and the root's active tree.
|
|
3
|
+
* React 将已提交的 Fiber 与工作中的副本关联,并由根节点指向当前生效的树。
|
|
4
|
+
*/
|
|
5
|
+
export interface Fiber {
|
|
6
|
+
readonly tag: number;
|
|
7
|
+
readonly memoizedProps: unknown;
|
|
8
|
+
readonly return: Fiber | null;
|
|
9
|
+
readonly child: Fiber | null;
|
|
10
|
+
readonly sibling: Fiber | null;
|
|
11
|
+
readonly alternate: Fiber | null;
|
|
12
|
+
readonly stateNode: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Given a DOM-owned fiber, return its committed path from the element to the root, or undefined if detached.
|
|
16
|
+
* 给定 DOM 节点持有的 Fiber,返回它从元素到根节点的已提交路径,节点脱离时返回 undefined。
|
|
17
|
+
*/
|
|
18
|
+
export declare function committedFiberPath(fiber: Fiber): readonly Fiber[] | undefined;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// React's HostRoot tag identifies the owner of the committed tree.
|
|
2
|
+
// React 的 HostRoot 标记标识已提交树的所有者。
|
|
3
|
+
const HOST_ROOT = 3;
|
|
4
|
+
/**
|
|
5
|
+
* Given a DOM-owned fiber, return its committed path from the element to the root, or undefined if detached.
|
|
6
|
+
* 给定 DOM 节点持有的 Fiber,返回它从元素到根节点的已提交路径,节点脱离时返回 undefined。
|
|
7
|
+
*/
|
|
8
|
+
export function committedFiberPath(fiber) {
|
|
9
|
+
// A bailout can share children across alternates, leaving their return pointers on the previous tree.
|
|
10
|
+
// React 跳过重渲染时,两个副本可能共用子节点,子节点的父指针仍指向上一轮的树。
|
|
11
|
+
const candidates = new Set();
|
|
12
|
+
// Follow both copies upward so the later search only visits possible ancestors of this element.
|
|
13
|
+
// 沿两个副本向上收集节点,让后续搜索只访问该元素可能的祖先。
|
|
14
|
+
const pending = [fiber];
|
|
15
|
+
// The root state is shared across alternates and identifies the currently committed root.
|
|
16
|
+
// 两个根副本共用根状态,其中记录当前已提交的根节点。
|
|
17
|
+
let root;
|
|
18
|
+
for (const candidate of pending) {
|
|
19
|
+
if (candidates.has(candidate))
|
|
20
|
+
continue;
|
|
21
|
+
candidates.add(candidate);
|
|
22
|
+
if (candidate.tag === HOST_ROOT) {
|
|
23
|
+
// Require the root's current pointer to identify one of its own fiber copies.
|
|
24
|
+
// 根状态的 current 必须指向该根节点的两个副本之一。
|
|
25
|
+
const state = candidate.stateNode;
|
|
26
|
+
if (typeof state === 'object' && state !== null && 'current' in state) {
|
|
27
|
+
if (state.current === candidate)
|
|
28
|
+
root = candidate;
|
|
29
|
+
else if (candidate.alternate !== null && state.current === candidate.alternate)
|
|
30
|
+
root = candidate.alternate;
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (candidate.return !== null)
|
|
35
|
+
pending.push(candidate.return);
|
|
36
|
+
if (candidate.alternate !== null)
|
|
37
|
+
pending.push(candidate.alternate);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Return the path from the requested fiber to this committed ancestor, if this branch owns it.
|
|
41
|
+
* 若此分支包含目标 Fiber,返回目标 Fiber 到当前已提交祖先的路径。
|
|
42
|
+
*/
|
|
43
|
+
function findPath(parent) {
|
|
44
|
+
if (parent === fiber || parent === fiber.alternate)
|
|
45
|
+
return [parent];
|
|
46
|
+
// Only the committed parent's child list establishes membership in the current tree.
|
|
47
|
+
// 只有已提交父节点的子节点列表能确定节点是否属于当前树。
|
|
48
|
+
for (let child = parent.child; child !== null; child = child.sibling) {
|
|
49
|
+
if (!candidates.has(child))
|
|
50
|
+
continue;
|
|
51
|
+
// A candidate reached from the root is committed. Verify that its branch reaches the element.
|
|
52
|
+
// 从根节点走到的候选节点已提交,再确认该分支能到达目标元素。
|
|
53
|
+
const path = findPath(child);
|
|
54
|
+
if (path !== undefined) {
|
|
55
|
+
path.push(parent);
|
|
56
|
+
return path;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return root === undefined ? undefined : findPath(root);
|
|
62
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openvulcan/dsh-vulcan-answers",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Interactive answers for DeepSeek Harness: the model answers with VulcanCode ui-* components, rendered in the chat.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"icon": "./assets/answers-icon.png",
|
|
@@ -303,6 +303,12 @@ window.__ModuleLoader__.load({
|
|
|
303
303
|
locale: i18n_1.NAMESPACE,
|
|
304
304
|
inject: () => ({ view }),
|
|
305
305
|
}, ({ t, view: injected }) => (0, jsx_runtime_1.jsx)(settings_1.SettingsPage, { view: injected, t: t })));
|
|
306
|
+
// dsh 0.2.2 declares this optional slot. The injection waits on older hosts.
|
|
307
|
+
// dsh 0.2.2 声明了此可选插槽,旧版宿主上的注入会保持等待。
|
|
308
|
+
ctx.slots.inject('settings.section.icon', () => ctx.slots.register({
|
|
309
|
+
name: 'settings.section.icon',
|
|
310
|
+
id: shared_1.SETTINGS_SECTION,
|
|
311
|
+
}, LuaSkillsIcon));
|
|
306
312
|
ctx.slots.inject('main', () => ctx.slots.register({
|
|
307
313
|
name: 'main',
|
|
308
314
|
key: shared_1.PANEL_ID,
|
|
@@ -316,10 +322,13 @@ window.__ModuleLoader__.load({
|
|
|
316
322
|
order: 20,
|
|
317
323
|
locale: i18n_1.NAMESPACE,
|
|
318
324
|
label: () => ctx.locale.bind(i18n_1.NAMESPACE)('panel'),
|
|
319
|
-
},
|
|
325
|
+
}, LuaSkillsIcon));
|
|
320
326
|
}
|
|
321
|
-
/**
|
|
322
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Render the LuaSkills moon and brackets at the size requested by either navigation slot.
|
|
329
|
+
* 按设置或侧栏导航插槽要求的尺寸渲染 LuaSkills 的月亮与括号图标。
|
|
330
|
+
*/
|
|
331
|
+
function LuaSkillsIcon({ size }) {
|
|
323
332
|
return ((0, jsx_runtime_1.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "8.5" }), (0, jsx_runtime_1.jsx)("circle", { cx: "16.2", cy: "7.8", r: "2.1" }), (0, jsx_runtime_1.jsx)("path", { d: "M9.5 9.5 7 12l2.5 2.5M14.5 14.5 17 12" })] }));
|
|
324
333
|
}
|
|
325
334
|
|
|
@@ -363,7 +372,7 @@ window.__ModuleLoader__.load({
|
|
|
363
372
|
});
|
|
364
373
|
return () => { current = false; };
|
|
365
374
|
}, [live?.state.kind, generation]);
|
|
366
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "vac vls-panel", children: (0, jsx_runtime_1.jsxs)("div", { className: "vac vac-page vls-panel-inner", children: [(0, jsx_runtime_1.jsxs)("
|
|
375
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "vac vls-panel", children: (0, jsx_runtime_1.jsxs)("div", { className: "vac vac-page vls-panel-inner", "data-vulcan-settings-width": "full", children: [(0, jsx_runtime_1.jsxs)("header", { className: "vac-page-head", "data-vulcan-settings-header": true, children: [(0, jsx_runtime_1.jsx)("h2", { children: t('panel') }), (0, jsx_runtime_1.jsx)("p", { className: "vac-lead", children: t('page.lead') })] }), live === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('loading') }) : null, live === null ? (0, jsx_runtime_1.jsx)("div", { className: "vac-alert bad", children: t('unavailable') }) : null, live != null && !ready ? ((0, jsx_runtime_1.jsxs)("div", { className: "vls-banner warn", children: [(0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: live.state.kind === 'installing' || live.state.kind === 'starting' ? t(`runtime.${live.state.kind === 'starting' ? 'starting' : `phase.${live.state.phase}`}`) : t('page.needRuntime') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "vac-btn primary", onClick: openSettings, children: t('page.openSettings') })] })) : null, (0, jsx_runtime_1.jsx)("div", { className: "vls-tabs", role: "tablist", children: ['installed', 'hub'].map(name => ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === name, className: tab === name ? 'vls-tab on' : 'vls-tab', onClick: () => { setTab(name); }, children: t(`tab.${name}`) }, name))) }), (0, jsx_runtime_1.jsx)("div", { hidden: tab !== 'installed', children: (0, jsx_runtime_1.jsx)(Installed, { skills: skills, error: skillsError, ready: ready, reload: reload, t: t }) }), (0, jsx_runtime_1.jsx)("div", { hidden: tab !== 'hub', children: (0, jsx_runtime_1.jsx)(Hub, { live: live ?? null, ready: ready, installed: skills ?? [], reload: reload, view: view, t: t }) })] }) }));
|
|
367
376
|
}
|
|
368
377
|
function Installed({ skills, error, ready, reload, t }) {
|
|
369
378
|
const [busy, setBusy] = (0, react_1.useState)(undefined);
|
|
@@ -594,7 +603,7 @@ window.__ModuleLoader__.load({
|
|
|
594
603
|
const shared_1 = require("./shared");
|
|
595
604
|
function SettingsPage({ view, t }) {
|
|
596
605
|
const live = (0, runtime_1.useRuntime)(view);
|
|
597
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "vac vac-page", children: [(0, jsx_runtime_1.jsxs)("
|
|
606
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "vac vac-page", "data-vulcan-settings-width": "full", children: [(0, jsx_runtime_1.jsxs)("header", { className: "vac-page-head", "data-vulcan-settings-header": true, children: [(0, jsx_runtime_1.jsx)("h2", { children: t('settingsPage.title') }), (0, jsx_runtime_1.jsx)("p", { className: "vac-lead", children: t('settingsPage.lead') })] }), live === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "vac-hint", children: t('loading') })
|
|
598
607
|
: live === null ? (0, jsx_runtime_1.jsx)("div", { className: "vac-alert bad", children: t('unavailable') })
|
|
599
608
|
: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(runtime_1.RuntimeSection, { view: view, live: live, t: t }), (0, jsx_runtime_1.jsx)(SettingsSection, { view: view, live: live, t: t })] }))] }));
|
|
600
609
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openvulcan/dsh-vulcan-luaskills",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "LuaSkills for DeepSeek Harness: skill tools for the model, the codekit default skill, VM settings and the LuaSkills Hub. The runtime is installed on demand.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"icon": "./assets/luaskills-icon.svg",
|
|
@@ -14,6 +14,7 @@ Where it belongs:
|
|
|
14
14
|
|
|
15
15
|
How to write:
|
|
16
16
|
- Search first. Update or append to an existing node instead of adding a near duplicate, and merge duplicates that a receipt reports.
|
|
17
|
+
- Batch operations run in order and commit all-or-nothing. For dependent edits to the same node, use separate calls and read its revision after each successful write.
|
|
17
18
|
- Make each title state the fact itself ("Verify with targeted tests only"), not a topic ("Testing"), so catalog lines carry meaning.
|
|
18
19
|
- One fact per node; keep bodies short and put details in child nodes (down to H4).
|
|
19
20
|
- Correct or delete nodes that turn out to be wrong or outdated.
|
|
@@ -11,7 +11,7 @@ Long-term memory is three Markdown files of heading nodes: `user` (who the user
|
|
|
11
11
|
|
|
12
12
|
1. Run `memory_outline` for all scopes. Note the budget lines from the last write receipt if you have one.
|
|
13
13
|
2. Read candidate nodes with `memory_search` (by `ids`, or by patterns for suspected duplicates) before changing anything. Every change needs the node's current `expectedRevision`.
|
|
14
|
-
3.
|
|
14
|
+
3. Batch independent changes in `memory_write`. One call applies up to 16 operations in order and commits all-or-nothing. For dependent edits to the same node, use separate calls and read its revision after each successful write.
|
|
15
15
|
4. Report what changed: merged, moved, renamed, deleted, and anything you left for the user to decide.
|
|
16
16
|
|
|
17
17
|
## What to fix
|
|
@@ -6,6 +6,13 @@ export declare class MemoryError extends Error {
|
|
|
6
6
|
static invalid(message: string): MemoryError;
|
|
7
7
|
static notFound(id: string): MemoryError;
|
|
8
8
|
static revisionMismatch(id: string, expected: string, current: string): MemoryError;
|
|
9
|
+
/**
|
|
10
|
+
* Report a node's expected and staged revisions and its revision before the batch, if present.
|
|
11
|
+
* 报告节点的预期修订、暂存修订,以及该节点在批次前已有的修订。
|
|
12
|
+
* @returns A revision conflict that retains the existing tool and HTTP error kind.
|
|
13
|
+
* 返回沿用工具与 HTTP 错误类型的修订冲突。
|
|
14
|
+
*/
|
|
15
|
+
static stagedRevisionMismatch(id: string, expected: string, staged: string, committed: string | undefined): MemoryError;
|
|
9
16
|
static concurrent(path: string): MemoryError;
|
|
10
17
|
static io(path: string, action: string, cause: unknown): MemoryError;
|
|
11
18
|
static json(path: string, message: string): MemoryError;
|
|
@@ -15,6 +15,17 @@ export class MemoryError extends Error {
|
|
|
15
15
|
static revisionMismatch(id, expected, current) {
|
|
16
16
|
return new MemoryError('revision_mismatch', `Memory node \`${id}\` changed (expected revision ${expected}, current ${current}); read it again before writing.`);
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Report a node's expected and staged revisions and its revision before the batch, if present.
|
|
20
|
+
* 报告节点的预期修订、暂存修订,以及该节点在批次前已有的修订。
|
|
21
|
+
* @returns A revision conflict that retains the existing tool and HTTP error kind.
|
|
22
|
+
* 返回沿用工具与 HTTP 错误类型的修订冲突。
|
|
23
|
+
*/
|
|
24
|
+
static stagedRevisionMismatch(id, expected, staged, committed) {
|
|
25
|
+
return new MemoryError('revision_mismatch', `Memory node \`${id}\` has staged revision ${staged} in this batch (expected ${expected}). ` +
|
|
26
|
+
`Committed revision before the batch: ${committed ?? 'none for this node ID'}. ` +
|
|
27
|
+
'Submit dependent edits in separate calls and read the node after each successful write.');
|
|
28
|
+
}
|
|
18
29
|
static concurrent(path) {
|
|
19
30
|
return new MemoryError('concurrent_modification', `Memory file \`${path}\` changed on disk while the write was prepared; retry the write.`);
|
|
20
31
|
}
|
|
@@ -125,7 +125,7 @@ export declare const TOOL_SPECS: {
|
|
|
125
125
|
};
|
|
126
126
|
readonly write: {
|
|
127
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.";
|
|
128
|
+
readonly description: "Change long-term memory with 1 to 16 operations applied in order and committed 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. For dependent edits to the same node, use separate calls and read its revision after each successful write. 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
129
|
readonly parameters: {
|
|
130
130
|
readonly type: "object";
|
|
131
131
|
readonly additionalProperties: false;
|
|
@@ -89,7 +89,7 @@ export const TOOL_SPECS = {
|
|
|
89
89
|
},
|
|
90
90
|
write: {
|
|
91
91
|
name: 'memory_write',
|
|
92
|
-
description: `Change long-term memory with 1 to ${MAX_OPERATIONS_PER_WRITE} 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.`,
|
|
92
|
+
description: `Change long-term memory with 1 to ${MAX_OPERATIONS_PER_WRITE} operations applied in order and committed 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. For dependent edits to the same node, use separate calls and read its revision after each successful write. 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.`,
|
|
93
93
|
parameters: {
|
|
94
94
|
type: 'object',
|
|
95
95
|
additionalProperties: false,
|
|
@@ -181,14 +181,33 @@ class WriteSession {
|
|
|
181
181
|
const scopes = this.projectId === undefined ? ['user', 'space'] : ['user', 'space', 'project'];
|
|
182
182
|
return scopes.flatMap(scope => this.scope(scope).document.views(scope));
|
|
183
183
|
}
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Find the staged node by id and compare its revision with the caller's token.
|
|
186
|
+
* 按标识查找批内暂存节点,并将其修订与调用方的令牌比较。
|
|
187
|
+
*/
|
|
185
188
|
locate(id, expectedRevision) {
|
|
186
189
|
const view = this.allViews().find(candidate => candidate.id === id);
|
|
187
190
|
if (view === undefined)
|
|
188
191
|
throw MemoryError.notFound(id);
|
|
189
|
-
if (view.revision
|
|
192
|
+
if (view.revision === expectedRevision.trim())
|
|
193
|
+
return view;
|
|
194
|
+
// A rejected batch discards its staged documents. Search the original files in scope order,
|
|
195
|
+
// since a staged move can put the node in a different scope from its committed version.
|
|
196
|
+
// 被拒绝的批次会丢弃暂存文档。按层顺序查找原始文件,因为批内移动可能改变节点所在的层。
|
|
197
|
+
let committed;
|
|
198
|
+
for (const scope of SCOPES) {
|
|
199
|
+
const original = this.scopes.get(scope)?.original;
|
|
200
|
+
if (original === undefined)
|
|
201
|
+
continue;
|
|
202
|
+
const node = MemoryDocument.parse(original).views(scope).find(candidate => candidate.id === id);
|
|
203
|
+
if (node === undefined)
|
|
204
|
+
continue;
|
|
205
|
+
committed = node.revision;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if (committed === view.revision)
|
|
190
209
|
throw MemoryError.revisionMismatch(id, expectedRevision, view.revision);
|
|
191
|
-
|
|
210
|
+
throw MemoryError.stagedRevisionMismatch(id, expectedRevision, view.revision, committed);
|
|
192
211
|
}
|
|
193
212
|
apply(operation) {
|
|
194
213
|
switch (operation.action) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openvulcan/dsh-vulcan-memory",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"icon": "./assets/memory-icon.png",
|
|
@@ -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.
|