@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,276 @@
1
+ /**
2
+ * LuaSkills in dsh. The runtime is not part of this package: the user installs it from the
3
+ * LuaSkills settings page, and from then on every enabled skill entry is a dsh tool, every
4
+ * loaded skill is in dsh's skill catalog, and the page manages installed skills and the Hub.
5
+ * The pages talk to the exact `/api/vulcan-luaskills/*` routes inside dsh's authenticated
6
+ * connection.
7
+ */
8
+ import path from 'node:path';
9
+ import { getDshRuntimeVersion } from '@deepseek-ai/dsh-app-boot';
10
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
11
+ import { READ_ONLY_ENTRIES } from './constants.js';
12
+ import { HubError, hubSkill, searchHub } from './hub.js';
13
+ import { LuaSkillsRuntime, RuntimeError } from './runtime.js';
14
+ import { SettingsError } from './settings.js';
15
+ import { listInstalled } from './skills.js';
16
+ import { helpTool, skillTool, toolName } from './tools.js';
17
+ export const name = 'vulcan-luaskills';
18
+ export const inject = ['skills', 'tools', 'workingDirectory'];
19
+ /** Prefix of every page route, below dsh's `/api`. Also restated in `client/shared.ts`. */
20
+ export const ROUTE_PREFIX = '/api/vulcan-luaskills/';
21
+ /** Skill catalog name of the help tool, as VulcanCode names it. */
22
+ const HELP_TOOL = 'luaskill_help';
23
+ /** Between user skills (400) and bundled ones (600): a user's own skill of the same name wins. */
24
+ const SKILL_RANK = 500;
25
+ const PROVIDER = 'vulcan-luaskills';
26
+ /** dsh skill names are kebab-case. */
27
+ const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
28
+ /** Hub skill ids are flat. */
29
+ const HUB_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
30
+ export function apply(ctx) {
31
+ const dshVersion = getDshRuntimeVersion();
32
+ const runtime = new LuaSkillsRuntime(path.join(resolveDshHome(), 'vulcan-luaskills'), () => { onChange(); });
33
+ let disposers = [];
34
+ /** dsh tool name → LuaSkills canonical name, for entries that may change files. */
35
+ const writeTools = new Map();
36
+ /** Canonical name → dsh tool name, for the skill guides. */
37
+ let toolNames = new Map();
38
+ let catalog = [];
39
+ let skillControl;
40
+ let generation = 0;
41
+ let lastKind = runtime.current.kind;
42
+ const onChange = () => {
43
+ const kind = runtime.current.kind;
44
+ if ((kind === 'ready') !== (lastKind === 'ready'))
45
+ void refresh();
46
+ lastKind = kind;
47
+ };
48
+ /** Register the tools and catalog of the running engine, replacing the previous set whole. */
49
+ const refresh = async () => {
50
+ const mine = ++generation;
51
+ let entries = [];
52
+ let help = [];
53
+ if (runtime.current.kind === 'ready') {
54
+ try {
55
+ const engine = runtime.engine();
56
+ [entries, help] = await Promise.all([engine.entries(), engine.help()]);
57
+ }
58
+ catch (error) {
59
+ ctx.logger.warn(`cannot list LuaSkills entries: ${messageOf(error)}`);
60
+ }
61
+ }
62
+ if (mine !== generation)
63
+ return;
64
+ for (const dispose of disposers)
65
+ dispose();
66
+ disposers = [];
67
+ writeTools.clear();
68
+ toolNames = new Map();
69
+ catalog = help;
70
+ if (entries.length > 0 || help.length > 0) {
71
+ const ours = new Set();
72
+ const taken = (candidate) => ours.has(candidate) || ctx.tools.get(candidate) !== undefined;
73
+ const register = (definition) => {
74
+ try {
75
+ disposers.push(ctx.tools.register(definition));
76
+ ours.add(definition.name);
77
+ return true;
78
+ }
79
+ catch (error) {
80
+ ctx.logger.warn(`cannot register the LuaSkills tool ${definition.name}: ${messageOf(error)}`);
81
+ return false;
82
+ }
83
+ };
84
+ register(helpTool(toolName(HELP_TOOL, taken), (skillId, flow) => renderHelp(skillId, flow)));
85
+ for (const entry of entries) {
86
+ const tool = toolName(entry.canonical_name, taken);
87
+ if (taken(tool)) {
88
+ ctx.logger.warn(`the LuaSkills entry ${entry.canonical_name} has no free tool name`);
89
+ continue;
90
+ }
91
+ if (!register(skillTool(tool, entry, toolHost)))
92
+ continue;
93
+ toolNames.set(entry.canonical_name, tool);
94
+ if (!READ_ONLY_ENTRIES.has(entry.canonical_name))
95
+ writeTools.set(tool, entry.canonical_name);
96
+ }
97
+ }
98
+ skillControl?.invalidate();
99
+ };
100
+ const toolHost = {
101
+ dshVersion,
102
+ workingDirectory: async (exec) => {
103
+ if (exec.agent === undefined)
104
+ throw new Error('this LuaSkills tool needs a session');
105
+ return await ctx.workingDirectory.ensure(exec.agent, exec.signal);
106
+ },
107
+ call: (canonical, args, context) => runtime.engine().call(canonical, args, context),
108
+ };
109
+ const renderHelp = async (skillId, flow) => {
110
+ const detail = await runtime.engine().helpDetail(skillId, flow);
111
+ if (detail === null)
112
+ throw new Error(flow === undefined ? `no LuaSkills skill ${skillId}` : `the skill ${skillId} has no topic ${flow}`);
113
+ return detail.content;
114
+ };
115
+ /** A skill's guide for dsh's catalog: its main help, its topics, and its tools' dsh names. */
116
+ const renderGuide = async (skillId) => {
117
+ const help = catalog.find(item => item.skill_id === skillId);
118
+ const parts = [await renderHelp(skillId, undefined)];
119
+ const helpName = ctx.tools.get(HELP_TOOL) !== undefined ? HELP_TOOL : `ls_${HELP_TOOL}`;
120
+ const topics = help?.flows.filter(flow => !flow.is_main) ?? [];
121
+ if (topics.length > 0) {
122
+ parts.push(`## Topics\n\nRead one with \`${helpName}\` (\`skill_id: "${skillId}"\`, \`flow\`):\n\n${topics.map(flow => `- \`${flow.flow_name}\`: ${flow.description}`).join('\n')}`);
123
+ }
124
+ const tools = [...toolNames].filter(([canonical]) => canonical.startsWith(`${skillId}-`));
125
+ if (tools.length > 0) {
126
+ parts.push(`## Tools\n\n${tools.map(([canonical, tool]) => `- \`${tool}\` (LuaSkills entry \`${canonical}\`)`).join('\n')}`);
127
+ }
128
+ return parts.join('\n\n');
129
+ };
130
+ ctx.skills.registerProvider((control) => {
131
+ skillControl = control;
132
+ const provider = {
133
+ name: PROVIDER,
134
+ list: () => Promise.resolve(catalog.filter(help => SKILL_NAME.test(help.skill_id)).map((help) => ({
135
+ name: help.skill_id,
136
+ description: help.main.description,
137
+ invocation: { modelInvocable: true, userInvocable: true },
138
+ provider: PROVIDER,
139
+ source: 'luaskills',
140
+ rank: SKILL_RANK,
141
+ locator: help.skill_id,
142
+ }))),
143
+ async get(candidate) {
144
+ const { rank: _rank, locator, ...summary } = candidate;
145
+ return { ...summary, content: await renderGuide(locator) };
146
+ },
147
+ };
148
+ return provider;
149
+ });
150
+ // Entries that may change files follow the session's sandbox: refused when read-only, run
151
+ // under full access, asked about otherwise.
152
+ ctx.on('tools/pre-execute', async (exec, next) => {
153
+ const canonical = writeTools.get(exec.name);
154
+ if (canonical === undefined)
155
+ return next();
156
+ const session = exec.agent?.session;
157
+ const mode = ctx.get('sandboxPolicy')?.resolve(session === undefined ? {} : { session }).mode;
158
+ if (mode === 'read-only') {
159
+ return { kind: 'deny', reason: `The LuaSkills tool ${exec.name} may change files, and this session's sandbox is read-only.` };
160
+ }
161
+ if (mode === 'danger-full-access')
162
+ return next();
163
+ const downstream = await next();
164
+ if (downstream.kind !== 'allow')
165
+ return downstream;
166
+ return {
167
+ kind: 'ask',
168
+ reason: `The LuaSkills entry ${canonical} may change files.`,
169
+ displayReason: { en: `LuaSkills: ${canonical} may change files.`, zh: `LuaSkills:${canonical} 可能修改文件。` },
170
+ };
171
+ });
172
+ const skillRows = async () => {
173
+ const installed = listInstalled(runtime.runtimeRoot);
174
+ const loaded = new Set(catalog.map(help => help.skill_id));
175
+ const tools = [...toolNames];
176
+ return installed.map(skill => ({
177
+ ...skill,
178
+ loaded: loaded.has(skill.skillId),
179
+ tools: tools.filter(([canonical]) => canonical.startsWith(`${skill.skillId}-`)).map(([, tool]) => tool),
180
+ }));
181
+ };
182
+ const findSkill = (url) => {
183
+ const id = url.searchParams.get('id') ?? '';
184
+ const skill = listInstalled(runtime.runtimeRoot).find(item => item.skillId === id);
185
+ if (skill === undefined)
186
+ throw new RuntimeError(`no installed skill ${id}`, 'invalid_request');
187
+ return skill;
188
+ };
189
+ /** Run one skill change, then re-read the tools and catalog. */
190
+ const mutate = async (work) => {
191
+ try {
192
+ return await work();
193
+ }
194
+ finally {
195
+ await refresh();
196
+ }
197
+ };
198
+ const hubBase = () => runtime.settings.network.officialSkillHubBaseUrl;
199
+ const routes = [
200
+ ['GET', 'state', async () => runtime.view()],
201
+ ['POST', 'install', async () => { runtime.install(); }],
202
+ ['POST', 'install/cancel', async () => { runtime.cancel(); }],
203
+ ['POST', 'remove', () => runtime.remove()],
204
+ ['POST', 'restart', () => runtime.restart()],
205
+ ['POST', 'settings', async (request) => runtime.saveSettings(await readJson(request))],
206
+ ['GET', 'skills', () => skillRows()],
207
+ ['POST', 'skills/enabled', async (_request, url) => {
208
+ const skill = findSkill(url);
209
+ const enabled = url.searchParams.get('enabled');
210
+ if (enabled !== 'true' && enabled !== 'false')
211
+ throw new RuntimeError('enabled must be true or false', 'invalid_request');
212
+ return await mutate(() => runtime.engine().setEnabled(skill.skillId, enabled === 'true'));
213
+ }],
214
+ ['POST', 'skills/update', async (_request, url) => {
215
+ const skill = findSkill(url);
216
+ if (skill.source === undefined)
217
+ throw new RuntimeError(`${skill.skillId} was not installed from a source it can be updated from`, 'invalid_request');
218
+ const source = skill.source;
219
+ return await mutate(() => runtime.engine().update(skill.root, { skill_id: skill.skillId, source: source.locator, source_type: source.type }));
220
+ }],
221
+ ['POST', 'skills/uninstall', async (_request, url) => {
222
+ const skill = findSkill(url);
223
+ if (skill.root !== 'USER')
224
+ throw new RuntimeError(`${skill.skillId} is a default skill; it can be disabled, not removed`, 'invalid_request');
225
+ return await mutate(() => runtime.engine().uninstall('USER', skill.skillId));
226
+ }],
227
+ ['POST', 'skills/install', async (_request, url) => {
228
+ const id = url.searchParams.get('id') ?? '';
229
+ if (!HUB_ID.test(id))
230
+ throw new RuntimeError('id must be a LuaSkills Hub skill id', 'invalid_request');
231
+ return await mutate(() => runtime.engine().install('USER', { skill_id: null, source: id, source_type: 'official_hub' }));
232
+ }],
233
+ ['GET', 'hub/search', async (_request, url) => searchHub(hubBase(), url.searchParams.get('q') ?? '', url.searchParams.get('cursor'), url.searchParams.get('lang'))],
234
+ ['GET', 'hub/skill', async (_request, url) => hubSkill(hubBase(), url.searchParams.get('id') ?? '', url.searchParams.get('lang'))],
235
+ ];
236
+ ctx.inject(['connection'], (child) => {
237
+ for (const [method, route, handler] of routes) {
238
+ child.effect(() => child.connection.fetch.register({
239
+ path: `${ROUTE_PREFIX}${route}`,
240
+ methods: [method],
241
+ requestBody: 'buffered',
242
+ fetch: request => handle(handler, request),
243
+ }));
244
+ }
245
+ });
246
+ ctx.effect(() => {
247
+ runtime.boot().catch((error) => { ctx.logger.warn(`cannot start LuaSkills: ${messageOf(error)}`); });
248
+ return () => {
249
+ generation++;
250
+ void runtime.close();
251
+ };
252
+ }, 'vulcan-luaskills: runtime');
253
+ }
254
+ async function readJson(request) {
255
+ try {
256
+ return await request.json();
257
+ }
258
+ catch {
259
+ throw new RuntimeError('the request body must be JSON', 'invalid_request');
260
+ }
261
+ }
262
+ /** Answer one page request; failures carry their code, never a stack. */
263
+ export async function handle(handler, request) {
264
+ try {
265
+ const value = await handler(request, new URL(request.url));
266
+ return Response.json({ ok: true, value: value ?? null }, { headers: { 'cache-control': 'no-store' } });
267
+ }
268
+ catch (error) {
269
+ const code = error instanceof RuntimeError || error instanceof SettingsError || error instanceof HubError ? error.code : 'failed';
270
+ const status = code === 'invalid_request' || code === 'invalid_settings' ? 400 : code === 'busy' ? 409 : code === 'not_ready' ? 503 : 500;
271
+ return Response.json({ ok: false, error: { code, message: messageOf(error) } }, { status, headers: { 'cache-control': 'no-store' } });
272
+ }
273
+ }
274
+ function messageOf(error) {
275
+ return error instanceof Error ? error.message : String(error);
276
+ }
@@ -0,0 +1,65 @@
1
+ /** Messages between the Host plugin and its LuaSkills worker process. */
2
+ export interface WorkerRequest {
3
+ readonly id: number;
4
+ readonly method: string;
5
+ readonly params: unknown;
6
+ }
7
+ export type WorkerReply = {
8
+ readonly id: number;
9
+ readonly ok: true;
10
+ readonly value: unknown;
11
+ } | {
12
+ readonly id: number;
13
+ readonly ok: false;
14
+ readonly error: string;
15
+ };
16
+ export type WorkerEvent = {
17
+ readonly event: 'ready';
18
+ }
19
+ /** Bytes of one runtime archive so far; `total` is null when the server sent no length. */
20
+ | {
21
+ readonly event: 'download';
22
+ readonly role: string;
23
+ readonly received: number;
24
+ readonly total: number | null;
25
+ };
26
+ /** One skill entry as the SDK lists it; only the fields read here. */
27
+ export interface EntryDescriptor {
28
+ readonly canonical_name: string;
29
+ readonly skill_id: string;
30
+ readonly local_name: string;
31
+ readonly root_name: string;
32
+ readonly description: string;
33
+ readonly input_schema: unknown;
34
+ }
35
+ /** One help node of a skill. */
36
+ export interface HelpNode {
37
+ readonly flow_name: string;
38
+ readonly description: string;
39
+ readonly related_entries: readonly string[];
40
+ readonly is_main: boolean;
41
+ }
42
+ /** One loaded skill's help, as the SDK lists it. */
43
+ export interface SkillHelp {
44
+ readonly skill_id: string;
45
+ readonly skill_name: string;
46
+ readonly skill_version: string;
47
+ readonly root_name: string;
48
+ readonly main: HelpNode;
49
+ readonly flows: readonly HelpNode[];
50
+ }
51
+ export interface HelpDetail extends HelpNode {
52
+ readonly skill_id: string;
53
+ readonly content: string;
54
+ }
55
+ export interface InvocationResult {
56
+ readonly content: string;
57
+ readonly overflow_mode?: string | null;
58
+ }
59
+ /** Outcome of an install, update or uninstall. */
60
+ export interface ApplyResult {
61
+ readonly skill_id: string;
62
+ readonly status: string;
63
+ readonly message: string;
64
+ readonly version?: string | null;
65
+ }
@@ -0,0 +1,2 @@
1
+ /** Messages between the Host plugin and its LuaSkills worker process. */
2
+ export {};
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The LuaSkills runtime's lifecycle: absent until the user installs it, then installed in five
3
+ * steps with progress, started on every dsh start while its versions match, restarted when
4
+ * settings change, and removable. A failed or cancelled install leaves nothing half-installed.
5
+ *
6
+ * Layout below the plugin's folder in the dsh home:
7
+ * - `sdk/node_modules/` — the SDK and Koffi;
8
+ * - `runtime/` — the SDK's runtime root (core library, Lua packages, skill layers, state);
9
+ * - `installed.json` — written last; without it the runtime counts as not installed;
10
+ * - `settings.json` — the user's settings, kept when the runtime is removed.
11
+ */
12
+ import { HUB_SITES } from './constants.js';
13
+ import { LuaSkillsWorker } from './engine.js';
14
+ import { type LuaSkillsSettings } from './settings.js';
15
+ export interface Versions {
16
+ readonly sdk: string;
17
+ readonly luaskills: string;
18
+ readonly luaRuntime: string;
19
+ }
20
+ /** The versions this plugin installs. */
21
+ export declare const PINNED: Versions;
22
+ /** Install steps, in the order the SDK fetches the runtime archives. */
23
+ export declare const PHASES: readonly ["sdk", "lua", "core", "engine", "skill"];
24
+ export type Phase = (typeof PHASES)[number];
25
+ export type RuntimeState = {
26
+ readonly kind: 'absent';
27
+ } | {
28
+ readonly kind: 'installing';
29
+ readonly phase: Phase;
30
+ readonly received: number;
31
+ readonly total: number | null;
32
+ } | {
33
+ readonly kind: 'starting';
34
+ } | {
35
+ readonly kind: 'ready';
36
+ }
37
+ /** Installed by another plugin version; it is not started until reinstalled. */
38
+ | {
39
+ readonly kind: 'outdated';
40
+ readonly installed: Versions;
41
+ }
42
+ /** `installed` says whether the runtime is on disk (the engine failed) or not (the install failed). */
43
+ | {
44
+ readonly kind: 'failed';
45
+ readonly error: string;
46
+ readonly installed: boolean;
47
+ };
48
+ /** Everything the pages show about the runtime, in one answer. */
49
+ export interface RuntimeView {
50
+ readonly state: RuntimeState;
51
+ readonly location: string;
52
+ readonly sizeMb: number;
53
+ readonly versions: Versions;
54
+ readonly settings: LuaSkillsSettings;
55
+ /** `defaults` until the user saves once. */
56
+ readonly settingsSource: 'defaults' | 'saved' | 'invalid';
57
+ readonly settingsError?: string;
58
+ readonly hubSites: typeof HUB_SITES;
59
+ }
60
+ export declare class RuntimeError extends Error {
61
+ readonly code: 'not_ready' | 'busy' | 'invalid_request';
62
+ constructor(message: string, code?: 'not_ready' | 'busy' | 'invalid_request');
63
+ }
64
+ export declare class LuaSkillsRuntime {
65
+ readonly home: string;
66
+ private readonly changed;
67
+ readonly runtimeRoot: string;
68
+ private readonly nodeModules;
69
+ private readonly marker;
70
+ private readonly settingsFile;
71
+ private readonly scratch;
72
+ private state;
73
+ private worker;
74
+ private loaded;
75
+ private installing;
76
+ private closed;
77
+ /**
78
+ * @param home - this plugin's folder in the dsh home.
79
+ * @param changed - called after every state change.
80
+ */
81
+ constructor(home: string, changed: () => void);
82
+ get current(): RuntimeState;
83
+ view(): RuntimeView;
84
+ get settings(): LuaSkillsSettings;
85
+ /** The running engine. @throws RuntimeError when it is not ready. */
86
+ engine(): LuaSkillsWorker;
87
+ /** Start the installed runtime, if there is one this version can use. */
88
+ boot(): Promise<void>;
89
+ /** Begin installing in the background; progress shows in the state. */
90
+ install(): void;
91
+ /** Stop the running install; what it wrote is removed. */
92
+ cancel(): void;
93
+ /** Stop the engine and delete the runtime; settings are kept. */
94
+ remove(): Promise<void>;
95
+ /** Restart the engine with the current settings. */
96
+ restart(): Promise<void>;
97
+ /**
98
+ * Check and save the settings, then restart a running engine so they apply.
99
+ * @throws SettingsError when the value is not valid; nothing is written then.
100
+ */
101
+ saveSettings(value: unknown): Promise<void>;
102
+ close(): Promise<void>;
103
+ private set;
104
+ private sdkEntry;
105
+ private readMarker;
106
+ private spawn;
107
+ private stopWorker;
108
+ private start;
109
+ private wipe;
110
+ private runInstall;
111
+ }