nolo-cli 0.1.8 → 0.1.10

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 (239) hide show
  1. package/README.md +32 -0
  2. package/agentRuntimeCommands.ts +3 -3
  3. package/ai/agent/_executeModel.ts +118 -0
  4. package/ai/agent/agentSlice.ts +525 -0
  5. package/ai/agent/appWorkingMemory.ts +126 -0
  6. package/ai/agent/avatarUtils.ts +24 -0
  7. package/ai/agent/buildEditingContext.ts +373 -0
  8. package/ai/agent/buildSystemPrompt.ts +532 -0
  9. package/ai/agent/cleanAgentMessages.ts +140 -0
  10. package/ai/agent/cliChatClient.ts +119 -0
  11. package/ai/agent/cliExecutor.ts +733 -0
  12. package/ai/agent/cliPrompt.ts +10 -0
  13. package/ai/agent/contextCompiler.ts +107 -0
  14. package/ai/agent/contextLayerContract.ts +44 -0
  15. package/ai/agent/createAgentSchema.ts +234 -0
  16. package/ai/agent/executeToolCall.ts +58 -0
  17. package/ai/agent/fetchAgentContexts.ts +42 -0
  18. package/ai/agent/generatePrompt.ts +3 -0
  19. package/ai/agent/getFullChatContextKeys.ts +168 -0
  20. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  21. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  22. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  23. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  24. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  25. package/ai/agent/machineRunPermissions.ts +95 -0
  26. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  27. package/ai/agent/planSlice.ts +259 -0
  28. package/ai/agent/referenceUtils.ts +229 -0
  29. package/ai/agent/runAgentBackground.ts +238 -0
  30. package/ai/agent/runAgentClientLoop.ts +138 -0
  31. package/ai/agent/runtimeGuidance.ts +97 -0
  32. package/ai/agent/runtimeServerBase.ts +37 -0
  33. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  34. package/ai/agent/startParallelAgentStreams.ts +424 -0
  35. package/ai/agent/startupProtocol.ts +53 -0
  36. package/ai/agent/streamAgentChatTurn.ts +1278 -0
  37. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  38. package/ai/agent/types.ts +71 -0
  39. package/ai/agent/utils/imageOutput.ts +33 -0
  40. package/ai/agent/utils/sortUtils.ts +250 -0
  41. package/ai/agent/web/referencePickerUtils.ts +146 -0
  42. package/ai/ai.locale.ts +1079 -0
  43. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  44. package/ai/chat/fetchUtils.native.ts +276 -0
  45. package/ai/chat/fetchUtils.ts +153 -0
  46. package/ai/chat/parseApiError.ts +64 -0
  47. package/ai/chat/parseMultilineSSE.ts +95 -0
  48. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  49. package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
  50. package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
  51. package/ai/chat/shouldUseServerProxy.ts +18 -0
  52. package/ai/chat/sseClient.native.ts +91 -0
  53. package/ai/chat/sseClient.ts +67 -0
  54. package/ai/chat/streamReader.native.ts +31 -0
  55. package/ai/chat/streamReader.ts +62 -0
  56. package/ai/chat/updateTotalUsage.ts +72 -0
  57. package/ai/context/buildReferenceContext.ts +437 -0
  58. package/ai/context/calculateContextUsage.ts +133 -0
  59. package/ai/context/retention.ts +165 -0
  60. package/ai/context/tokenUtils.ts +78 -0
  61. package/ai/index.ts +1 -0
  62. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  63. package/ai/llm/deepinfra.ts +28 -0
  64. package/ai/llm/fireworks.ts +50 -0
  65. package/ai/llm/generateRequestBody.ts +165 -0
  66. package/ai/llm/getModelContextWindow.ts +84 -0
  67. package/ai/llm/getNoloKey.ts +31 -0
  68. package/ai/llm/getPricing.ts +199 -0
  69. package/ai/llm/hooks/useModelPricing.ts +75 -0
  70. package/ai/llm/imagePricing.ts +40 -0
  71. package/ai/llm/isResponseAPIModel.ts +13 -0
  72. package/ai/llm/mimo.ts +71 -0
  73. package/ai/llm/mistral.ts +22 -0
  74. package/ai/llm/modelAvatar.ts +427 -0
  75. package/ai/llm/models.ts +45 -0
  76. package/ai/llm/openrouterModels.ts +269 -0
  77. package/ai/llm/providers.ts +306 -0
  78. package/ai/llm/reasoningModels.ts +28 -0
  79. package/ai/llm/types.ts +59 -0
  80. package/ai/llm/usageRequestOptions.ts +59 -0
  81. package/ai/memory/capture.ts +148 -0
  82. package/ai/memory/consolidate.ts +104 -0
  83. package/ai/memory/delete.ts +147 -0
  84. package/ai/memory/overlay.ts +84 -0
  85. package/ai/memory/query.ts +38 -0
  86. package/ai/memory/queryShared.ts +160 -0
  87. package/ai/memory/rank.ts +105 -0
  88. package/ai/memory/recentRelationshipRecap.ts +249 -0
  89. package/ai/memory/remember.ts +167 -0
  90. package/ai/memory/runtime.ts +76 -0
  91. package/ai/memory/store.ts +20 -0
  92. package/ai/memory/storeShared.ts +76 -0
  93. package/ai/memory/types.ts +46 -0
  94. package/ai/memory/understanding.ts +349 -0
  95. package/ai/memory/understandingGreeting.ts +264 -0
  96. package/ai/messages/type.ts +20 -0
  97. package/ai/policy/personalizationDialog.ts +333 -0
  98. package/ai/policy/runtimePolicy.ts +440 -0
  99. package/ai/policy/selfUpdateFields.ts +48 -0
  100. package/ai/policy/types.ts +64 -0
  101. package/ai/skills/referenceRuntime.ts +274 -0
  102. package/ai/skills/skillDiagnostics.ts +251 -0
  103. package/ai/skills/skillDocBuilder.ts +139 -0
  104. package/ai/skills/skillDocProtocol.ts +434 -0
  105. package/ai/skills/skillReferenceSummary.ts +63 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +544 -0
  108. package/ai/token/db.ts +98 -0
  109. package/ai/token/externalToolCost.ts +330 -0
  110. package/ai/token/hooks/useRecords.ts +65 -0
  111. package/ai/token/missingUsageEstimate.ts +42 -0
  112. package/ai/token/modelUsageQuery.ts +252 -0
  113. package/ai/token/normalizeUsage.ts +84 -0
  114. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  115. package/ai/token/prepareTokenUsageData.ts +88 -0
  116. package/ai/token/query.ts +88 -0
  117. package/ai/token/queryUserTokens.ts +59 -0
  118. package/ai/token/resolveBillingTarget.ts +52 -0
  119. package/ai/token/saveTokenRecord.ts +53 -0
  120. package/ai/token/serverDialogProjection.ts +78 -0
  121. package/ai/token/serverTokenWriter.ts +143 -0
  122. package/ai/token/stats.ts +21 -0
  123. package/ai/token/tokenThunks.ts +24 -0
  124. package/ai/token/types.ts +93 -0
  125. package/ai/tools/agent/agentTools.ts +176 -0
  126. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  127. package/ai/tools/agent/callAgentTool.ts +139 -0
  128. package/ai/tools/agent/createAgentTool.ts +512 -0
  129. package/ai/tools/agent/createDialogTool.ts +69 -0
  130. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  131. package/ai/tools/agent/parallelBudget.ts +221 -0
  132. package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
  133. package/ai/tools/agent/runLlmTool.ts +96 -0
  134. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  135. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  136. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  137. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  138. package/ai/tools/agent/updateAgentTool.ts +96 -0
  139. package/ai/tools/agent/updateSelfTool.ts +113 -0
  140. package/ai/tools/amazonProductScraperTool.ts +86 -0
  141. package/ai/tools/apifyActorClient.ts +45 -0
  142. package/ai/tools/appEditGuard.ts +372 -0
  143. package/ai/tools/appReadSnapshot.ts +153 -0
  144. package/ai/tools/appTools.ts +1549 -0
  145. package/ai/tools/applyEditTool.ts +256 -0
  146. package/ai/tools/applyLineEditsTool.ts +312 -0
  147. package/ai/tools/browserTools/click.ts +33 -0
  148. package/ai/tools/browserTools/closeSession.ts +29 -0
  149. package/ai/tools/browserTools/common.ts +27 -0
  150. package/ai/tools/browserTools/openSession.ts +48 -0
  151. package/ai/tools/browserTools/readContent.ts +38 -0
  152. package/ai/tools/browserTools/selectOption.ts +46 -0
  153. package/ai/tools/browserTools/typeText.ts +42 -0
  154. package/ai/tools/category/createCategoryTool.ts +66 -0
  155. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  156. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  157. package/ai/tools/cfBrowserTools.ts +319 -0
  158. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  159. package/ai/tools/checkEnvTool.ts +65 -0
  160. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  161. package/ai/tools/codeSearchTool.ts +111 -0
  162. package/ai/tools/codeTools.ts +101 -0
  163. package/ai/tools/createDocTool.ts +132 -0
  164. package/ai/tools/createPlanTool.ts +999 -0
  165. package/ai/tools/createSkillDocTool.ts +155 -0
  166. package/ai/tools/createWorkflowTool.ts +154 -0
  167. package/ai/tools/deepseekOcrTool.ts +34 -0
  168. package/ai/tools/delayTool.ts +31 -0
  169. package/ai/tools/deleteSpacesTool.ts +325 -0
  170. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  171. package/ai/tools/devReloadUtils.ts +29 -0
  172. package/ai/tools/dialogMessageSearch.ts +137 -0
  173. package/ai/tools/doctorSkillTool.ts +72 -0
  174. package/ai/tools/ecommerceScraperTool.ts +86 -0
  175. package/ai/tools/emailTools.ts +549 -0
  176. package/ai/tools/evalSkillTool.ts +92 -0
  177. package/ai/tools/exaSearchTool.ts +64 -0
  178. package/ai/tools/execBashTool.ts +379 -0
  179. package/ai/tools/executeSqlTool.ts +192 -0
  180. package/ai/tools/fetchWebpageSupport.ts +309 -0
  181. package/ai/tools/fetchWebpageTool.ts +84 -0
  182. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  183. package/ai/tools/generateDocxTool.ts +215 -0
  184. package/ai/tools/googleSearchScraperTool.ts +106 -0
  185. package/ai/tools/importDataTool.ts +133 -0
  186. package/ai/tools/importSkillTool.ts +162 -0
  187. package/ai/tools/index.ts +1858 -0
  188. package/ai/tools/listFilesTool.ts +82 -0
  189. package/ai/tools/listUserSpacesTool.ts +113 -0
  190. package/ai/tools/modelUsageTools.ts +142 -0
  191. package/ai/tools/olmOcrTool.ts +34 -0
  192. package/ai/tools/openaiImageTool.ts +218 -0
  193. package/ai/tools/paddleOcrTool.ts +34 -0
  194. package/ai/tools/prepareTools.ts +23 -0
  195. package/ai/tools/readDocTool.ts +84 -0
  196. package/ai/tools/readFileTool.ts +211 -0
  197. package/ai/tools/readTool.ts +163 -0
  198. package/ai/tools/readXPostTool.ts +233 -0
  199. package/ai/tools/rememberMemoryTool.ts +84 -0
  200. package/ai/tools/remotionVideoTool.ts +151 -0
  201. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  202. package/ai/tools/searchRepoTool.ts +115 -0
  203. package/ai/tools/searchWorkspaceTool.ts +259 -0
  204. package/ai/tools/skillFollowup.ts +86 -0
  205. package/ai/tools/surfWeatherTool.ts +169 -0
  206. package/ai/tools/table/addTableRowTool.ts +217 -0
  207. package/ai/tools/table/createTableTool.ts +315 -0
  208. package/ai/tools/table/rowTools.ts +366 -0
  209. package/ai/tools/table/schemaTools.ts +244 -0
  210. package/ai/tools/table/shareTableTool.ts +148 -0
  211. package/ai/tools/table/toolShared.ts +129 -0
  212. package/ai/tools/toolApiClient.ts +198 -0
  213. package/ai/tools/toolNameAliases.ts +57 -0
  214. package/ai/tools/toolResultError.ts +42 -0
  215. package/ai/tools/toolRunSlice.ts +303 -0
  216. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  217. package/ai/tools/toolVisibility.ts +4 -0
  218. package/ai/tools/types.ts +20 -0
  219. package/ai/tools/uiAskChoiceTool.ts +104 -0
  220. package/ai/tools/updateContentTitleTool.ts +84 -0
  221. package/ai/tools/updateDocTool.ts +105 -0
  222. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  223. package/ai/tools/whisperTool.ts +77 -0
  224. package/ai/tools/writeFileTool.ts +210 -0
  225. package/ai/tools/youtubeScraperTool.ts +116 -0
  226. package/ai/tools/ziweiChartTool.ts +678 -0
  227. package/ai/types.ts +55 -0
  228. package/ai/workflow/workflowExecutor.ts +323 -0
  229. package/ai/workflow/workflowSlice.ts +73 -0
  230. package/ai/workflow/workflowTypes.ts +106 -0
  231. package/client/compactDialog.ts +222 -0
  232. package/connector-experimental/capabilities.ts +73 -0
  233. package/connector-experimental/codexBinary.ts +41 -0
  234. package/connector-experimental/heartbeatLoop.ts +22 -0
  235. package/connector-experimental/index.ts +5 -0
  236. package/connector-experimental/machineInfo.ts +46 -0
  237. package/connector-experimental/protocol.ts +54 -0
  238. package/machineCommands.ts +4 -4
  239. package/package.json +22 -6
@@ -0,0 +1,434 @@
1
+ import { dump as dumpYaml, load as loadYaml } from "js-yaml";
2
+
3
+ const SKILL_DOC_ENUMS = {
4
+ triggerMode: ["explicit", "required", "recommended"],
5
+ budgetTier: ["low", "medium", "high"],
6
+ modality: ["text", "image", "video", "audio", "3d"],
7
+ docKind: ["knowledge", "instruction", "skill"],
8
+ } as const;
9
+
10
+ type SkillDocEnumKey = keyof typeof SKILL_DOC_ENUMS;
11
+
12
+ export type SkillTriggerMode = (typeof SKILL_DOC_ENUMS.triggerMode)[number];
13
+ export type SkillBudgetTier = (typeof SKILL_DOC_ENUMS.budgetTier)[number];
14
+ export type SkillModality = (typeof SKILL_DOC_ENUMS.modality)[number];
15
+ export type SkillDocKind = (typeof SKILL_DOC_ENUMS.docKind)[number];
16
+
17
+ export interface SkillDocConfig {
18
+ version: "0.1";
19
+ kind: "skill";
20
+ id?: string;
21
+ name: string;
22
+ description: string;
23
+ triggerMode?: SkillTriggerMode;
24
+ toolNames?: string[];
25
+ preferredAgents?: string[];
26
+ budgetTier?: SkillBudgetTier;
27
+ dispatchPreferred?: boolean;
28
+ modalities?: SkillModality[];
29
+ requiredSkills?: string[];
30
+ recommendedSkills?: string[];
31
+ promptPatch?: string;
32
+ discover?: {
33
+ keywords?: string[];
34
+ examples?: string[];
35
+ };
36
+ }
37
+
38
+ export interface SkillEvalCase {
39
+ input: string;
40
+ expectedTools?: string[];
41
+ expectedSignals?: string[];
42
+ forbiddenSignals?: string[];
43
+ }
44
+
45
+ export interface SkillEvalConfig {
46
+ version: "0.1";
47
+ cases: SkillEvalCase[];
48
+ }
49
+
50
+ export interface PageSkillMetadata {
51
+ kind?: SkillDocKind;
52
+ requiredSkills?: string[];
53
+ recommendedSkills?: string[];
54
+ skillConfig?: SkillDocConfig;
55
+ evalConfig?: SkillEvalConfig;
56
+ }
57
+
58
+ export interface ParsedSkillDocProtocol {
59
+ content: string;
60
+ meta?: PageSkillMetadata;
61
+ }
62
+
63
+ export interface ParsedExternalSkillMarkdown {
64
+ name?: string;
65
+ description?: string;
66
+ compatibility?: string;
67
+ allowedTools: string[];
68
+ metadata?: Record<string, string>;
69
+ body: string;
70
+ }
71
+
72
+ const SKILL_CONFIG_BLOCK = "skill-config";
73
+ const EVAL_CONFIG_BLOCK = "eval-config";
74
+
75
+ const normalizeStringArray = (value: unknown): string[] | undefined => {
76
+ if (!Array.isArray(value)) return undefined;
77
+ const items = value
78
+ .filter((item): item is string => typeof item === "string")
79
+ .map((item) => item.trim())
80
+ .filter(Boolean);
81
+ return items.length > 0 ? Array.from(new Set(items)) : undefined;
82
+ };
83
+
84
+ const normalizeBoolean = (value: unknown): boolean | undefined =>
85
+ typeof value === "boolean" ? value : undefined;
86
+
87
+ const normalizeSkillEnumValue = <K extends SkillDocEnumKey>(
88
+ key: K,
89
+ value: unknown
90
+ ): (typeof SKILL_DOC_ENUMS)[K][number] | undefined =>
91
+ typeof value === "string" &&
92
+ (SKILL_DOC_ENUMS[key] as readonly string[]).includes(value)
93
+ ? (value as (typeof SKILL_DOC_ENUMS)[K][number])
94
+ : undefined;
95
+
96
+ const normalizeSkillModalities = (
97
+ value: unknown
98
+ ): SkillModality[] | undefined => {
99
+ const raw = normalizeStringArray(value);
100
+ if (!raw) return undefined;
101
+
102
+ const filtered = raw.flatMap((item) => {
103
+ const modality = normalizeSkillEnumValue("modality", item);
104
+ return modality ? [modality] : [];
105
+ });
106
+
107
+ return filtered.length > 0 ? Array.from(new Set(filtered)) : undefined;
108
+ };
109
+
110
+ const normalizeSkillConfig = (
111
+ value: unknown,
112
+ fallbackTools?: string[]
113
+ ): SkillDocConfig | undefined => {
114
+ if (!value || typeof value !== "object") return undefined;
115
+ const record = value as Record<string, unknown>;
116
+ const name =
117
+ typeof record.name === "string" && record.name.trim()
118
+ ? record.name.trim()
119
+ : "";
120
+ const description =
121
+ typeof record.description === "string" && record.description.trim()
122
+ ? record.description.trim()
123
+ : "";
124
+
125
+ if (!name || !description) return undefined;
126
+
127
+ const triggerMode = normalizeSkillEnumValue("triggerMode", record.triggerMode);
128
+ const budgetTier = normalizeSkillEnumValue("budgetTier", record.budgetTier);
129
+ const modalities = normalizeSkillModalities(record.modalities);
130
+
131
+ const toolNames =
132
+ normalizeStringArray(record.toolNames) ??
133
+ normalizeStringArray(fallbackTools) ??
134
+ undefined;
135
+
136
+ const discover =
137
+ record.discover && typeof record.discover === "object"
138
+ ? {
139
+ keywords: normalizeStringArray(
140
+ (record.discover as Record<string, unknown>).keywords
141
+ ),
142
+ examples: normalizeStringArray(
143
+ (record.discover as Record<string, unknown>).examples
144
+ ),
145
+ }
146
+ : undefined;
147
+
148
+ return {
149
+ version: "0.1",
150
+ kind: "skill",
151
+ id:
152
+ typeof record.id === "string" && record.id.trim()
153
+ ? record.id.trim()
154
+ : undefined,
155
+ name,
156
+ description,
157
+ triggerMode,
158
+ toolNames,
159
+ preferredAgents: normalizeStringArray(record.preferredAgents),
160
+ budgetTier,
161
+ dispatchPreferred: normalizeBoolean(record.dispatchPreferred),
162
+ modalities,
163
+ requiredSkills: normalizeStringArray(record.requiredSkills),
164
+ recommendedSkills: normalizeStringArray(record.recommendedSkills),
165
+ promptPatch:
166
+ typeof record.promptPatch === "string" && record.promptPatch.trim()
167
+ ? record.promptPatch.trim()
168
+ : undefined,
169
+ discover:
170
+ discover?.keywords || discover?.examples ? discover : undefined,
171
+ };
172
+ };
173
+
174
+ const normalizeEvalConfig = (value: unknown): SkillEvalConfig | undefined => {
175
+ if (!value || typeof value !== "object") return undefined;
176
+ const record = value as Record<string, unknown>;
177
+ if (!Array.isArray(record.cases)) return undefined;
178
+ const cases: SkillEvalCase[] = [];
179
+ for (const item of record.cases) {
180
+ if (!item || typeof item !== "object") continue;
181
+ const testCase = item as Record<string, unknown>;
182
+ const input =
183
+ typeof testCase.input === "string" && testCase.input.trim()
184
+ ? testCase.input.trim()
185
+ : "";
186
+ if (!input) continue;
187
+ cases.push({
188
+ input,
189
+ expectedTools: normalizeStringArray(testCase.expectedTools),
190
+ expectedSignals: normalizeStringArray(testCase.expectedSignals),
191
+ forbiddenSignals: normalizeStringArray(testCase.forbiddenSignals),
192
+ });
193
+ }
194
+
195
+ return cases.length > 0
196
+ ? {
197
+ version: "0.1",
198
+ cases,
199
+ }
200
+ : undefined;
201
+ };
202
+
203
+ const normalizePageSkillMetadata = (
204
+ value: unknown,
205
+ fallbackTools?: string[]
206
+ ): PageSkillMetadata | undefined => {
207
+ if (!value || typeof value !== "object") return undefined;
208
+ const record = value as Record<string, unknown>;
209
+ const kind = normalizeSkillEnumValue("docKind", record.kind);
210
+
211
+ const meta: PageSkillMetadata = {
212
+ kind,
213
+ requiredSkills: normalizeStringArray(record.requiredSkills),
214
+ recommendedSkills: normalizeStringArray(record.recommendedSkills),
215
+ skillConfig: normalizeSkillConfig(record.skillConfig, fallbackTools),
216
+ evalConfig: normalizeEvalConfig(record.evalConfig),
217
+ };
218
+
219
+ if (meta.skillConfig && !meta.kind) {
220
+ meta.kind = "skill";
221
+ }
222
+
223
+ return meta.kind ||
224
+ meta.requiredSkills ||
225
+ meta.recommendedSkills ||
226
+ meta.skillConfig ||
227
+ meta.evalConfig
228
+ ? meta
229
+ : undefined;
230
+ };
231
+
232
+ const extractCommentBlock = (
233
+ markdown: string,
234
+ blockName: string
235
+ ): string | undefined => {
236
+ if (!markdown) return undefined;
237
+ const matcher = new RegExp(
238
+ `<!--\\s*${blockName}\\s*\\n([\\s\\S]*?)-->`,
239
+ "i"
240
+ );
241
+ const match = markdown.match(matcher);
242
+ return typeof match?.[1] === "string" ? match[1].trim() : undefined;
243
+ };
244
+
245
+ const removeCommentBlock = (markdown: string, blockName: string): string =>
246
+ markdown.replace(
247
+ new RegExp(`\\n?<!--\\s*${blockName}\\s*\\n[\\s\\S]*?-->\\s*`, "gi"),
248
+ "\n\n"
249
+ );
250
+
251
+ const parseYamlObject = (raw: string | undefined): Record<string, unknown> | undefined => {
252
+ if (!raw) return undefined;
253
+ try {
254
+ const parsed = loadYaml(raw);
255
+ return parsed && typeof parsed === "object"
256
+ ? (parsed as Record<string, unknown>)
257
+ : undefined;
258
+ } catch {
259
+ return undefined;
260
+ }
261
+ };
262
+
263
+ export const parseSkillDocProtocol = (
264
+ markdown: string | undefined,
265
+ existingMeta?: PageSkillMetadata,
266
+ fallbackTools?: string[]
267
+ ): ParsedSkillDocProtocol => {
268
+ const source = typeof markdown === "string" ? markdown : "";
269
+ const skillBlock = parseYamlObject(extractCommentBlock(source, SKILL_CONFIG_BLOCK));
270
+ const evalBlock = parseYamlObject(extractCommentBlock(source, EVAL_CONFIG_BLOCK));
271
+ const cleanedContent = removeCommentBlock(
272
+ removeCommentBlock(source, SKILL_CONFIG_BLOCK),
273
+ EVAL_CONFIG_BLOCK
274
+ )
275
+ .replace(/\n{3,}/g, "\n\n")
276
+ .trim();
277
+
278
+ const parsedMeta = normalizePageSkillMetadata(
279
+ {
280
+ ...(existingMeta ?? {}),
281
+ ...(skillBlock
282
+ ? {
283
+ kind:
284
+ existingMeta?.kind === "skill" || skillBlock.kind === "skill"
285
+ ? "skill"
286
+ : existingMeta?.kind,
287
+ requiredSkills:
288
+ skillBlock.requiredSkills ?? existingMeta?.requiredSkills,
289
+ recommendedSkills:
290
+ skillBlock.recommendedSkills ?? existingMeta?.recommendedSkills,
291
+ skillConfig: skillBlock,
292
+ }
293
+ : {}),
294
+ ...(evalBlock ? { evalConfig: evalBlock } : {}),
295
+ },
296
+ fallbackTools
297
+ );
298
+
299
+ return {
300
+ content: cleanedContent,
301
+ meta: parsedMeta,
302
+ };
303
+ };
304
+
305
+ export const resolvePageSkillMetadata = (
306
+ page: {
307
+ content?: string | null;
308
+ meta?: unknown;
309
+ tools?: string[];
310
+ } | null | undefined
311
+ ): PageSkillMetadata | undefined => {
312
+ if (!page) return undefined;
313
+ const existingMeta = normalizePageSkillMetadata(page.meta, page.tools);
314
+ return parseSkillDocProtocol(page.content ?? "", existingMeta, page.tools).meta;
315
+ };
316
+
317
+ const yamlBlock = (value: Record<string, unknown>): string =>
318
+ dumpYaml(value, { lineWidth: 120, noRefs: true }).trim();
319
+
320
+ export const buildSkillConfigComment = (config: SkillDocConfig): string => {
321
+ const payload: Record<string, unknown> = {
322
+ version: config.version,
323
+ kind: config.kind,
324
+ ...(config.id ? { id: config.id } : {}),
325
+ name: config.name,
326
+ description: config.description,
327
+ ...(config.triggerMode ? { triggerMode: config.triggerMode } : {}),
328
+ ...(config.requiredSkills?.length
329
+ ? { requiredSkills: config.requiredSkills }
330
+ : {}),
331
+ ...(config.recommendedSkills?.length
332
+ ? { recommendedSkills: config.recommendedSkills }
333
+ : {}),
334
+ ...(config.toolNames?.length ? { toolNames: config.toolNames } : {}),
335
+ ...(config.preferredAgents?.length
336
+ ? { preferredAgents: config.preferredAgents }
337
+ : {}),
338
+ ...(config.budgetTier ? { budgetTier: config.budgetTier } : {}),
339
+ ...(typeof config.dispatchPreferred === "boolean"
340
+ ? { dispatchPreferred: config.dispatchPreferred }
341
+ : {}),
342
+ ...(config.modalities?.length ? { modalities: config.modalities } : {}),
343
+ ...(config.discover?.keywords?.length ||
344
+ config.discover?.examples?.length
345
+ ? {
346
+ discover: {
347
+ ...(config.discover?.keywords?.length
348
+ ? { keywords: config.discover.keywords }
349
+ : {}),
350
+ ...(config.discover?.examples?.length
351
+ ? { examples: config.discover.examples }
352
+ : {}),
353
+ },
354
+ }
355
+ : {}),
356
+ ...(config.promptPatch ? { promptPatch: config.promptPatch } : {}),
357
+ };
358
+ return `<!-- ${SKILL_CONFIG_BLOCK}\n${yamlBlock(payload)}\n-->`;
359
+ };
360
+
361
+ export const buildEvalConfigComment = (config: SkillEvalConfig): string =>
362
+ `<!-- ${EVAL_CONFIG_BLOCK}\n${yamlBlock({
363
+ version: config.version,
364
+ cases: config.cases,
365
+ })}\n-->`;
366
+
367
+ export const buildSkillDocMarkdown = (options: {
368
+ body?: string;
369
+ skillConfig: SkillDocConfig;
370
+ evalConfig?: SkillEvalConfig;
371
+ }): string => {
372
+ const sections = [
373
+ options.body?.trim() || "",
374
+ buildSkillConfigComment(options.skillConfig),
375
+ options.evalConfig ? buildEvalConfigComment(options.evalConfig) : "",
376
+ ].filter(Boolean);
377
+ return sections.join("\n\n").trim();
378
+ };
379
+
380
+ const extractFrontmatter = (markdown: string): {
381
+ frontmatter?: Record<string, unknown>;
382
+ body: string;
383
+ } => {
384
+ const match = markdown.match(/^\s*---\s*\n([\s\S]*?)\n\s*---\s*\n?/);
385
+ if (!match) return { body: markdown.trim() };
386
+ const frontmatter = parseYamlObject(match[1]);
387
+ const body = markdown.slice(match[0].length).trim();
388
+ return { frontmatter, body };
389
+ };
390
+
391
+ const normalizeAllowedTools = (value: unknown): string[] => {
392
+ if (Array.isArray(value)) {
393
+ return normalizeStringArray(value) ?? [];
394
+ }
395
+ if (typeof value === "string") {
396
+ return value
397
+ .split(/\s+/)
398
+ .map((item) => item.trim())
399
+ .filter(Boolean);
400
+ }
401
+ return [];
402
+ };
403
+
404
+ export const parseExternalSkillMarkdown = (
405
+ markdown: string
406
+ ): ParsedExternalSkillMarkdown => {
407
+ const { frontmatter, body } = extractFrontmatter(markdown);
408
+ return {
409
+ name:
410
+ typeof frontmatter?.name === "string" && frontmatter.name.trim()
411
+ ? frontmatter.name.trim()
412
+ : undefined,
413
+ description:
414
+ typeof frontmatter?.description === "string" &&
415
+ frontmatter.description.trim()
416
+ ? frontmatter.description.trim()
417
+ : undefined,
418
+ compatibility:
419
+ typeof frontmatter?.compatibility === "string" &&
420
+ frontmatter.compatibility.trim()
421
+ ? frontmatter.compatibility.trim()
422
+ : undefined,
423
+ allowedTools: normalizeAllowedTools(frontmatter?.["allowed-tools"]),
424
+ metadata:
425
+ frontmatter?.metadata && typeof frontmatter.metadata === "object"
426
+ ? Object.fromEntries(
427
+ Object.entries(frontmatter.metadata as Record<string, unknown>)
428
+ .filter(([, value]) => typeof value === "string")
429
+ .map(([key, value]) => [key, String(value)])
430
+ )
431
+ : undefined,
432
+ body,
433
+ };
434
+ };
@@ -0,0 +1,63 @@
1
+ import type { ReferenceItem } from "app/types";
2
+
3
+ import { resolvePageSkillMetadata } from "./skillDocProtocol";
4
+ import { joinUniqueStrings, type SkillRuntimePageLike } from "./referenceRuntime";
5
+
6
+ export type SkillReferenceSummary = {
7
+ dbKey: string;
8
+ title: string;
9
+ referenceType: ReferenceItem["type"];
10
+ skillId?: string;
11
+ skillName: string;
12
+ description?: string;
13
+ toolNames: string[];
14
+ requiredSkills: string[];
15
+ recommendedSkills: string[];
16
+ promptPatch?: string;
17
+ };
18
+
19
+ export const summarizeSkillReferences = (
20
+ references: ReferenceItem[] | undefined,
21
+ contentByKey: Map<string, SkillRuntimePageLike>
22
+ ): SkillReferenceSummary[] => {
23
+ if (!Array.isArray(references) || references.length === 0) {
24
+ return [];
25
+ }
26
+
27
+ return references.flatMap((reference) => {
28
+ const content = contentByKey.get(reference.dbKey);
29
+ const meta = resolvePageSkillMetadata(content);
30
+ const skillConfig = meta?.skillConfig;
31
+
32
+ if (meta?.kind !== "skill" && !skillConfig) {
33
+ return [];
34
+ }
35
+
36
+ const skillName =
37
+ skillConfig?.name?.trim() ||
38
+ reference.title?.trim() ||
39
+ content?.title?.trim() ||
40
+ reference.dbKey;
41
+
42
+ return [
43
+ {
44
+ dbKey: reference.dbKey,
45
+ title: reference.title?.trim() || content?.title?.trim() || reference.dbKey,
46
+ referenceType: reference.type,
47
+ skillId: skillConfig?.id?.trim() || undefined,
48
+ skillName,
49
+ description: skillConfig?.description?.trim() || undefined,
50
+ toolNames: skillConfig?.toolNames ?? [],
51
+ requiredSkills: joinUniqueStrings(
52
+ meta?.requiredSkills,
53
+ skillConfig?.requiredSkills
54
+ ),
55
+ recommendedSkills: joinUniqueStrings(
56
+ meta?.recommendedSkills,
57
+ skillConfig?.recommendedSkills
58
+ ),
59
+ promptPatch: skillConfig?.promptPatch?.trim() || undefined,
60
+ },
61
+ ];
62
+ });
63
+ };
@@ -0,0 +1,26 @@
1
+ import type { PageSkillMetadata } from "./skillDocProtocol";
2
+ import type { SpaceContent } from "app/types";
3
+
4
+ export type SkillSummaryMarker = NonNullable<SpaceContent["skillSummary"]>;
5
+
6
+ export const buildSkillSummaryMarker = (
7
+ meta?: PageSkillMetadata | null
8
+ ): SkillSummaryMarker | null => {
9
+ const skillConfig = meta?.skillConfig;
10
+ if (meta?.kind !== "skill" && !skillConfig) {
11
+ return null;
12
+ }
13
+
14
+ return {
15
+ isSkill: true,
16
+ ...(skillConfig?.id ? { skillId: skillConfig.id } : {}),
17
+ ...(skillConfig?.name ? { name: skillConfig.name } : {}),
18
+ ...(skillConfig?.description ? { description: skillConfig.description } : {}),
19
+ ...(skillConfig?.toolNames?.length ? { toolNames: skillConfig.toolNames } : {}),
20
+ ...(skillConfig?.triggerMode ? { triggerMode: skillConfig.triggerMode } : {}),
21
+ };
22
+ };
23
+
24
+ export const isSkillSummaryMarker = (
25
+ value: SpaceContent["skillSummary"] | undefined | null
26
+ ): value is SkillSummaryMarker => Boolean(value?.isSkill);