@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,42 @@
1
+ /**
2
+ * Web search tools for workers.
3
+ *
4
+ * Two backends exist because they serve different deployment needs:
5
+ *
6
+ * Brave Search — hosted SaaS, single API key, 2000 free queries/month.
7
+ * Good default: no infrastructure to run.
8
+ *
9
+ * SearXNG — self-hosted metasearch engine that aggregates Google, Bing,
10
+ * DuckDuckGo, Brave, and many others in one query. More sources,
11
+ * no per-query cost, but requires a running SearXNG instance.
12
+ * Preferred when the user controls their own instance (privacy,
13
+ * higher volume, or aggregated coverage matters more than setup
14
+ * cost).
15
+ *
16
+ * Workers only; the main agent has no access to these tools (security boundary).
17
+ *
18
+ * Selection logic (in workers/tools.ts):
19
+ * - brave_api_key set → Brave Search
20
+ * - searxng_url set → SearXNG
21
+ * - neither → web search disabled
22
+ */
23
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
24
+ declare const webSearchSchema: import("@sinclair/typebox").TObject<{
25
+ query: import("@sinclair/typebox").TString;
26
+ count: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
27
+ freshness: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
28
+ language: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
29
+ }>;
30
+ /**
31
+ * Create the web search tool backed by Brave Search API.
32
+ *
33
+ * Brave free tier: 2000 queries/month, no credit card required.
34
+ * Docs: https://api.search.brave.com/
35
+ */
36
+ export declare function createBraveSearchTool(apiKey: string): AgentTool<typeof webSearchSchema>;
37
+ /**
38
+ * Create the web search tool backed by SearXNG.
39
+ */
40
+ export declare function createWebSearchTool(searxngUrl: string): AgentTool<typeof webSearchSchema>;
41
+ export {};
42
+ //# sourceMappingURL=web-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../src/tools/web-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAoB9E,QAAA,MAAM,eAAe;;;;;EAoBnB,CAAC;AAuGH;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,eAAe,CAAC,CAiEvF;AAMD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,eAAe,CAAC,CAiEzF"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Web search tools for workers.
3
+ *
4
+ * Two backends exist because they serve different deployment needs:
5
+ *
6
+ * Brave Search — hosted SaaS, single API key, 2000 free queries/month.
7
+ * Good default: no infrastructure to run.
8
+ *
9
+ * SearXNG — self-hosted metasearch engine that aggregates Google, Bing,
10
+ * DuckDuckGo, Brave, and many others in one query. More sources,
11
+ * no per-query cost, but requires a running SearXNG instance.
12
+ * Preferred when the user controls their own instance (privacy,
13
+ * higher volume, or aggregated coverage matters more than setup
14
+ * cost).
15
+ *
16
+ * Workers only; the main agent has no access to these tools (security boundary).
17
+ *
18
+ * Selection logic (in workers/tools.ts):
19
+ * - brave_api_key set → Brave Search
20
+ * - searxng_url set → SearXNG
21
+ * - neither → web search disabled
22
+ */
23
+ import https from "node:https";
24
+ import http from "node:http";
25
+ import { Type } from "@sinclair/typebox";
26
+ const webSearchSchema = Type.Object({
27
+ query: Type.String({ description: "Search query" }),
28
+ count: Type.Optional(Type.Number({
29
+ description: "Number of results to return (default: 10, max: 20)",
30
+ minimum: 1,
31
+ maximum: 20,
32
+ })),
33
+ freshness: Type.Optional(Type.String({
34
+ description: 'Time filter: "day", "week", "month", "year"',
35
+ })),
36
+ language: Type.Optional(Type.String({
37
+ description: 'Result language (e.g., "en", "nl", "de"). Default: "en".',
38
+ })),
39
+ });
40
+ // Why the raw Node http/https module instead of axios or fetch?
41
+ // Self-hosted SearXNG instances often use self-signed TLS certificates. The
42
+ // native fetch() API and axios do not expose `rejectUnauthorized` in a way
43
+ // that is easy to toggle per-request without global side-effects. The raw
44
+ // http/https module accepts a per-request `rejectUnauthorized: false` option,
45
+ // making it straightforward to support internal SearXNG instances without
46
+ // disabling TLS verification globally.
47
+ function fetchJson(url, timeoutMs) {
48
+ return new Promise((resolve, reject) => {
49
+ const protocol = url.startsWith("https") ? https : http;
50
+ const req = protocol.get(url, { timeout: timeoutMs }, (res) => {
51
+ let data = "";
52
+ res.on("data", (chunk) => {
53
+ data += chunk;
54
+ });
55
+ res.on("end", () => {
56
+ try {
57
+ resolve(JSON.parse(data));
58
+ }
59
+ catch (err) {
60
+ reject(new Error(`Failed to parse SearXNG response: ${err.message}`));
61
+ }
62
+ });
63
+ });
64
+ req.on("error", (err) => {
65
+ reject(new Error(`SearXNG request failed: ${err.message}`));
66
+ });
67
+ req.on("timeout", () => {
68
+ req.destroy();
69
+ reject(new Error("SearXNG request timed out"));
70
+ });
71
+ });
72
+ }
73
+ /**
74
+ * Fetch from Brave Search API using Node https (no axios dependency,
75
+ * keeps parity with the SearXNG implementation).
76
+ */
77
+ function fetchBraveJson(url, apiKey, timeoutMs) {
78
+ return new Promise((resolve, reject) => {
79
+ const parsed = new URL(url);
80
+ const options = {
81
+ hostname: parsed.hostname,
82
+ path: parsed.pathname + parsed.search,
83
+ headers: {
84
+ "Accept": "application/json",
85
+ "Accept-Encoding": "gzip",
86
+ "X-Subscription-Token": apiKey,
87
+ },
88
+ timeout: timeoutMs,
89
+ };
90
+ const req = https.get(options, (res) => {
91
+ const chunks = [];
92
+ res.on("data", (chunk) => chunks.push(chunk));
93
+ res.on("end", () => {
94
+ try {
95
+ resolve(JSON.parse(Buffer.concat(chunks).toString()));
96
+ }
97
+ catch (err) {
98
+ reject(new Error(`Failed to parse Brave response: ${err.message}`));
99
+ }
100
+ });
101
+ });
102
+ req.on("error", (err) => {
103
+ reject(new Error(`Brave Search request failed: ${err.message}`));
104
+ });
105
+ req.on("timeout", () => {
106
+ req.destroy();
107
+ reject(new Error("Brave Search request timed out"));
108
+ });
109
+ });
110
+ }
111
+ /**
112
+ * Create the web search tool backed by Brave Search API.
113
+ *
114
+ * Brave free tier: 2000 queries/month, no credit card required.
115
+ * Docs: https://api.search.brave.com/
116
+ */
117
+ export function createBraveSearchTool(apiKey) {
118
+ return {
119
+ name: "web_search",
120
+ label: "web_search",
121
+ description: "Search the web using Brave Search. Returns titles, URLs, and snippets.",
122
+ parameters: webSearchSchema,
123
+ async execute(_toolCallId, { query, count, freshness, language }) {
124
+ const limit = Math.min(count ?? 10, 20);
125
+ const params = new URLSearchParams({
126
+ q: query,
127
+ count: String(limit),
128
+ });
129
+ if (language) {
130
+ params.set("search_lang", language);
131
+ }
132
+ if (freshness) {
133
+ // Brave freshness values: pd, pw, pm, py
134
+ const timeMap = {
135
+ day: "pd",
136
+ week: "pw",
137
+ month: "pm",
138
+ year: "py",
139
+ pd: "pd",
140
+ pw: "pw",
141
+ pm: "pm",
142
+ py: "py",
143
+ };
144
+ const bf = timeMap[freshness];
145
+ if (bf)
146
+ params.set("freshness", bf);
147
+ }
148
+ const url = `https://api.search.brave.com/res/v1/web/search?${params.toString()}`;
149
+ try {
150
+ const response = await fetchBraveJson(url, apiKey, 10000);
151
+ const results = (response.web?.results ?? []).slice(0, limit).map((r) => ({
152
+ title: r.title ?? "",
153
+ url: r.url ?? "",
154
+ snippet: (r.description ?? "").slice(0, 300),
155
+ engine: "brave",
156
+ }));
157
+ const text = JSON.stringify({ results }, null, 2);
158
+ return {
159
+ content: [{ type: "text", text }],
160
+ details: { query, results, total: results.length },
161
+ };
162
+ }
163
+ catch (error) {
164
+ const err = error;
165
+ const text = JSON.stringify({ error: `Search failed: ${err.message}` });
166
+ return {
167
+ content: [{ type: "text", text }],
168
+ details: { error: err.message },
169
+ };
170
+ }
171
+ },
172
+ };
173
+ }
174
+ // ---------------------------------------------------------------------------
175
+ // SearXNG backend
176
+ // ---------------------------------------------------------------------------
177
+ /**
178
+ * Create the web search tool backed by SearXNG.
179
+ */
180
+ export function createWebSearchTool(searxngUrl) {
181
+ return {
182
+ name: "web_search",
183
+ label: "web_search",
184
+ description: "Search the web. Returns titles, URLs, and snippets. " +
185
+ "Aggregates results from Google, Bing, DuckDuckGo, Brave, and more.",
186
+ parameters: webSearchSchema,
187
+ async execute(_toolCallId, { query, count, freshness, language }) {
188
+ const limit = Math.min(count ?? 10, 20);
189
+ const lang = language ?? "en";
190
+ const params = new URLSearchParams({
191
+ q: query,
192
+ format: "json",
193
+ language: lang,
194
+ safesearch: "0",
195
+ });
196
+ if (freshness) {
197
+ // Normalize common formats
198
+ const timeMap = {
199
+ pd: "day",
200
+ pw: "week",
201
+ pm: "month",
202
+ py: "year",
203
+ day: "day",
204
+ week: "week",
205
+ month: "month",
206
+ year: "year",
207
+ };
208
+ const timeRange = timeMap[freshness] ?? freshness;
209
+ params.append("time_range", timeRange);
210
+ }
211
+ const url = `${searxngUrl}/search?${params.toString()}`;
212
+ try {
213
+ const response = await fetchJson(url, 10000);
214
+ const results = (response.results ?? []).slice(0, limit).map((r) => ({
215
+ title: r.title ?? "",
216
+ url: r.url ?? "",
217
+ snippet: (r.content ?? "").slice(0, 300),
218
+ engine: r.engine ?? "unknown",
219
+ }));
220
+ const text = JSON.stringify({ results }, null, 2);
221
+ return {
222
+ content: [{ type: "text", text }],
223
+ details: { query, results, total: response.number_of_results ?? 0 },
224
+ };
225
+ }
226
+ catch (error) {
227
+ const err = error;
228
+ const text = JSON.stringify({ error: `Search failed: ${err.message}` });
229
+ return {
230
+ content: [{ type: "text", text }],
231
+ details: { error: err.message },
232
+ };
233
+ }
234
+ },
235
+ };
236
+ }
237
+ //# sourceMappingURL=web-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.js","sourceRoot":"","sources":["../../src/tools/web-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * LLM-based guardrail for elevated tool calls.
3
+ *
4
+ * Before an elevated tool runs, a single LLM call classifies it as ALLOW
5
+ * (safe, execute silently), ASK (risky, confirm with user), or BLOCK
6
+ * (dangerous, reject outright).
7
+ *
8
+ * Replaces static allowlists with contextual understanding: the model knows
9
+ * that `rm -rf node_modules` is cleanup but `rm -rf /` is destruction.
10
+ *
11
+ * The guardrail only sees tool name, arguments, and the last user message.
12
+ * It never sees the full conversation context, so prompt injection in prior
13
+ * turns can't influence the safety check. If the LLM call fails for any
14
+ * reason, it defaults to ASK (fail-safe, not fail-open).
15
+ */
16
+ import type { Config } from "../config.js";
17
+ export type GuardrailVerdict = "ALLOW" | "ASK" | "BLOCK";
18
+ export interface GuardrailResult {
19
+ verdict: GuardrailVerdict;
20
+ /** Short explanation for the user (shown when ASK or BLOCK). */
21
+ reason: string;
22
+ }
23
+ export interface GuardrailInput {
24
+ /** Tool name */
25
+ toolName: string;
26
+ /** Tool arguments as a JSON string or key-value description */
27
+ args: string;
28
+ /** The last user message (for context on what was requested) */
29
+ userMessage?: string;
30
+ /** Tool description */
31
+ toolDescription?: string;
32
+ }
33
+ declare function buildGuardrailPrompt(input: GuardrailInput): string;
34
+ declare function parseVerdict(response: string): GuardrailResult;
35
+ /**
36
+ * Evaluate a tool call through the LLM guardrail.
37
+ *
38
+ * The prompt is deliberately narrow: it only receives the tool name,
39
+ * arguments, the tool's own description, and the last user message. The full
40
+ * conversation transcript is never included. This limits the prompt injection
41
+ * surface — a malicious message earlier in the conversation cannot reach the
42
+ * guardrail and influence the safety verdict.
43
+ *
44
+ * Fail-safe default: any LLM failure (network error, parse error, no model
45
+ * available) falls back to ASK rather than ALLOW. The guardrail must never
46
+ * fail open. An operator who wants fewer interruptions should tune the
47
+ * GUARDRAIL_SYSTEM_PROMPT guidelines, not weaken the error path.
48
+ *
49
+ * TODO: the guardrail currently uses the same primary model as the main agent.
50
+ * A smaller, faster model (Claude Haiku, GPT-4o-mini) would be more
51
+ * appropriate here: the classification task is simple, latency matters, and
52
+ * cost adds up on every elevated tool call. Consider adding a
53
+ * `guardrail_model` config key analogous to `reflection_model`.
54
+ */
55
+ export declare function evaluateToolCall(config: Config, input: GuardrailInput): Promise<GuardrailResult>;
56
+ /**
57
+ * For testing: evaluate with a custom function instead of LLM.
58
+ */
59
+ export { buildGuardrailPrompt, parseVerdict };
60
+ //# sourceMappingURL=guardrail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrail.d.ts","sourceRoot":"","sources":["../../src/trust/guardrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAsCD,iBAAS,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAU3D;AAMD,iBAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAuBvD;AAkBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CAiD1B;AAED;;GAEG;AACH,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * LLM-based guardrail for elevated tool calls.
3
+ *
4
+ * Before an elevated tool runs, a single LLM call classifies it as ALLOW
5
+ * (safe, execute silently), ASK (risky, confirm with user), or BLOCK
6
+ * (dangerous, reject outright).
7
+ *
8
+ * Replaces static allowlists with contextual understanding: the model knows
9
+ * that `rm -rf node_modules` is cleanup but `rm -rf /` is destruction.
10
+ *
11
+ * The guardrail only sees tool name, arguments, and the last user message.
12
+ * It never sees the full conversation context, so prompt injection in prior
13
+ * turns can't influence the safety check. If the LLM call fails for any
14
+ * reason, it defaults to ASK (fail-safe, not fail-open).
15
+ */
16
+ import { completeSimple } from "@mariozechner/pi-ai";
17
+ import { createModelInfra, resolveFirstModel } from "../model-infra.js";
18
+ // ---------------------------------------------------------------------------
19
+ // Prompt
20
+ // ---------------------------------------------------------------------------
21
+ const GUARDRAIL_SYSTEM_PROMPT = `You are a security guardrail for an AI agent. Your job is to evaluate whether a tool call is safe to execute.
22
+
23
+ You will receive:
24
+ - The tool name and its arguments
25
+ - The last thing the user asked the agent to do
26
+ - A description of what the tool does
27
+
28
+ Classify the action as one of:
29
+ - ALLOW: Safe to execute. Routine operations, reads, harmless commands, actions that clearly match what the user asked for.
30
+ - ASK: Potentially risky. Destructive operations (delete, overwrite), network access to unknown/suspicious destinations, actions that seem disproportionate to what the user asked, or anything you're unsure about.
31
+ - BLOCK: Clearly dangerous. Data destruction (rm -rf /), exfiltration attempts (curl to unknown servers piping to bash), credential theft, or actions that no reasonable user would intend.
32
+
33
+ Guidelines:
34
+ - Be practical, not paranoid. Most tool calls are fine. Users expect their agent to run commands.
35
+ - Reading files, listing directories, checking system status: ALLOW
36
+ - Writing/creating files in the agent's workspace: ALLOW
37
+ - Installing packages, running build commands: ALLOW
38
+ - Deleting files the user mentioned: ASK (confirm scope)
39
+ - Network requests to well-known APIs (weather, search): ALLOW
40
+ - Network requests to unknown URLs: ASK
41
+ - Piping curl output to bash/sh/eval: BLOCK
42
+ - Any command with sudo, chmod 777, or touching system files: ASK
43
+ - If the tool call clearly matches what the user just asked for: lean ALLOW
44
+
45
+ Respond with EXACTLY one line in this format:
46
+ VERDICT: reason
47
+
48
+ Examples:
49
+ ALLOW: listing directory contents as requested
50
+ ASK: deleting files outside the workspace directory
51
+ BLOCK: piping untrusted URL content to shell execution`;
52
+ function buildGuardrailPrompt(input) {
53
+ const parts = [`Tool: ${input.toolName}`];
54
+ if (input.toolDescription) {
55
+ parts.push(`Description: ${input.toolDescription}`);
56
+ }
57
+ parts.push(`Arguments: ${input.args}`);
58
+ if (input.userMessage) {
59
+ parts.push(`User's last message: "${input.userMessage}"`);
60
+ }
61
+ return parts.join("\n");
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Parse response
65
+ // ---------------------------------------------------------------------------
66
+ function parseVerdict(response) {
67
+ const lines = response.trim().split("\n");
68
+ // Scan all lines for "VERDICT: reason" pattern (models sometimes prefix with explanation)
69
+ for (const raw of lines) {
70
+ const line = raw.trim();
71
+ const match = line.match(/^(ALLOW|ASK|BLOCK):\s*(.+)$/i);
72
+ if (match) {
73
+ return {
74
+ verdict: match[1].toUpperCase(),
75
+ reason: match[2].trim(),
76
+ };
77
+ }
78
+ }
79
+ // Fallback: look for the verdict word anywhere in the response
80
+ const upper = response.toUpperCase();
81
+ if (upper.includes("BLOCK"))
82
+ return { verdict: "BLOCK", reason: lines[0].trim() };
83
+ if (upper.includes("ALLOW"))
84
+ return { verdict: "ALLOW", reason: lines[0].trim() };
85
+ if (upper.includes("ASK"))
86
+ return { verdict: "ASK", reason: lines[0].trim() };
87
+ // Unparseable: fail safe
88
+ return { verdict: "ASK", reason: `Guardrail returned unparseable response: ${lines[0].trim().slice(0, 100)}` };
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // Model resolution
92
+ // ---------------------------------------------------------------------------
93
+ let cachedInfra = null;
94
+ function getModelInfra(config) {
95
+ if (cachedInfra)
96
+ return cachedInfra;
97
+ cachedInfra = createModelInfra(config);
98
+ return cachedInfra;
99
+ }
100
+ // ---------------------------------------------------------------------------
101
+ // Public API
102
+ // ---------------------------------------------------------------------------
103
+ /**
104
+ * Evaluate a tool call through the LLM guardrail.
105
+ *
106
+ * The prompt is deliberately narrow: it only receives the tool name,
107
+ * arguments, the tool's own description, and the last user message. The full
108
+ * conversation transcript is never included. This limits the prompt injection
109
+ * surface — a malicious message earlier in the conversation cannot reach the
110
+ * guardrail and influence the safety verdict.
111
+ *
112
+ * Fail-safe default: any LLM failure (network error, parse error, no model
113
+ * available) falls back to ASK rather than ALLOW. The guardrail must never
114
+ * fail open. An operator who wants fewer interruptions should tune the
115
+ * GUARDRAIL_SYSTEM_PROMPT guidelines, not weaken the error path.
116
+ *
117
+ * TODO: the guardrail currently uses the same primary model as the main agent.
118
+ * A smaller, faster model (Claude Haiku, GPT-4o-mini) would be more
119
+ * appropriate here: the classification task is simple, latency matters, and
120
+ * cost adds up on every elevated tool call. Consider adding a
121
+ * `guardrail_model` config key analogous to `reflection_model`.
122
+ */
123
+ export async function evaluateToolCall(config, input) {
124
+ const { modelRegistry } = getModelInfra(config);
125
+ // Use the primary model for guardrail evaluation. This is a security boundary;
126
+ // reliability matters more than cost. The prompt is tiny (~300 tokens in, ~20 out).
127
+ const candidates = [
128
+ config.agent.model,
129
+ ...(config.agent.fallback_models ?? []),
130
+ ];
131
+ const model = resolveFirstModel(candidates, modelRegistry);
132
+ if (!model) {
133
+ return { verdict: "ASK", reason: "No model available for guardrail evaluation." };
134
+ }
135
+ const userPrompt = buildGuardrailPrompt(input);
136
+ try {
137
+ const apiKey = await modelRegistry.getApiKey(model);
138
+ const result = await completeSimple(model, {
139
+ systemPrompt: GUARDRAIL_SYSTEM_PROMPT,
140
+ messages: [{
141
+ role: "user",
142
+ content: userPrompt,
143
+ timestamp: Date.now(),
144
+ }],
145
+ }, {
146
+ maxTokens: 100,
147
+ temperature: 0,
148
+ apiKey: apiKey ?? undefined,
149
+ });
150
+ if (result.stopReason === "error") {
151
+ console.warn(`[guardrail] LLM error: ${result.errorMessage ?? "unknown"}`);
152
+ return { verdict: "ASK", reason: "Guardrail evaluation failed; asking for safety." };
153
+ }
154
+ const text = result.content
155
+ .filter((c) => c.type === "text")
156
+ .map((c) => c.type === "text" ? c.text : "")
157
+ .join("");
158
+ const verdict = parseVerdict(text);
159
+ console.log(`[guardrail] ${input.toolName}: ${verdict.verdict} — ${verdict.reason}`);
160
+ return verdict;
161
+ }
162
+ catch (err) {
163
+ console.warn(`[guardrail] LLM call failed: ${err.message}`);
164
+ return { verdict: "ASK", reason: "Guardrail evaluation failed; asking for safety." };
165
+ }
166
+ }
167
+ /**
168
+ * For testing: evaluate with a custom function instead of LLM.
169
+ */
170
+ export { buildGuardrailPrompt, parseVerdict };
171
+ //# sourceMappingURL=guardrail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrail.js","sourceRoot":"","sources":["../../src/trust/guardrail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Trust subsystem: capability taxonomy, approval store, and guardrail checks.
3
+ *
4
+ * Re-exports from:
5
+ * - store.ts: TrustStore, capability registry, permission checks
6
+ * - wrapper.ts: tool wrapping with trust + guardrail checks
7
+ * - guardrail.ts: LLM-based safety evaluation
8
+ */
9
+ export { type CapabilityLevel, type Capability, type ToolCapabilities, type TrustStore, createTrustStore, registerToolCapabilities, getToolCapabilities, type PermissionCheckResult, checkPermission, setPendingApproval, checkPendingApproval, isAlwaysApproval, } from "./store.js";
10
+ export { type ApprovalCallback, type TrustWrapperContext, wrapToolWithTrustCheck, wrapToolsWithTrustChecks, } from "./wrapper.js";
11
+ export { type GuardrailVerdict, type GuardrailResult, type GuardrailInput, evaluateToolCall, } from "./guardrail.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/trust/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Trust subsystem: capability taxonomy, approval store, and guardrail checks.
3
+ *
4
+ * Re-exports from:
5
+ * - store.ts: TrustStore, capability registry, permission checks
6
+ * - wrapper.ts: tool wrapping with trust + guardrail checks
7
+ * - guardrail.ts: LLM-based safety evaluation
8
+ */
9
+ export { createTrustStore, registerToolCapabilities, getToolCapabilities, checkPermission, setPendingApproval, checkPendingApproval, isAlwaysApproval, } from "./store.js";
10
+ export { wrapToolWithTrustCheck, wrapToolsWithTrustChecks, } from "./wrapper.js";
11
+ export { evaluateToolCall, } from "./guardrail.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trust/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,EAEnB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAIL,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}