@bryti/agent 0.0.1 → 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 (228) hide show
  1. package/Dockerfile +27 -0
  2. package/README.md +77 -50
  3. package/config.example.yml +265 -0
  4. package/dist/active-hours.d.ts +23 -0
  5. package/dist/active-hours.d.ts.map +1 -0
  6. package/dist/active-hours.js +68 -0
  7. package/dist/active-hours.js.map +1 -0
  8. package/dist/agent.d.ts +84 -0
  9. package/dist/agent.d.ts.map +1 -0
  10. package/dist/agent.js +383 -0
  11. package/dist/agent.js.map +1 -0
  12. package/dist/channels/markdown/ir.d.ts +79 -0
  13. package/dist/channels/markdown/ir.d.ts.map +1 -0
  14. package/dist/channels/markdown/ir.js +824 -0
  15. package/dist/channels/markdown/ir.js.map +1 -0
  16. package/dist/channels/markdown/render.d.ts +35 -0
  17. package/dist/channels/markdown/render.d.ts.map +1 -0
  18. package/dist/channels/markdown/render.js +178 -0
  19. package/dist/channels/markdown/render.js.map +1 -0
  20. package/dist/channels/telegram-network-errors.d.ts +27 -0
  21. package/dist/channels/telegram-network-errors.d.ts.map +1 -0
  22. package/dist/channels/telegram-network-errors.js +156 -0
  23. package/dist/channels/telegram-network-errors.js.map +1 -0
  24. package/dist/channels/telegram.d.ts +76 -0
  25. package/dist/channels/telegram.d.ts.map +1 -0
  26. package/dist/channels/telegram.js +814 -0
  27. package/dist/channels/telegram.js.map +1 -0
  28. package/dist/channels/types.d.ts +59 -0
  29. package/dist/channels/types.d.ts.map +1 -0
  30. package/dist/channels/types.js +9 -0
  31. package/dist/channels/types.js.map +1 -0
  32. package/dist/channels/whatsapp.d.ts +45 -0
  33. package/dist/channels/whatsapp.d.ts.map +1 -0
  34. package/dist/channels/whatsapp.js +310 -0
  35. package/dist/channels/whatsapp.js.map +1 -0
  36. package/dist/cli.d.ts +13 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +635 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/commands.d.ts +35 -0
  41. package/dist/commands.d.ts.map +1 -0
  42. package/dist/commands.js +113 -0
  43. package/dist/commands.js.map +1 -0
  44. package/dist/compaction/history.d.ts +17 -0
  45. package/dist/compaction/history.d.ts.map +1 -0
  46. package/dist/compaction/history.js +35 -0
  47. package/dist/compaction/history.js.map +1 -0
  48. package/dist/compaction/index.d.ts +3 -0
  49. package/dist/compaction/index.d.ts.map +1 -0
  50. package/dist/compaction/index.js +3 -0
  51. package/dist/compaction/index.js.map +1 -0
  52. package/dist/compaction/proactive.d.ts +25 -0
  53. package/dist/compaction/proactive.d.ts.map +1 -0
  54. package/dist/compaction/proactive.js +87 -0
  55. package/dist/compaction/proactive.js.map +1 -0
  56. package/dist/compaction/transcript-repair.d.ts +55 -0
  57. package/dist/compaction/transcript-repair.d.ts.map +1 -0
  58. package/dist/compaction/transcript-repair.js +215 -0
  59. package/dist/compaction/transcript-repair.js.map +1 -0
  60. package/dist/config.d.ts +128 -0
  61. package/dist/config.d.ts.map +1 -0
  62. package/dist/config.js +317 -0
  63. package/dist/config.js.map +1 -0
  64. package/dist/crash-recovery.d.ts +23 -0
  65. package/dist/crash-recovery.d.ts.map +1 -0
  66. package/dist/crash-recovery.js +96 -0
  67. package/dist/crash-recovery.js.map +1 -0
  68. package/dist/defaults/extensions/EXTENSIONS.md +158 -0
  69. package/dist/defaults/extensions/documents-hedgedoc.ts +153 -0
  70. package/dist/history.d.ts +31 -0
  71. package/dist/history.d.ts.map +1 -0
  72. package/dist/history.js +49 -0
  73. package/dist/history.js.map +1 -0
  74. package/dist/index.d.ts +19 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +673 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/logger.d.ts +39 -0
  79. package/dist/logger.d.ts.map +1 -0
  80. package/dist/logger.js +143 -0
  81. package/dist/logger.js.map +1 -0
  82. package/dist/memory/conversation-search.d.ts +15 -0
  83. package/dist/memory/conversation-search.d.ts.map +1 -0
  84. package/dist/memory/conversation-search.js +60 -0
  85. package/dist/memory/conversation-search.js.map +1 -0
  86. package/dist/memory/core-memory.d.ts +28 -0
  87. package/dist/memory/core-memory.d.ts.map +1 -0
  88. package/dist/memory/core-memory.js +102 -0
  89. package/dist/memory/core-memory.js.map +1 -0
  90. package/dist/memory/embeddings.d.ts +44 -0
  91. package/dist/memory/embeddings.d.ts.map +1 -0
  92. package/dist/memory/embeddings.js +139 -0
  93. package/dist/memory/embeddings.js.map +1 -0
  94. package/dist/memory/search.d.ts +49 -0
  95. package/dist/memory/search.d.ts.map +1 -0
  96. package/dist/memory/search.js +97 -0
  97. package/dist/memory/search.js.map +1 -0
  98. package/dist/memory/store.d.ts +32 -0
  99. package/dist/memory/store.d.ts.map +1 -0
  100. package/dist/memory/store.js +205 -0
  101. package/dist/memory/store.js.map +1 -0
  102. package/dist/message-queue.d.ts +73 -0
  103. package/dist/message-queue.d.ts.map +1 -0
  104. package/dist/message-queue.js +188 -0
  105. package/dist/message-queue.js.map +1 -0
  106. package/dist/model-infra.d.ts +64 -0
  107. package/dist/model-infra.d.ts.map +1 -0
  108. package/dist/model-infra.js +202 -0
  109. package/dist/model-infra.js.map +1 -0
  110. package/dist/projection/format.d.ts +10 -0
  111. package/dist/projection/format.d.ts.map +1 -0
  112. package/dist/projection/format.js +30 -0
  113. package/dist/projection/format.js.map +1 -0
  114. package/dist/projection/index.d.ts +11 -0
  115. package/dist/projection/index.d.ts.map +1 -0
  116. package/dist/projection/index.js +9 -0
  117. package/dist/projection/index.js.map +1 -0
  118. package/dist/projection/reflection.d.ts +94 -0
  119. package/dist/projection/reflection.d.ts.map +1 -0
  120. package/dist/projection/reflection.js +334 -0
  121. package/dist/projection/reflection.js.map +1 -0
  122. package/dist/projection/store.d.ts +144 -0
  123. package/dist/projection/store.d.ts.map +1 -0
  124. package/dist/projection/store.js +519 -0
  125. package/dist/projection/store.js.map +1 -0
  126. package/dist/projection/tools.d.ts +11 -0
  127. package/dist/projection/tools.d.ts.map +1 -0
  128. package/dist/projection/tools.js +237 -0
  129. package/dist/projection/tools.js.map +1 -0
  130. package/dist/scheduler.d.ts +36 -0
  131. package/dist/scheduler.d.ts.map +1 -0
  132. package/dist/scheduler.js +286 -0
  133. package/dist/scheduler.js.map +1 -0
  134. package/dist/system-prompt.d.ts +41 -0
  135. package/dist/system-prompt.d.ts.map +1 -0
  136. package/dist/system-prompt.js +162 -0
  137. package/dist/system-prompt.js.map +1 -0
  138. package/dist/time.d.ts +52 -0
  139. package/dist/time.d.ts.map +1 -0
  140. package/dist/time.js +138 -0
  141. package/dist/time.js.map +1 -0
  142. package/dist/tools/archival-memory-tool.d.ts +8 -0
  143. package/dist/tools/archival-memory-tool.d.ts.map +1 -0
  144. package/dist/tools/archival-memory-tool.js +68 -0
  145. package/dist/tools/archival-memory-tool.js.map +1 -0
  146. package/dist/tools/conversation-search-tool.d.ts +6 -0
  147. package/dist/tools/conversation-search-tool.d.ts.map +1 -0
  148. package/dist/tools/conversation-search-tool.js +28 -0
  149. package/dist/tools/conversation-search-tool.js.map +1 -0
  150. package/dist/tools/core-memory-tool.d.ts +7 -0
  151. package/dist/tools/core-memory-tool.d.ts.map +1 -0
  152. package/dist/tools/core-memory-tool.js +59 -0
  153. package/dist/tools/core-memory-tool.js.map +1 -0
  154. package/dist/tools/fetch-url.d.ts +15 -0
  155. package/dist/tools/fetch-url.d.ts.map +1 -0
  156. package/dist/tools/fetch-url.js +76 -0
  157. package/dist/tools/fetch-url.js.map +1 -0
  158. package/dist/tools/files.d.ts +10 -0
  159. package/dist/tools/files.d.ts.map +1 -0
  160. package/dist/tools/files.js +127 -0
  161. package/dist/tools/files.js.map +1 -0
  162. package/dist/tools/index.d.ts +17 -0
  163. package/dist/tools/index.d.ts.map +1 -0
  164. package/dist/tools/index.js +118 -0
  165. package/dist/tools/index.js.map +1 -0
  166. package/dist/tools/result.d.ts +21 -0
  167. package/dist/tools/result.d.ts.map +1 -0
  168. package/dist/tools/result.js +36 -0
  169. package/dist/tools/result.js.map +1 -0
  170. package/dist/tools/skill-install.d.ts +17 -0
  171. package/dist/tools/skill-install.d.ts.map +1 -0
  172. package/dist/tools/skill-install.js +148 -0
  173. package/dist/tools/skill-install.js.map +1 -0
  174. package/dist/tools/web-search.d.ts +42 -0
  175. package/dist/tools/web-search.d.ts.map +1 -0
  176. package/dist/tools/web-search.js +237 -0
  177. package/dist/tools/web-search.js.map +1 -0
  178. package/dist/trust/guardrail.d.ts +60 -0
  179. package/dist/trust/guardrail.d.ts.map +1 -0
  180. package/dist/trust/guardrail.js +171 -0
  181. package/dist/trust/guardrail.js.map +1 -0
  182. package/dist/trust/index.d.ts +12 -0
  183. package/dist/trust/index.d.ts.map +1 -0
  184. package/dist/trust/index.js +12 -0
  185. package/dist/trust/index.js.map +1 -0
  186. package/dist/trust/store.d.ts +118 -0
  187. package/dist/trust/store.d.ts.map +1 -0
  188. package/dist/trust/store.js +209 -0
  189. package/dist/trust/store.js.map +1 -0
  190. package/dist/trust/wrapper.d.ts +36 -0
  191. package/dist/trust/wrapper.d.ts.map +1 -0
  192. package/dist/trust/wrapper.js +142 -0
  193. package/dist/trust/wrapper.js.map +1 -0
  194. package/dist/usage.d.ts +53 -0
  195. package/dist/usage.d.ts.map +1 -0
  196. package/dist/usage.js +124 -0
  197. package/dist/usage.js.map +1 -0
  198. package/dist/util/math.d.ts +9 -0
  199. package/dist/util/math.d.ts.map +1 -0
  200. package/dist/util/math.js +22 -0
  201. package/dist/util/math.js.map +1 -0
  202. package/dist/util/ssrf.d.ts +21 -0
  203. package/dist/util/ssrf.d.ts.map +1 -0
  204. package/dist/util/ssrf.js +77 -0
  205. package/dist/util/ssrf.js.map +1 -0
  206. package/dist/workers/index.d.ts +8 -0
  207. package/dist/workers/index.d.ts.map +1 -0
  208. package/dist/workers/index.js +7 -0
  209. package/dist/workers/index.js.map +1 -0
  210. package/dist/workers/registry.d.ts +53 -0
  211. package/dist/workers/registry.d.ts.map +1 -0
  212. package/dist/workers/registry.js +38 -0
  213. package/dist/workers/registry.js.map +1 -0
  214. package/dist/workers/scoped-tools.d.ts +21 -0
  215. package/dist/workers/scoped-tools.d.ts.map +1 -0
  216. package/dist/workers/scoped-tools.js +111 -0
  217. package/dist/workers/scoped-tools.js.map +1 -0
  218. package/dist/workers/spawn.d.ts +62 -0
  219. package/dist/workers/spawn.d.ts.map +1 -0
  220. package/dist/workers/spawn.js +314 -0
  221. package/dist/workers/spawn.js.map +1 -0
  222. package/dist/workers/tools.d.ts +26 -0
  223. package/dist/workers/tools.d.ts.map +1 -0
  224. package/dist/workers/tools.js +380 -0
  225. package/dist/workers/tools.js.map +1 -0
  226. package/docker-compose.yml +72 -0
  227. package/package.json +16 -1
  228. package/run.sh +27 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * URL fetch and content extraction. HTTP GET, extract readable text via
3
+ * @mozilla/readability + linkedom, truncate to ~4000 chars so it doesn't
4
+ * blow up the context window.
5
+ */
6
+ import axios from "axios";
7
+ import { parseHTML } from "linkedom";
8
+ import { Readability } from "@mozilla/readability";
9
+ import { toolError, toolSuccess } from "./result.js";
10
+ import { Type } from "@sinclair/typebox";
11
+ import { isPrivateHostname, safeLookup } from "../util/ssrf.js";
12
+ const MAX_CONTENT_LENGTH = 4000;
13
+ const fetchUrlSchema = Type.Object({
14
+ url: Type.String({ description: "The URL to fetch" }),
15
+ });
16
+ /**
17
+ * Create the fetch URL tool.
18
+ */
19
+ export function createFetchUrlTool(timeoutMs = 10000) {
20
+ return {
21
+ name: "fetch_url",
22
+ label: "fetch_url",
23
+ description: "Fetch a URL and extract its readable text content. Useful for getting details from a specific webpage.",
24
+ parameters: fetchUrlSchema,
25
+ async execute(_toolCallId, { url }) {
26
+ // SSRF protection: reject requests that target private infrastructure.
27
+ // Without this, a worker instructed to "fetch http://169.254.169.254/
28
+ // latest/meta-data/" would read cloud instance metadata (AWS/GCP/Azure
29
+ // IMDSv1 is unauthenticated). isPrivateHostname() catches obvious cases
30
+ // by hostname pattern; safeLookup() below catches cases where a public
31
+ // hostname resolves to a private IP (DNS rebinding / split-horizon DNS).
32
+ if (isPrivateHostname(url)) {
33
+ return toolError("Cannot fetch private URLs");
34
+ }
35
+ try {
36
+ const response = await axios.get(url, {
37
+ timeout: timeoutMs,
38
+ headers: {
39
+ "User-Agent": "Mozilla/5.0 (compatible; Bryti/1.0)",
40
+ Accept: "text/html,application/xhtml+xml",
41
+ },
42
+ maxContentLength: 2 * 1024 * 1024, // 2MB max
43
+ // DNS-level SSRF protection: reject if hostname resolves to a private IP
44
+ lookup: safeLookup,
45
+ });
46
+ const html = response.data;
47
+ // Readability strips navigation menus, sidebars, ads, cookie banners,
48
+ // and other boilerplate, leaving only the main article text. This
49
+ // produces much cleaner context than raw innerHTML stripping.
50
+ const { document } = parseHTML(html);
51
+ const reader = new Readability(document);
52
+ const article = reader.parse();
53
+ if (!article) {
54
+ const text = JSON.stringify({ error: "Could not extract content from page" });
55
+ return {
56
+ content: [{ type: "text", text }],
57
+ details: { error: "Could not extract content from page" },
58
+ };
59
+ }
60
+ let content = article.textContent || "";
61
+ const title = article.title || "";
62
+ // Hard cap at 4000 chars: a deliberate context-window budget decision.
63
+ // Workers typically fetch several URLs per task; letting one page consume
64
+ // the entire context would crowd out the worker's reasoning and results.
65
+ if (content.length > MAX_CONTENT_LENGTH) {
66
+ content = content.substring(0, MAX_CONTENT_LENGTH) + "\n\n[Content truncated...]";
67
+ }
68
+ return toolSuccess({ title, content: content.trim() });
69
+ }
70
+ catch (error) {
71
+ return toolError(error, "Failed to fetch URL");
72
+ }
73
+ },
74
+ };
75
+ }
76
+ //# sourceMappingURL=fetch-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-url.js","sourceRoot":"","sources":["../../src/tools/fetch-url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Sandboxed file read/write/list tools. Operates within data/files/ with
3
+ * path traversal protection (resolve and verify under base_dir).
4
+ */
5
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
6
+ /**
7
+ * Create file tools.
8
+ */
9
+ export declare function createFileTools(baseDir: string): AgentTool<any>[];
10
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/tools/files.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AA6C9E;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAkGjE"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Sandboxed file read/write/list tools. Operates within data/files/ with
3
+ * path traversal protection (resolve and verify under base_dir).
4
+ */
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { Type } from "@sinclair/typebox";
8
+ import { toolError, toolSuccess } from "./result.js";
9
+ const MAX_FILE_SIZE = 50 * 1024; // 50KB
10
+ const MAX_DEPTH = 3;
11
+ // Schema for read_file
12
+ const readFileSchema = Type.Object({
13
+ path: Type.String({ description: "Relative path to the file (e.g., 'notes/todo.md')" }),
14
+ });
15
+ // Schema for write_file
16
+ const writeFileSchema = Type.Object({
17
+ path: Type.String({ description: "Relative path to the file (e.g., 'notes/todo.md')" }),
18
+ content: Type.String({ description: "Content to write to the file" }),
19
+ });
20
+ // Schema for list_files
21
+ const listFilesSchema = Type.Object({
22
+ directory: Type.Optional(Type.String({ description: "Optional subdirectory to list (e.g., 'notes')" })),
23
+ });
24
+ /**
25
+ * Resolve path and verify it's under base_dir (path traversal protection).
26
+ */
27
+ function resolveSafePath(baseDir, filePath) {
28
+ const resolved = path.resolve(baseDir, filePath);
29
+ const baseResolved = path.resolve(baseDir);
30
+ // Verify the resolved path is under base_dir
31
+ if (!resolved.startsWith(baseResolved + path.sep) && resolved !== baseResolved) {
32
+ return null;
33
+ }
34
+ return resolved;
35
+ }
36
+ /**
37
+ * Create file tools.
38
+ */
39
+ export function createFileTools(baseDir) {
40
+ // Ensure base directory exists
41
+ fs.mkdirSync(baseDir, { recursive: true });
42
+ const readFileTool = {
43
+ name: "file_read",
44
+ label: "file_read",
45
+ description: "Read the contents of a file from the sandboxed files directory.",
46
+ parameters: readFileSchema,
47
+ async execute(_toolCallId, { path: filePath }) {
48
+ const resolved = resolveSafePath(baseDir, filePath);
49
+ if (!resolved)
50
+ return toolError("Invalid path: path traversal not allowed");
51
+ try {
52
+ if (!fs.existsSync(resolved))
53
+ return toolError("File not found");
54
+ const stats = fs.statSync(resolved);
55
+ if (stats.isDirectory())
56
+ return toolError("Path is a directory, not a file");
57
+ if (stats.size > MAX_FILE_SIZE) {
58
+ const content = fs.readFileSync(resolved, "utf-8").slice(0, MAX_FILE_SIZE);
59
+ return toolSuccess({ content: content + "\n\n[File truncated, was larger than 50KB]", truncated: true });
60
+ }
61
+ return toolSuccess({ content: fs.readFileSync(resolved, "utf-8") });
62
+ }
63
+ catch (error) {
64
+ return toolError(error, "Failed to read file");
65
+ }
66
+ },
67
+ };
68
+ const writeFileTool = {
69
+ name: "file_write",
70
+ label: "file_write",
71
+ description: "Write content to a file in the sandboxed files directory. Creates parent directories if needed.",
72
+ parameters: writeFileSchema,
73
+ async execute(_toolCallId, { path: filePath, content }) {
74
+ const resolved = resolveSafePath(baseDir, filePath);
75
+ if (!resolved)
76
+ return toolError("Invalid path: path traversal not allowed");
77
+ try {
78
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
79
+ fs.writeFileSync(resolved, content, "utf-8");
80
+ return toolSuccess({ success: true, bytes: Buffer.byteLength(content, "utf-8") });
81
+ }
82
+ catch (error) {
83
+ return toolError(error, "Failed to write file");
84
+ }
85
+ },
86
+ };
87
+ const listFilesTool = {
88
+ name: "file_list",
89
+ label: "file_list",
90
+ description: "List files in the sandboxed files directory, optionally under a subdirectory.",
91
+ parameters: listFilesSchema,
92
+ async execute(_toolCallId, { directory }) {
93
+ let targetDir = baseDir;
94
+ if (directory) {
95
+ const resolved = resolveSafePath(baseDir, directory);
96
+ if (!resolved)
97
+ return toolError("Invalid path: path traversal not allowed");
98
+ if (!fs.existsSync(resolved))
99
+ return toolError("Directory not found");
100
+ targetDir = resolved;
101
+ }
102
+ try {
103
+ const files = [];
104
+ function walkDir(currentDir, depth) {
105
+ if (depth > MAX_DEPTH)
106
+ return;
107
+ for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
108
+ const fullPath = path.join(currentDir, entry.name);
109
+ if (entry.isFile()) {
110
+ files.push(path.relative(baseDir, fullPath));
111
+ }
112
+ else if (entry.isDirectory()) {
113
+ walkDir(fullPath, depth + 1);
114
+ }
115
+ }
116
+ }
117
+ walkDir(targetDir, 0);
118
+ return toolSuccess({ files });
119
+ }
120
+ catch (error) {
121
+ return toolError(error, "Failed to list files");
122
+ }
123
+ },
124
+ };
125
+ return [readFileTool, writeFileTool, listFilesTool];
126
+ }
127
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/tools/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tool registry. Bryti-specific tools registered as pi SDK custom tools,
3
+ * supplementing pi's built-in tools.
4
+ */
5
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
6
+ import type { Config } from "../config.js";
7
+ import type { CoreMemory } from "../memory/core-memory.js";
8
+ import type { WorkerTriggerCallback } from "../workers/tools.js";
9
+ /** Callback invoked when the agent requests a restart. */
10
+ export type RestartCallback = (reason: string) => Promise<void>;
11
+ type BrytiTool = AgentTool<any>;
12
+ /**
13
+ * Create all bryti tools based on configuration.
14
+ */
15
+ export declare function createTools(config: Config, coreMemory: CoreMemory, userId: string, onWorkerTrigger?: WorkerTriggerCallback, onRestart?: RestartCallback): BrytiTool[];
16
+ export {};
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAa9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGjE,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhE,KAAK,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;AAMhC;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,qBAAqB,EACvC,SAAS,CAAC,EAAE,eAAe,GAC1B,SAAS,EAAE,CAgHb"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Tool registry. Bryti-specific tools registered as pi SDK custom tools,
3
+ * supplementing pi's built-in tools.
4
+ */
5
+ import { Type } from "@sinclair/typebox";
6
+ import { createFileTools } from "./files.js";
7
+ import { createSkillInstallTool } from "./skill-install.js";
8
+ import { createCoreMemoryTools } from "./core-memory-tool.js";
9
+ import { createArchivalMemoryTools } from "./archival-memory-tool.js";
10
+ import { createConversationSearchTool } from "./conversation-search-tool.js";
11
+ import { createProjectionTools, createProjectionStore } from "../projection/index.js";
12
+ import { createWorkerTools, createWorkerRegistry } from "../workers/index.js";
13
+ import { toolSuccess } from "./result.js";
14
+ import { embed } from "../memory/embeddings.js";
15
+ import { createMemoryStore } from "../memory/store.js";
16
+ import path from "node:path";
17
+ import { registerToolCapabilities } from "../trust/index.js";
18
+ const restartSchema = Type.Object({
19
+ reason: Type.String({ description: "Brief description of why a restart is needed (shown in logs)" }),
20
+ });
21
+ /**
22
+ * Create all bryti tools based on configuration.
23
+ */
24
+ export function createTools(config, coreMemory, userId, onWorkerTrigger, onRestart) {
25
+ const tools = [];
26
+ // File tools (main agent can read/write in its sandbox)
27
+ tools.push(...createFileTools(config.tools.files.base_dir));
28
+ // Skill installation: fetch and install skills from URLs or local paths
29
+ tools.push(createSkillInstallTool(config.data_dir));
30
+ registerToolCapabilities("skill_install", {
31
+ level: "elevated",
32
+ capabilities: ["network", "filesystem"],
33
+ reason: "Installs skills from URLs or copies local directories into the skills folder.",
34
+ });
35
+ // ---------------------------------------------------------------------------
36
+ // SECURITY BOUNDARY: web_search and fetch_url are intentionally excluded
37
+ // from the main agent's tool set. All external network access goes through
38
+ // workers, which run in isolated sessions with scoped file access.
39
+ //
40
+ // Rationale: a web page or search result could contain prompt-injected
41
+ // instructions. Processing untrusted content inside the main agent session
42
+ // would let an attacker influence the agent's memory, projections, or tool
43
+ // calls. Workers are disposable; the main agent only sees their sanitised
44
+ // result files.
45
+ // ---------------------------------------------------------------------------
46
+ // Memory tools
47
+ tools.push(...createCoreMemoryTools(coreMemory));
48
+ // Projection memory: forward-looking events and commitments.
49
+ // Created before archival tools so we can pass it in for trigger checking.
50
+ //
51
+ // NOTE: this is a second ProjectionStore instance for the same user — agent.ts
52
+ // creates one too when assembling the system prompt. Both instances point at
53
+ // the same SQLite file, which is safe because the DB is opened in WAL mode
54
+ // (concurrent readers, serialised writers). They do not share in-memory state,
55
+ // so neither instance caches stale rows across the other's writes.
56
+ //
57
+ // TODO: consolidate into a single store per user (pass it in from agent.ts)
58
+ // to make the ownership model explicit and remove the WAL dependency.
59
+ const projectionStore = createProjectionStore(userId, config.data_dir);
60
+ tools.push(...createProjectionTools(projectionStore, config.agent.timezone));
61
+ // Archival memory: always available, uses local embeddings (no API key needed).
62
+ // Receives the projection store so archival inserts can activate trigger-based projections.
63
+ const modelsDir = path.join(config.data_dir, ".models");
64
+ const archivalStore = createMemoryStore(userId, config.data_dir);
65
+ tools.push(...createArchivalMemoryTools(archivalStore, (text) => embed(text, modelsDir), projectionStore));
66
+ tools.push(createConversationSearchTool(path.join(config.data_dir, "history")));
67
+ // Worker tools: dispatch and check background research sessions.
68
+ // The registry lives for the lifetime of this tool set (one per user session).
69
+ // Workers write completion facts to the user's archival memory store.
70
+ // The projection store is passed so worker completion can trigger projections
71
+ // immediately (instead of waiting for the 5-minute scheduler tick).
72
+ const workerRegistry = createWorkerRegistry();
73
+ tools.push(...createWorkerTools(config, archivalStore, workerRegistry, false, projectionStore, onWorkerTrigger));
74
+ // Restart tool: agent can trigger a clean process restart.
75
+ // Used after writing or modifying extensions so new tools load immediately.
76
+ if (onRestart) {
77
+ const restartTool = {
78
+ name: "system_restart",
79
+ label: "system_restart",
80
+ description: "Restart the bryti process to reload extensions and config. " +
81
+ "Use this after writing or modifying an extension file, or after changing config.yml. " +
82
+ "The user will receive a 'Restarting' message, then 'Back online' when ready. " +
83
+ "Always tell the user what you changed and why you are restarting before calling this.",
84
+ parameters: restartSchema,
85
+ async execute(_toolCallId, { reason }) {
86
+ await onRestart(reason);
87
+ // process.exit() will have been called by onRestart — this line is unreachable
88
+ // but satisfies the return type.
89
+ return toolSuccess({ restarting: true });
90
+ },
91
+ };
92
+ tools.push(restartTool);
93
+ // Elevated even though a restart sounds harmless: the guardrail ensures
94
+ // the agent cannot be tricked into restart-looping by a malicious prompt
95
+ // (e.g. "please restart every 10 seconds until I say stop").
96
+ registerToolCapabilities("system_restart", {
97
+ level: "elevated",
98
+ capabilities: ["shell"],
99
+ reason: "Restarts the bryti process.",
100
+ });
101
+ }
102
+ // Register trust capabilities for well-known elevated tools.
103
+ // Extension tools (loaded by pi SDK from data/agent/extensions/) default to
104
+ // elevated since they can execute arbitrary code. Known extension names are
105
+ // registered explicitly for better permission prompts.
106
+ registerToolCapabilities("shell_exec", {
107
+ level: "elevated",
108
+ capabilities: ["shell", "filesystem"],
109
+ reason: "Runs shell commands with access to the system.",
110
+ });
111
+ registerToolCapabilities("http_request", {
112
+ level: "elevated",
113
+ capabilities: ["network"],
114
+ reason: "Makes HTTP requests to external services.",
115
+ });
116
+ return tools;
117
+ }
118
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared helpers for building AgentToolResult objects.
3
+ */
4
+ import type { AgentToolResult } from "@mariozechner/pi-agent-core";
5
+ /**
6
+ * Extract a string message from any thrown value.
7
+ */
8
+ export declare function errorMessage(error: unknown): string;
9
+ /**
10
+ * Build a failure AgentToolResult from a caught error.
11
+ *
12
+ * @param error The caught value (any type).
13
+ * @param prefix Optional prefix for the user-facing message, e.g. "Failed to read file".
14
+ * The raw error message is always stored separately in details.
15
+ */
16
+ export declare function toolError(error: unknown, prefix?: string): AgentToolResult<unknown>;
17
+ /**
18
+ * Build a success AgentToolResult with a JSON payload.
19
+ */
20
+ export declare function toolSuccess(data: unknown): AgentToolResult<unknown>;
21
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAOnF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAKnE"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared helpers for building AgentToolResult objects.
3
+ */
4
+ /**
5
+ * Extract a string message from any thrown value.
6
+ */
7
+ export function errorMessage(error) {
8
+ if (error instanceof Error)
9
+ return error.message;
10
+ return String(error);
11
+ }
12
+ /**
13
+ * Build a failure AgentToolResult from a caught error.
14
+ *
15
+ * @param error The caught value (any type).
16
+ * @param prefix Optional prefix for the user-facing message, e.g. "Failed to read file".
17
+ * The raw error message is always stored separately in details.
18
+ */
19
+ export function toolError(error, prefix) {
20
+ const msg = errorMessage(error);
21
+ const displayMsg = prefix ? `${prefix}: ${msg}` : msg;
22
+ return {
23
+ content: [{ type: "text", text: JSON.stringify({ error: displayMsg }) }],
24
+ details: { error: msg },
25
+ };
26
+ }
27
+ /**
28
+ * Build a success AgentToolResult with a JSON payload.
29
+ */
30
+ export function toolSuccess(data) {
31
+ return {
32
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
33
+ details: data,
34
+ };
35
+ }
36
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,MAAe;IACvD,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACtD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;KACxB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,IAA+B;KACzC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Skill installation tool. Fetches a skill from a URL or local path
3
+ * and installs it into data/skills/.
4
+ *
5
+ * Supports:
6
+ * - Raw URL to a SKILL.md file → saves as skills/<name>/SKILL.md
7
+ * - GitHub/GitLab directory URL → fetches SKILL.md (and referenced files)
8
+ * - Local absolute path → copies the directory
9
+ */
10
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
11
+ declare const skillInstallSchema: import("@sinclair/typebox").TObject<{
12
+ name: import("@sinclair/typebox").TString;
13
+ source: import("@sinclair/typebox").TString;
14
+ }>;
15
+ export declare function createSkillInstallTool(dataDir: string): AgentTool<typeof skillInstallSchema>;
16
+ export {};
17
+ //# sourceMappingURL=skill-install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-install.d.ts","sourceRoot":"","sources":["../../src/tools/skill-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAK9E,QAAA,MAAM,kBAAkB;;;EAQtB,CAAC;AA+CH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,kBAAkB,CAAC,CA0G5F"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Skill installation tool. Fetches a skill from a URL or local path
3
+ * and installs it into data/skills/.
4
+ *
5
+ * Supports:
6
+ * - Raw URL to a SKILL.md file → saves as skills/<name>/SKILL.md
7
+ * - GitHub/GitLab directory URL → fetches SKILL.md (and referenced files)
8
+ * - Local absolute path → copies the directory
9
+ */
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { Type } from "@sinclair/typebox";
13
+ import { toolError, toolSuccess } from "./result.js";
14
+ import { isPrivateHostname } from "../util/ssrf.js";
15
+ const skillInstallSchema = Type.Object({
16
+ name: Type.String({
17
+ description: "Short name for the skill (used as directory name, e.g. 'scribe', 'linkedin')",
18
+ }),
19
+ source: Type.String({
20
+ description: "URL to a SKILL.md file, a GitHub directory URL, or an absolute local path to a skill directory",
21
+ }),
22
+ });
23
+ /**
24
+ * Try to fetch a raw URL. Returns the body text or null.
25
+ */
26
+ async function fetchText(url, timeoutMs = 15_000) {
27
+ try {
28
+ const controller = new AbortController();
29
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
30
+ const response = await fetch(url, { signal: controller.signal });
31
+ clearTimeout(timer);
32
+ if (!response.ok)
33
+ return null;
34
+ return await response.text();
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Convert a GitHub directory URL to the raw URL for SKILL.md.
42
+ * Handles: github.com/<owner>/<repo>/tree/<branch>/<path>
43
+ */
44
+ function githubRawUrl(url) {
45
+ const match = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/(?:tree|blob)\/([^/]+)\/(.+)/);
46
+ if (!match)
47
+ return null;
48
+ const [, owner, repo, branch, filePath] = match;
49
+ // If URL points to a directory, append SKILL.md
50
+ const target = filePath.endsWith(".md") ? filePath : `${filePath}/SKILL.md`;
51
+ return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${target}`;
52
+ }
53
+ /**
54
+ * Convert a GitLab directory URL to the raw URL for SKILL.md.
55
+ * Handles: gitlab.com/<owner>/<repo>/-/tree/<branch>/<path>
56
+ */
57
+ function gitlabRawUrl(url) {
58
+ const match = url.match(/^https?:\/\/gitlab\.com\/([^/]+)\/([^/]+)\/-\/(?:tree|blob)\/([^/]+)\/(.+)/);
59
+ if (!match)
60
+ return null;
61
+ const [, owner, repo, branch, filePath] = match;
62
+ const target = filePath.endsWith(".md") ? filePath : `${filePath}/SKILL.md`;
63
+ return `https://gitlab.com/${owner}/${repo}/-/raw/${branch}/${target}`;
64
+ }
65
+ export function createSkillInstallTool(dataDir) {
66
+ const skillsDir = path.join(dataDir, "skills");
67
+ return {
68
+ name: "skill_install",
69
+ label: "skill_install",
70
+ description: "Install a skill from a URL or local path. Skills are instruction sets that teach you " +
71
+ "how to handle specific tasks. After installing, call system_restart to load the skill. " +
72
+ "Accepts: a direct URL to a SKILL.md file, a GitHub/GitLab directory URL containing " +
73
+ "a SKILL.md, or an absolute local filesystem path to a skill directory.",
74
+ parameters: skillInstallSchema,
75
+ async execute(_toolCallId, { name, source }) {
76
+ // Validate skill name (directory-safe)
77
+ if (!/^[a-z0-9_-]+$/.test(name)) {
78
+ return toolError("Skill name must be lowercase alphanumeric with hyphens or underscores only.");
79
+ }
80
+ const targetDir = path.join(skillsDir, name);
81
+ // --- Local path ---
82
+ if (source.startsWith("/")) {
83
+ if (!fs.existsSync(source)) {
84
+ return toolError(`Path not found: ${source}`);
85
+ }
86
+ const stat = fs.statSync(source);
87
+ if (stat.isDirectory()) {
88
+ // Check for SKILL.md
89
+ const skillMd = path.join(source, "SKILL.md");
90
+ if (!fs.existsSync(skillMd)) {
91
+ return toolError(`No SKILL.md found in ${source}`);
92
+ }
93
+ // Copy the entire directory
94
+ fs.cpSync(source, targetDir, { recursive: true });
95
+ return toolSuccess({
96
+ installed: name,
97
+ path: targetDir,
98
+ files: fs.readdirSync(targetDir),
99
+ message: `Skill "${name}" installed. Call system_restart to load it.`,
100
+ });
101
+ }
102
+ else if (stat.isFile()) {
103
+ // Single file, treat as SKILL.md
104
+ fs.mkdirSync(targetDir, { recursive: true });
105
+ fs.copyFileSync(source, path.join(targetDir, "SKILL.md"));
106
+ return toolSuccess({
107
+ installed: name,
108
+ path: targetDir,
109
+ files: ["SKILL.md"],
110
+ message: `Skill "${name}" installed from file. Call system_restart to load it.`,
111
+ });
112
+ }
113
+ return toolError(`Source is neither a file nor a directory: ${source}`);
114
+ }
115
+ // --- URL ---
116
+ if (!source.startsWith("http://") && !source.startsWith("https://")) {
117
+ return toolError("Source must be an absolute local path (starting with /) or a URL (starting with http).");
118
+ }
119
+ // SSRF protection: reject private/internal URLs
120
+ if (isPrivateHostname(source)) {
121
+ return toolError("Cannot fetch from private or internal URLs.");
122
+ }
123
+ // Try GitHub/GitLab directory URL conversion
124
+ let rawUrl = githubRawUrl(source) ?? gitlabRawUrl(source);
125
+ // If it's a direct URL (not GitHub/GitLab tree), use as-is
126
+ if (!rawUrl) {
127
+ rawUrl = source;
128
+ }
129
+ const content = await fetchText(rawUrl);
130
+ if (!content) {
131
+ return toolError(`Could not fetch skill from ${rawUrl}. Check the URL is accessible and points to a SKILL.md file.`);
132
+ }
133
+ // Basic validation: should look like a skill file
134
+ if (!content.includes("SKILL") && !content.includes("skill") && !content.includes("---")) {
135
+ return toolError("Fetched content doesn't look like a SKILL.md file (no frontmatter or skill markers found).");
136
+ }
137
+ fs.mkdirSync(targetDir, { recursive: true });
138
+ fs.writeFileSync(path.join(targetDir, "SKILL.md"), content, "utf-8");
139
+ return toolSuccess({
140
+ installed: name,
141
+ path: targetDir,
142
+ files: ["SKILL.md"],
143
+ message: `Skill "${name}" installed from URL. Call system_restart to load it.`,
144
+ });
145
+ },
146
+ };
147
+ }
148
+ //# sourceMappingURL=skill-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-install.js","sourceRoot":"","sources":["../../src/tools/skill-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,8EAA8E;KAC5F,CAAC;IACF,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;QAClB,WAAW,EACT,gGAAgG;KACnG,CAAC;CACH,CAAC,CAAC;AAEH;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,SAAS,GAAG,MAAM;IACtD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CACrB,yEAAyE,CAC1E,CAAC;IACF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;IAChD,gDAAgD;IAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,WAAW,CAAC;IAC5E,OAAO,qCAAqC,KAAK,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CACrB,4EAA4E,CAC7E,CAAC;IACF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;IAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,WAAW,CAAC;IAC5E,OAAO,sBAAsB,KAAK,IAAI,IAAI,UAAU,MAAM,IAAI,MAAM,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE/C,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,uFAAuF;YACvF,yFAAyF;YACzF,qFAAqF;YACrF,wEAAwE;QAC1E,UAAU,EAAE,kBAAkB;QAE9B,KAAK,CAAC,OAAO,CACX,WAAmB,EACnB,EAAE,IAAI,EAAE,MAAM,EAAoC;YAElD,uCAAuC;YACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,OAAO,SAAS,CACd,6EAA6E,CAC9E,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAE7C,qBAAqB;YACrB,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC3B,OAAO,SAAS,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;gBAChD,CAAC;gBAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBACvB,qBAAqB;oBACrB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;oBAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC5B,OAAO,SAAS,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;oBACrD,CAAC;oBACD,4BAA4B;oBAC5B,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBAClD,OAAO,WAAW,CAAC;wBACjB,SAAS,EAAE,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC;wBAChC,OAAO,EAAE,UAAU,IAAI,8CAA8C;qBACtE,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;oBACzB,iCAAiC;oBACjC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC7C,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;oBAC1D,OAAO,WAAW,CAAC;wBACjB,SAAS,EAAE,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE,CAAC,UAAU,CAAC;wBACnB,OAAO,EAAE,UAAU,IAAI,wDAAwD;qBAChF,CAAC,CAAC;gBACL,CAAC;gBACD,OAAO,SAAS,CAAC,6CAA6C,MAAM,EAAE,CAAC,CAAC;YAC1E,CAAC;YAED,cAAc;YACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpE,OAAO,SAAS,CACd,wFAAwF,CACzF,CAAC;YACJ,CAAC;YAED,gDAAgD;YAChD,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,OAAO,SAAS,CAAC,6CAA6C,CAAC,CAAC;YAClE,CAAC;YAED,6CAA6C;YAC7C,IAAI,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;YAE1D,2DAA2D;YAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,MAAM,CAAC;YAClB,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,SAAS,CACd,8BAA8B,MAAM,8DAA8D,CACnG,CAAC;YACJ,CAAC;YAED,kDAAkD;YAClD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzF,OAAO,SAAS,CACd,4FAA4F,CAC7F,CAAC;YACJ,CAAC;YAED,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAErE,OAAO,WAAW,CAAC;gBACjB,SAAS,EAAE,IAAI;gBACf,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,CAAC,UAAU,CAAC;gBACnB,OAAO,EAAE,UAAU,IAAI,uDAAuD;aAC/E,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}