cortico 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/package.json +43 -0
  4. package/src/boot.ts +46 -0
  5. package/src/bot.ts +1342 -0
  6. package/src/config-file.ts +41 -0
  7. package/src/core/README.md +92 -0
  8. package/src/core/billing.ts +26 -0
  9. package/src/core/blobs.ts +106 -0
  10. package/src/core/bus.ts +311 -0
  11. package/src/core/config-schema.ts +201 -0
  12. package/src/core/config.ts +262 -0
  13. package/src/core/core.ts +699 -0
  14. package/src/core/cost.ts +244 -0
  15. package/src/core/event-store.ts +304 -0
  16. package/src/core/fork.ts +113 -0
  17. package/src/core/generation.ts +118 -0
  18. package/src/core/instance-lock.ts +215 -0
  19. package/src/core/ipc-logger.ts +105 -0
  20. package/src/core/language.ts +62 -0
  21. package/src/core/log-context.ts +38 -0
  22. package/src/core/loop.ts +1858 -0
  23. package/src/core/markers.ts +48 -0
  24. package/src/core/prefix.ts +121 -0
  25. package/src/core/run.ts +95 -0
  26. package/src/core/secrets.ts +18 -0
  27. package/src/core/session.ts +56 -0
  28. package/src/core/sessions.ts +261 -0
  29. package/src/core/state.ts +104 -0
  30. package/src/core/template.ts +72 -0
  31. package/src/core/timers.ts +129 -0
  32. package/src/core/tool-log.ts +135 -0
  33. package/src/core/transcript.ts +92 -0
  34. package/src/core/truncate.ts +121 -0
  35. package/src/core/types.ts +1140 -0
  36. package/src/core/usage-log.ts +79 -0
  37. package/src/core/util.ts +386 -0
  38. package/src/deploy-listing.ts +160 -0
  39. package/src/deploy.ts +162 -0
  40. package/src/extensions/README.md +72 -0
  41. package/src/extensions/dry-mount.ts +390 -0
  42. package/src/extensions/manifest.ts +153 -0
  43. package/src/extensions/runtime.ts +42 -0
  44. package/src/extensions.ts +519 -0
  45. package/src/launcher.ts +285 -0
  46. package/src/paths.ts +155 -0
  47. package/src/protocol/open-responses/LICENSE +201 -0
  48. package/src/protocol/open-responses/README.md +14 -0
  49. package/src/protocol/open-responses/context-helpers.ts +27 -0
  50. package/src/protocol/open-responses/context-log.ts +59 -0
  51. package/src/protocol/open-responses/context.ts +89 -0
  52. package/src/protocol/open-responses/generated.ts +115 -0
  53. package/src/protocol/open-responses/index.ts +33 -0
  54. package/src/protocol/open-responses/openapi.json +4230 -0
  55. package/src/protocol/open-responses/stream.ts +146 -0
  56. package/src/protocol/open-responses/tokens.ts +31 -0
  57. package/src/providers/README.md +86 -0
  58. package/src/providers/base.ts +111 -0
  59. package/src/providers/configuration.ts +97 -0
  60. package/src/providers/console/config.ts +21 -0
  61. package/src/providers/console/settings.ts +443 -0
  62. package/src/providers/console/strings.ts +74 -0
  63. package/src/providers/console/types.ts +11 -0
  64. package/src/providers/llamacpp/archive.ts +148 -0
  65. package/src/providers/llamacpp/catalog.ts +247 -0
  66. package/src/providers/llamacpp/config.ts +36 -0
  67. package/src/providers/llamacpp/console/client.ts +6 -0
  68. package/src/providers/llamacpp/console/models-panel.ts +120 -0
  69. package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
  70. package/src/providers/llamacpp/console/server.ts +153 -0
  71. package/src/providers/llamacpp/index.ts +95 -0
  72. package/src/providers/llamacpp/native.ts +51 -0
  73. package/src/providers/llamacpp/options.ts +117 -0
  74. package/src/providers/llamacpp/runtime-store.ts +139 -0
  75. package/src/providers/llamacpp/runtime.ts +214 -0
  76. package/src/providers/llamacpp/server.ts +286 -0
  77. package/src/providers/llamacpp/strings.ts +230 -0
  78. package/src/providers/openai-responses-compat/config.ts +20 -0
  79. package/src/providers/openai-responses-compat/index.ts +89 -0
  80. package/src/providers/openai-responses-compat/native.ts +127 -0
  81. package/src/providers/openai-responses-compat/strings.ts +19 -0
  82. package/src/providers/pricebook.ts +75 -0
  83. package/src/providers/registry.ts +137 -0
  84. package/src/providers/strings.ts +71 -0
  85. package/src/providers/transport/chat.ts +156 -0
  86. package/src/providers/transport/errors.ts +48 -0
  87. package/src/providers/transport/history.ts +68 -0
  88. package/src/providers/transport/native-input.ts +83 -0
  89. package/src/providers/transport/native-types.ts +13 -0
  90. package/src/providers/transport/response-assembly.ts +191 -0
  91. package/src/providers/transport/response-http.ts +196 -0
  92. package/src/providers/transport/response-meters.ts +27 -0
  93. package/src/providers/transport/responses-input.ts +56 -0
  94. package/src/web/README.md +84 -0
  95. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
  96. package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
  97. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
  98. package/src/web/client/console-pages/builtins.ts +10 -0
  99. package/src/web/client/console-pages/context.ts +135 -0
  100. package/src/web/client/console-pages/host.ts +633 -0
  101. package/src/web/client/console-pages/loader.ts +132 -0
  102. package/src/web/client/console-pages/strings.ts +90 -0
  103. package/src/web/client/console-pages/tools/strings.ts +49 -0
  104. package/src/web/client/console-pages/tools/view.ts +215 -0
  105. package/src/web/client/core/api.ts +200 -0
  106. package/src/web/client/core/language.ts +56 -0
  107. package/src/web/client/core/lifecycle.ts +132 -0
  108. package/src/web/client/core/router.ts +199 -0
  109. package/src/web/client/core/stream.ts +157 -0
  110. package/src/web/client/core/websocket.ts +82 -0
  111. package/src/web/client/features/appearance/index.ts +333 -0
  112. package/src/web/client/features/appearance/strings.ts +87 -0
  113. package/src/web/client/features/config/index.ts +11 -0
  114. package/src/web/client/features/config/strings.ts +41 -0
  115. package/src/web/client/features/config/view.ts +428 -0
  116. package/src/web/client/features/core/events.ts +197 -0
  117. package/src/web/client/features/core/index.ts +254 -0
  118. package/src/web/client/features/core/run.ts +56 -0
  119. package/src/web/client/features/core/runlog.ts +214 -0
  120. package/src/web/client/features/core/sessions.ts +53 -0
  121. package/src/web/client/features/core/strings.ts +157 -0
  122. package/src/web/client/features/extensions/index.ts +422 -0
  123. package/src/web/client/features/extensions/strings.ts +164 -0
  124. package/src/web/client/features/feature.ts +93 -0
  125. package/src/web/client/features/live/context.ts +280 -0
  126. package/src/web/client/features/live/fork.ts +156 -0
  127. package/src/web/client/features/live/index.ts +392 -0
  128. package/src/web/client/features/live/onboarding.ts +113 -0
  129. package/src/web/client/features/live/protocol.ts +122 -0
  130. package/src/web/client/features/live/sessions.ts +71 -0
  131. package/src/web/client/features/live/status.ts +68 -0
  132. package/src/web/client/features/live/strings.ts +252 -0
  133. package/src/web/client/features/live/timeline.ts +590 -0
  134. package/src/web/client/features/prompts/editor.ts +292 -0
  135. package/src/web/client/features/prompts/index.ts +346 -0
  136. package/src/web/client/features/prompts/strings.ts +136 -0
  137. package/src/web/client/features/prompts/view.ts +256 -0
  138. package/src/web/client/features/providers/index.ts +100 -0
  139. package/src/web/client/features/providers/strings.ts +23 -0
  140. package/src/web/client/features/settings/general.ts +28 -0
  141. package/src/web/client/features/settings/index.ts +82 -0
  142. package/src/web/client/features/settings/strings.ts +33 -0
  143. package/src/web/client/features/storage/index.ts +1 -0
  144. package/src/web/client/features/storage/strings.ts +45 -0
  145. package/src/web/client/features/storage/view.ts +154 -0
  146. package/src/web/client/features/usage/chart.ts +486 -0
  147. package/src/web/client/features/usage/index.ts +407 -0
  148. package/src/web/client/features/usage/labels.ts +46 -0
  149. package/src/web/client/features/usage/range.ts +36 -0
  150. package/src/web/client/features/usage/state.ts +37 -0
  151. package/src/web/client/features/usage/strings.ts +191 -0
  152. package/src/web/client/features/usage/tooltip.ts +86 -0
  153. package/src/web/client/features/usage/types.ts +96 -0
  154. package/src/web/client/features/worlds/index.ts +424 -0
  155. package/src/web/client/features/worlds/strings.ts +121 -0
  156. package/src/web/client/main.ts +273 -0
  157. package/src/web/client/shell/avatar.ts +233 -0
  158. package/src/web/client/shell/index.ts +549 -0
  159. package/src/web/client/shell/strings.ts +89 -0
  160. package/src/web/client/theme/handoff.ts +43 -0
  161. package/src/web/client/theme/palette.ts +109 -0
  162. package/src/web/client/theme/registry.ts +130 -0
  163. package/src/web/client/theme/storage.ts +72 -0
  164. package/src/web/client/theme/strings.ts +123 -0
  165. package/src/web/client/theme/studio.ts +366 -0
  166. package/src/web/client/ui/actions.ts +109 -0
  167. package/src/web/client/ui/data.ts +185 -0
  168. package/src/web/client/ui/dom.ts +23 -0
  169. package/src/web/client/ui/fields.ts +191 -0
  170. package/src/web/client/ui/format.ts +75 -0
  171. package/src/web/client/ui/icons.ts +183 -0
  172. package/src/web/client/ui/images.ts +91 -0
  173. package/src/web/client/ui/index.ts +89 -0
  174. package/src/web/client/ui/lamp.ts +125 -0
  175. package/src/web/client/ui/log.ts +98 -0
  176. package/src/web/client/ui/overlay.ts +184 -0
  177. package/src/web/client/ui/page.ts +8 -0
  178. package/src/web/client/ui/prompt-input.tsx +311 -0
  179. package/src/web/client/ui/sheet.ts +131 -0
  180. package/src/web/client/ui/strings.ts +71 -0
  181. package/src/web/console-pages.ts +367 -0
  182. package/src/web/files.ts +107 -0
  183. package/src/web/path-picker.ts +296 -0
  184. package/src/web/public/index.html +17 -0
  185. package/src/web/public/styles.css +1715 -0
  186. package/src/web/server.ts +1883 -0
  187. package/src/web/shared/client-panel.ts +603 -0
  188. package/src/web/shared/console-protocol.ts +560 -0
  189. package/src/web/shared/css.d.ts +1 -0
  190. package/src/web/shared/path-picker.ts +23 -0
  191. package/src/web/shared/theme.ts +256 -0
  192. package/src/web/theme-store.ts +37 -0
  193. package/src/world.ts +449 -0
  194. package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
  195. package/src/worlds/bilibili/README.md +176 -0
  196. package/src/worlds/bilibili/audience-admission.ts +995 -0
  197. package/src/worlds/bilibili/client.ts +528 -0
  198. package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
  199. package/src/worlds/bilibili/config.ts +185 -0
  200. package/src/worlds/bilibili/console/client.ts +202 -0
  201. package/src/worlds/bilibili/definition.ts +35 -0
  202. package/src/worlds/bilibili/gift-frame.ts +131 -0
  203. package/src/worlds/bilibili/normalize.ts +452 -0
  204. package/src/worlds/bilibili/overlay/announcement.ts +76 -0
  205. package/src/worlds/bilibili/overlay/assets.ts +82 -0
  206. package/src/worlds/bilibili/overlay/model.ts +494 -0
  207. package/src/worlds/bilibili/overlay/project.ts +202 -0
  208. package/src/worlds/bilibili/overlay/server.ts +286 -0
  209. package/src/worlds/bilibili/overlay/types.ts +217 -0
  210. package/src/worlds/bilibili/overlay/web/app.js +690 -0
  211. package/src/worlds/bilibili/overlay/web/editor.css +297 -0
  212. package/src/worlds/bilibili/overlay/web/editor.html +102 -0
  213. package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
  214. package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
  215. package/src/worlds/bilibili/overlay/web/styles.css +258 -0
  216. package/src/worlds/bilibili/protobuf.ts +123 -0
  217. package/src/worlds/bilibili/wire.ts +94 -0
  218. package/src/worlds/bilibili/world.ts +1389 -0
  219. package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
  220. package/src/worlds/console-fixture/console/client.ts +72 -0
  221. package/src/worlds/console-fixture/world.ts +73 -0
  222. package/src/worlds/index.ts +15 -0
  223. package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
  224. package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
  225. package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
  226. package/src/worlds/minecraft/README.md +917 -0
  227. package/src/worlds/minecraft/blueprint-plan.ts +915 -0
  228. package/src/worlds/minecraft/blueprint-registry.ts +566 -0
  229. package/src/worlds/minecraft/blueprint-repair.ts +236 -0
  230. package/src/worlds/minecraft/blueprint-resource.ts +272 -0
  231. package/src/worlds/minecraft/blueprint.ts +492 -0
  232. package/src/worlds/minecraft/body-lease.ts +340 -0
  233. package/src/worlds/minecraft/bridge.ts +749 -0
  234. package/src/worlds/minecraft/check.ts +651 -0
  235. package/src/worlds/minecraft/chests.ts +292 -0
  236. package/src/worlds/minecraft/client-launch.ts +272 -0
  237. package/src/worlds/minecraft/client-options.ts +85 -0
  238. package/src/worlds/minecraft/client-skins.ts +164 -0
  239. package/src/worlds/minecraft/client-window.ps1 +91 -0
  240. package/src/worlds/minecraft/client.ts +423 -0
  241. package/src/worlds/minecraft/combat-context.ts +109 -0
  242. package/src/worlds/minecraft/combat.ts +1552 -0
  243. package/src/worlds/minecraft/config.ts +499 -0
  244. package/src/worlds/minecraft/console/access.ts +218 -0
  245. package/src/worlds/minecraft/console/client.ts +244 -0
  246. package/src/worlds/minecraft/console/log.ts +110 -0
  247. package/src/worlds/minecraft/console/mount.ts +420 -0
  248. package/src/worlds/minecraft/console/skin.ts +257 -0
  249. package/src/worlds/minecraft/console/style.css +154 -0
  250. package/src/worlds/minecraft/console/world.ts +449 -0
  251. package/src/worlds/minecraft/deaths.ts +135 -0
  252. package/src/worlds/minecraft/definition.ts +18 -0
  253. package/src/worlds/minecraft/engine-child.ts +274 -0
  254. package/src/worlds/minecraft/engine-ipc.ts +98 -0
  255. package/src/worlds/minecraft/entity-facts.ts +132 -0
  256. package/src/worlds/minecraft/escape.ts +407 -0
  257. package/src/worlds/minecraft/executor.ts +14494 -0
  258. package/src/worlds/minecraft/explored.ts +156 -0
  259. package/src/worlds/minecraft/geometry.ts +205 -0
  260. package/src/worlds/minecraft/goal-plan.ts +647 -0
  261. package/src/worlds/minecraft/item-break.ts +126 -0
  262. package/src/worlds/minecraft/item-facts.ts +146 -0
  263. package/src/worlds/minecraft/item-pick.ts +67 -0
  264. package/src/worlds/minecraft/level-dat.ts +176 -0
  265. package/src/worlds/minecraft/log.ts +132 -0
  266. package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
  267. package/src/worlds/minecraft/names.ts +377 -0
  268. package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
  269. package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
  270. package/src/worlds/minecraft/piglin.ts +49 -0
  271. package/src/worlds/minecraft/policy.ts +547 -0
  272. package/src/worlds/minecraft/precheck.ts +655 -0
  273. package/src/worlds/minecraft/proxy.ts +445 -0
  274. package/src/worlds/minecraft/ranged.ts +635 -0
  275. package/src/worlds/minecraft/readouts.ts +102 -0
  276. package/src/worlds/minecraft/round.ts +26 -0
  277. package/src/worlds/minecraft/search-observation.ts +92 -0
  278. package/src/worlds/minecraft/server-config.ts +467 -0
  279. package/src/worlds/minecraft/server.ts +658 -0
  280. package/src/worlds/minecraft/show.ts +84 -0
  281. package/src/worlds/minecraft/skills.ts +1763 -0
  282. package/src/worlds/minecraft/terrain.ts +1735 -0
  283. package/src/worlds/minecraft/window.ts +96 -0
  284. package/src/worlds/minecraft/works.ts +179 -0
  285. package/src/worlds/minecraft/world.ts +6438 -0
  286. package/src/worlds/qq/ENV_PROMPT.md +5 -0
  287. package/src/worlds/qq/config.ts +108 -0
  288. package/src/worlds/qq/console/client.ts +126 -0
  289. package/src/worlds/qq/console/events.ts +155 -0
  290. package/src/worlds/qq/console/gate.ts +232 -0
  291. package/src/worlds/qq/console/roster.ts +203 -0
  292. package/src/worlds/qq/console/style.css +65 -0
  293. package/src/worlds/qq/conversation.ts +28 -0
  294. package/src/worlds/qq/definition.ts +62 -0
  295. package/src/worlds/qq/driver.ts +375 -0
  296. package/src/worlds/qq/history-tools.ts +209 -0
  297. package/src/worlds/qq/normalize.ts +323 -0
  298. package/src/worlds/qq/qface-map.ts +282 -0
  299. package/src/worlds/qq/vision-prompt.ts +27 -0
  300. package/src/worlds/qq/vision.ts +642 -0
  301. package/src/worlds/qq/vlm.ts +142 -0
  302. package/src/worlds/qq/world.ts +1503 -0
  303. package/src/worlds/terminal/ENV_PROMPT.md +11 -0
  304. package/src/worlds/terminal/config.ts +9 -0
  305. package/src/worlds/terminal/definition.ts +14 -0
  306. package/src/worlds/terminal/world.ts +700 -0
  307. package/src/worlds/websearch/ENV_PROMPT.md +1 -0
  308. package/src/worlds/websearch/brave-client.ts +261 -0
  309. package/src/worlds/websearch/config.ts +75 -0
  310. package/src/worlds/websearch/definition.ts +20 -0
  311. package/src/worlds/websearch/world.ts +171 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Core、Persona 和 World 用 JSON Schema 声明配置,控制台读取声明生成表单。
3
+ * 支持 integer、number、boolean、string(含 enum / x-options)与二元数组
4
+ * (prefixItems,或 items + minItems=maxItems=2);其他类型只读。
5
+ * 扩展属性:
6
+ * x-scale:显示值 = 存储值 / scale,提交时乘回 scale。
7
+ * x-suffix:显示单位;x-hot=false:修改后需重启。
8
+ * x-options:从 /api/config/options/:kind 获取当前选项,配置仍存字符串。
9
+ * x-path:选择本机路径;x-download:该路径对应的浏览器下载链接。
10
+ */
11
+ import type { CoreConfig } from './types.ts';
12
+ import { pick, type Language } from './language.ts';
13
+
14
+ export interface ConfigProperty {
15
+ type: 'integer' | 'number' | 'boolean' | 'string' | 'array';
16
+ title: string;
17
+ description?: string;
18
+ minimum?: number;
19
+ maximum?: number;
20
+ multipleOf?: number;
21
+ enum?: string[];
22
+ /** 2 元数组的元素声明 */
23
+ items?: { type: 'integer' | 'number'; minimum?: number; maximum?: number };
24
+ minItems?: number;
25
+ maxItems?: number;
26
+ /** 允许写 null(用于"留空=关掉"这类项) */
27
+ nullable?: boolean;
28
+ 'x-scale'?: number;
29
+ 'x-suffix'?: string;
30
+ 'x-hot'?: boolean;
31
+ /** 动态下拉的选项源 id;控制台打开下拉前现探 */
32
+ 'x-options'?: string;
33
+ /** 字符串路径的本机选择器。推荐目录只作部署提示与对话框起始位置。 */
34
+ 'x-path'?: {
35
+ kind: 'file' | 'directory';
36
+ extensions?: string[];
37
+ recommendedDir?: string;
38
+ };
39
+ /** 由声明方提供的可信下载地址。 */
40
+ 'x-download'?: {
41
+ href: string;
42
+ label?: string;
43
+ };
44
+ }
45
+
46
+ export interface ConfigGroupSchema {
47
+ type: 'object';
48
+ title: string;
49
+ description?: string;
50
+ /** 键是 cfg 中的点分路径。 */
51
+ properties: Record<string, ConfigProperty>;
52
+ }
53
+
54
+ export interface ConfigGroup {
55
+ /** 配置组的稳定 id,用于提交寻址;由所有者定义,可包含具体 Persona 名称。 */
56
+ id: string;
57
+ /** 所有者角色,不含具体实现名称;不同 Persona 的 owner 均为 persona。 */
58
+ owner: 'core' | 'persona' | `world:${string}` | `provider:${string}`;
59
+ schema: ConfigGroupSchema;
60
+ }
61
+
62
+ export type ConfigValue = number | boolean | string | null | [number, number];
63
+ export type ConfigValues = Record<string, ConfigValue>;
64
+
65
+
66
+ export function getByPath(obj: Record<string, unknown>, path: string): unknown {
67
+ let cur: unknown = obj;
68
+ for (const seg of path.split('.')) {
69
+ if (cur == null || typeof cur !== 'object') return undefined;
70
+ cur = (cur as Record<string, unknown>)[seg];
71
+ }
72
+ return cur;
73
+ }
74
+
75
+ /** 仅替换叶子属性,保留父对象引用供配置热更新使用。 */
76
+ export function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void {
77
+ const segs = path.split('.');
78
+ const leaf = segs.pop()!;
79
+ let cur: Record<string, unknown> = obj;
80
+ for (const seg of segs) {
81
+ const next = cur[seg];
82
+ if (next == null || typeof next !== 'object') cur[seg] = {};
83
+ cur = cur[seg] as Record<string, unknown>;
84
+ }
85
+ cur[leaf] = value;
86
+ }
87
+
88
+ /** 一组配置项的当前值(按 schema 的键从活配置里读) */
89
+ export function readGroupValues(cfg: CoreConfig, group: ConfigGroup, read?: (path:string)=>unknown): ConfigValues {
90
+ const root = cfg as unknown as Record<string, unknown>;
91
+ const out: ConfigValues = {};
92
+ for (const [path, prop] of Object.entries(group.schema.properties)) {
93
+ const raw = read ? read(path) : getByPath(root, path);
94
+ if (prop.type === 'boolean') out[path] = raw === true;
95
+ else if (prop.type === 'array') {
96
+ const pair = Array.isArray(raw) ? raw : [0, 0];
97
+ out[path] = [Number(pair[0]) || 0, Number(pair[1]) || 0];
98
+ } else if (prop.type === 'string') out[path] = raw == null ? '' : String(raw);
99
+ else if (raw == null) out[path] = prop.nullable ? null : 0;
100
+ else out[path] = Number(raw);
101
+ }
102
+ return out;
103
+ }
104
+
105
+
106
+ /** 校验回执的措辞。`label` 是声明方给的 title,已经是当前语言。 */
107
+ const VALIDATION_TEXT = {
108
+ zh: {
109
+ notNumber: (label: string) => `${label} 必须是数值`,
110
+ below: (label: string, min: number) => `${label} 不能小于 ${min}`,
111
+ above: (label: string, max: number) => `${label} 不能大于 ${max}`,
112
+ notInEnum: (label: string, options: string) => `${label} 只能是 ${options}`,
113
+ needsPair: (label: string) => `${label} 需要两个数`,
114
+ first: (label: string) => `${label} 第一项`,
115
+ second: (label: string) => `${label} 第二项`,
116
+ pairOrder: (label: string) => `${label} 的第一项不能大于第二项`,
117
+ empty: (label: string) => `${label} 不能为空`,
118
+ },
119
+ en: {
120
+ notNumber: (label: string) => `${label} must be a number`,
121
+ below: (label: string, min: number) => `${label} cannot be less than ${min}`,
122
+ above: (label: string, max: number) => `${label} cannot be greater than ${max}`,
123
+ notInEnum: (label: string, options: string) => `${label} must be one of ${options}`,
124
+ needsPair: (label: string) => `${label} needs two numbers`,
125
+ first: (label: string) => `${label} (first)`,
126
+ second: (label: string) => `${label} (second)`,
127
+ pairOrder: (label: string) => `${label}: the first value cannot exceed the second`,
128
+ empty: (label: string) => `${label} cannot be empty`,
129
+ },
130
+ };
131
+ const validationText = (language: Language) => pick(language, VALIDATION_TEXT);
132
+ type ValidationText = ReturnType<typeof validationText>;
133
+
134
+ const numberIn = (
135
+ raw: unknown,
136
+ prop: ConfigProperty | NonNullable<ConfigProperty['items']>,
137
+ label: string,
138
+ integer: boolean,
139
+ text: ValidationText,
140
+ ): number | { error: string } => {
141
+ let n = Number(raw);
142
+ if (!Number.isFinite(n)) return { error: text.notNumber(label) };
143
+ if (integer) n = Math.floor(n);
144
+ if (prop.minimum != null && n < prop.minimum) return { error: text.below(label, prop.minimum) };
145
+ if (prop.maximum != null && n > prop.maximum) return { error: text.above(label, prop.maximum) };
146
+ return n;
147
+ };
148
+
149
+ /** 按声明校验提交值;忽略未知字段。回执采用请求语言,默认中文。 */
150
+ export function coerceGroupValues(
151
+ group: ConfigGroup,
152
+ body: Record<string, unknown>,
153
+ language: Language = 'zh',
154
+ ): { values: ConfigValues } | { error: string } {
155
+ const text = validationText(language);
156
+ const out: ConfigValues = {};
157
+ for (const [path, prop] of Object.entries(group.schema.properties)) {
158
+ if (!(path in body)) continue;
159
+ const raw = body[path];
160
+ const label = prop.title;
161
+
162
+ if (prop.type === 'boolean') {
163
+ out[path] = raw === true;
164
+ continue;
165
+ }
166
+ if (prop.type === 'string') {
167
+ const s = String(raw ?? '');
168
+ if (prop.enum && !prop.enum.includes(s)) {
169
+ return { error: text.notInEnum(label, prop.enum.join(' / ')) };
170
+ }
171
+ out[path] = s;
172
+ continue;
173
+ }
174
+ if (prop.type === 'array') {
175
+ if (!Array.isArray(raw) || raw.length !== 2) return { error: text.needsPair(label) };
176
+ const spec = prop.items ?? { type: 'number' as const };
177
+ const integer = spec.type === 'integer';
178
+ const first = numberIn(raw[0], spec, text.first(label), integer, text);
179
+ if (typeof first !== 'number') return first;
180
+ const second = numberIn(raw[1], spec, text.second(label), integer, text);
181
+ if (typeof second !== 'number') return second;
182
+ if (first > second) return { error: text.pairOrder(label) };
183
+ out[path] = [first, second];
184
+ continue;
185
+ }
186
+ if (prop.type === 'integer' || prop.type === 'number') {
187
+ if (raw === null) {
188
+ // 必须在数值转换前处理 null;Number(null) 会将清空请求转换为 0。
189
+ if (!prop.nullable) return { error: text.empty(label) };
190
+ out[path] = null;
191
+ continue;
192
+ }
193
+ const n = numberIn(raw, prop, label, prop.type === 'integer', text);
194
+ if (typeof n !== 'number') return n;
195
+ out[path] = n;
196
+ continue;
197
+ }
198
+ // 未支持的类型只读,不写回。
199
+ }
200
+ return { values: out };
201
+ }
@@ -0,0 +1,262 @@
1
+ /** Core 默认配置与深合并工具。Persona 和 World 参数由各自声明,部署合并顺序见 src/deploy.ts。 */
2
+ import type { CoreConfig } from './types.ts';
3
+ import type { PriceDefinition } from '../providers/pricebook.ts';
4
+ import type { ConfigGroup } from './config-schema.ts';
5
+ import { pick, type Language } from './language.ts';
6
+
7
+ const CORE_GROUP_TEXT = {
8
+ zh: {
9
+ title: "事件合批、推理与日志",
10
+ description: "",
11
+ displayName: {
12
+ title: '展示名',
13
+ description: '控制台标题与 bot 发出的消息用这个名字。控制台立刻跟上;已经挂载的 World 在自己重启后才跟上。',
14
+ },
15
+ quietGap: {
16
+ title: '安静窗口',
17
+ description: "参与合批的事件在最后一项到达后等待此时长;达到批次时限或数量上限时提前投递。",
18
+ },
19
+ minBatchAge: {
20
+ title: "最短合批时间",
21
+ description: "参与合批的事件从第一项到达起至少等待此时长;批次时限和数量上限优先。",
22
+ },
23
+ maxBatchAge: {
24
+ title: "最长合批时间",
25
+ description: "从第一项到达起,合批等待不超过此时长。",
26
+ },
27
+ maxBatchSize: {
28
+ title: '单批上限',
29
+ suffix: '条',
30
+ description: "外部事件和候选达到此数量时立即投递;不计延迟渲染项和 piggyback 项。",
31
+ },
32
+ keepPastThinking: {
33
+ title: '保留历史思维链',
34
+ description: "启用后,provider 可回传兼容的历史推理;关闭后请求不含历史推理。已保存的 session 不变。",
35
+ },
36
+ logFile: {
37
+ title: '日志落盘门槛',
38
+ description: "低于此级别的记录不写入 data/runs/<run>/log.jsonl。",
39
+ },
40
+ logConsole: {
41
+ title: '日志打印门槛',
42
+ description: '低于这一级的记录不打到控制台窗口。',
43
+ },
44
+ logAreas: {
45
+ title: '按区域覆盖落盘门槛',
46
+ description: "以逗号分隔 `区域=级别`,例如 `core.loop=trace,console=warn`;支持 `.*`。最长匹配前缀优先,未匹配的区域使用默认门槛。",
47
+ },
48
+ },
49
+ en: {
50
+ title: "Event batching, reasoning and logging",
51
+ description: "",
52
+ displayName: {
53
+ title: 'Display name',
54
+ description: 'Used for the console title and as the sender name on messages the bot sends. The console picks it up at once; a mounted World does so when that World restarts.',
55
+ },
56
+ quietGap: {
57
+ title: 'Quiet window',
58
+ description: "Wait this long after the last batched item arrives; the batch time and size limits can trigger earlier delivery.",
59
+ },
60
+ minBatchAge: {
61
+ title: 'Minimum batch age',
62
+ description: "Wait at least this long after the first batched item arrives; the batch time and size limits take precedence.",
63
+ },
64
+ maxBatchAge: {
65
+ title: "Maximum batch age",
66
+ description: "Limit batching delay to this duration from the first item.",
67
+ },
68
+ maxBatchSize: {
69
+ title: 'Batch size limit',
70
+ suffix: 'items',
71
+ description: "Deliver when external events and candidates reach this count; deferred rendering and piggyback items are excluded.",
72
+ },
73
+ keepPastThinking: {
74
+ title: 'Keep past reasoning',
75
+ description: "Allow the provider to replay compatible past reasoning. When disabled, requests omit past reasoning. Saved sessions are unchanged.",
76
+ },
77
+ logFile: {
78
+ title: 'Log file threshold',
79
+ description: "Records below this level are not written to data/runs/<run>/log.jsonl.",
80
+ },
81
+ logConsole: {
82
+ title: 'Log print threshold',
83
+ description: 'Records below this level are not printed to the console window.',
84
+ },
85
+ logAreas: {
86
+ title: 'Per-area file threshold overrides',
87
+ description: "Comma-separated `area=level`, such as `core.loop=trace,console=warn`; `.*` is supported. The longest matching prefix applies. Unmatched areas use the default threshold.",
88
+ },
89
+ },
90
+ };
91
+
92
+ /** 按请求语言生成配置文案;两种语言使用相同的结构与取值范围。 */
93
+ export function coreConfigGroup(language: Language): ConfigGroup {
94
+ const t = pick(language, CORE_GROUP_TEXT);
95
+ return {
96
+ id: 'core',
97
+ owner: 'core',
98
+ schema: {
99
+ type: 'object',
100
+ title: t.title,
101
+ description: t.description,
102
+ properties: {
103
+ displayName: {
104
+ type: 'string',
105
+ title: t.displayName.title,
106
+ 'x-hot': true,
107
+ description: t.displayName.description,
108
+ },
109
+ 'batching.quietGapMs': {
110
+ type: 'integer',
111
+ title: t.quietGap.title,
112
+ minimum: 100,
113
+ maximum: 600_000,
114
+ multipleOf: 100,
115
+ 'x-scale': 1000,
116
+ 'x-suffix': 's',
117
+ 'x-hot': true,
118
+ description: t.quietGap.description,
119
+ },
120
+ 'batching.minBatchAgeMs': {
121
+ type: 'integer',
122
+ title: t.minBatchAge.title,
123
+ minimum: 0,
124
+ maximum: 600_000,
125
+ multipleOf: 100,
126
+ 'x-scale': 1000,
127
+ 'x-suffix': 's',
128
+ 'x-hot': true,
129
+ description: t.minBatchAge.description,
130
+ },
131
+ 'batching.maxBatchAgeMs': {
132
+ type: 'integer',
133
+ title: t.maxBatchAge.title,
134
+ minimum: 1000,
135
+ maximum: 3_600_000,
136
+ multipleOf: 500,
137
+ 'x-scale': 1000,
138
+ 'x-suffix': 's',
139
+ 'x-hot': true,
140
+ description: t.maxBatchAge.description,
141
+ },
142
+ 'batching.maxBatchSize': {
143
+ type: 'integer',
144
+ title: t.maxBatchSize.title,
145
+ minimum: 1,
146
+ maximum: 1000,
147
+ 'x-suffix': t.maxBatchSize.suffix,
148
+ 'x-hot': true,
149
+ description: t.maxBatchSize.description,
150
+ },
151
+ 'context.keepPastThinking': {
152
+ type: 'boolean',
153
+ title: t.keepPastThinking.title,
154
+ 'x-hot': true,
155
+ description: t.keepPastThinking.description,
156
+ },
157
+ 'logging.file': {
158
+ type: 'string',
159
+ title: t.logFile.title,
160
+ enum: ['trace', 'debug', 'info', 'warn', 'error'],
161
+ 'x-hot': true,
162
+ description: t.logFile.description,
163
+ },
164
+ 'logging.console': {
165
+ type: 'string',
166
+ title: t.logConsole.title,
167
+ enum: ['trace', 'debug', 'info', 'warn', 'error'],
168
+ 'x-hot': true,
169
+ description: t.logConsole.description,
170
+ },
171
+ 'logging.areas': {
172
+ type: 'string',
173
+ title: t.logAreas.title,
174
+ 'x-hot': true,
175
+ description: t.logAreas.description,
176
+ },
177
+ },
178
+ },
179
+ };
180
+ }
181
+
182
+ /** 供开发脚本和测试引用的中文版;运行时按请求语言生成。 */
183
+ export const CORE_CONFIG_GROUP: ConfigGroup = coreConfigGroup('zh');
184
+
185
+ export const CORE_DEFAULTS = {
186
+ /** 用于控制台标题和终端消息的发送方名称。 */
187
+ displayName: 'Cortico Bot',
188
+ timezone: 'Asia/Shanghai',
189
+ /** 默认端点;部署根 providers/ 中的同名端点配置覆盖此项。 */
190
+ providers: {
191
+ deepseek: {
192
+ kind: 'openai-responses-compat' as const,
193
+ baseUrl: 'https://api.deepseek.com',
194
+ secret: 'DEEPSEEK_API_KEY',
195
+ spec: { model: 'deepseek-flash', thinking: false },
196
+ // 默认价目为零;操作者可在控制台填写实际价格。
197
+ pricing: [{
198
+ models: ['*'], currency: 'USD', basis: 'marginal', source: 'console',
199
+ rules: [{ meter: 'cachedInput', perMillion: 0 }, { meter: 'uncachedInput', perMillion: 0 }, { meter: 'output', perMillion: 0 }],
200
+ }] as PriceDefinition[],
201
+ },
202
+ },
203
+ activeProvider: 'deepseek',
204
+ web: { port: 7777, theme: 'mint' },
205
+ paths: { memory: 'memory', data: 'data' },
206
+ batching: { quietGapMs: 2500, minBatchAgeMs: 0, maxBatchAgeMs: 15000, maxBatchSize: 100 },
207
+ context: { keepPastThinking: true },
208
+ logging: { file: 'debug' as const, console: 'info' as const, areas: '' },
209
+ } as const;
210
+
211
+
212
+ export function cloneConfigValue<T>(value: T): T {
213
+ if (Array.isArray(value)) {
214
+ return value.map((item) => cloneConfigValue(item)) as T;
215
+ }
216
+ if (typeof value === 'object' && value !== null) {
217
+ const out: Record<string, unknown> = {};
218
+ for (const [key, item] of Object.entries(value)) out[key] = cloneConfigValue(item);
219
+ return out as T;
220
+ }
221
+ return value;
222
+ }
223
+
224
+ /** 后一层覆盖前一层;对象递归合并,数组整体替换。 */
225
+ export function deepMerge<T>(base: T, patch: Partial<T> | undefined): T {
226
+ if (patch === undefined) return cloneConfigValue(base);
227
+ if (Array.isArray(base) || Array.isArray(patch)) {
228
+ return cloneConfigValue((patch as T) ?? base);
229
+ }
230
+ if (typeof base === 'object' && base !== null && typeof patch === 'object' && patch !== null) {
231
+ const out = cloneConfigValue(base) as Record<string, unknown>;
232
+ for (const [k, v] of Object.entries(patch as Record<string, unknown>)) {
233
+ const bv = (base as Record<string, unknown>)[k];
234
+ out[k] = bv !== undefined && typeof bv === 'object' && bv !== null && !Array.isArray(bv)
235
+ ? deepMerge(bv, v as never)
236
+ : cloneConfigValue(v);
237
+ }
238
+ return out as T;
239
+ }
240
+ return cloneConfigValue((patch as T) ?? base);
241
+ }
242
+
243
+ /** 已合并的部署配置。运行时共享 config 引用,配置热更新必须保留父对象身份。 */
244
+ export interface LoadedConfig<C extends CoreConfig = CoreConfig> {
245
+ /** 可包含 Persona 与 World 配置;Core 仅依赖 CoreConfig 字段。 */
246
+ config: C;
247
+ /** 密钥名称由使用方声明,装配层按名称读取。 */
248
+ secret(name: string): string;
249
+ /** 部署目录,包含 config.json、.env 和 data/。 */
250
+ rootDir: string;
251
+ /** bot 代码包目录,可供多个部署使用;缺省时使用 rootDir。 */
252
+ packageDir?: string;
253
+ /**
254
+ * 共享端点目录 <部署根>/providers/,各端点内部结构由 provider 管理。
255
+ * 缺省时使用 <rootDir>/providers。
256
+ */
257
+ providersDir?: string;
258
+ repoRoot?: string;
259
+ /** 解析后的绝对路径 */
260
+ memoryDir: string;
261
+ dataDir: string;
262
+ }