@roxy-agent/agents 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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +306 -0
  3. package/dist/approvals.js +143 -0
  4. package/dist/approvals.js.map +1 -0
  5. package/dist/classifier.js +436 -0
  6. package/dist/classifier.js.map +1 -0
  7. package/dist/dashboard/client.js +2057 -0
  8. package/dist/dashboard/client.js.map +1 -0
  9. package/dist/dashboard/html.js +57 -0
  10. package/dist/dashboard/html.js.map +1 -0
  11. package/dist/dashboard/icons.js +18 -0
  12. package/dist/dashboard/icons.js.map +1 -0
  13. package/dist/dashboard/server.js +423 -0
  14. package/dist/dashboard/server.js.map +1 -0
  15. package/dist/dashboard/styles.js +1685 -0
  16. package/dist/dashboard/styles.js.map +1 -0
  17. package/dist/dashboard.js +2 -0
  18. package/dist/dashboard.js.map +1 -0
  19. package/dist/db.js +526 -0
  20. package/dist/db.js.map +1 -0
  21. package/dist/index.js +94 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/license.js +257 -0
  24. package/dist/license.js.map +1 -0
  25. package/dist/logger.js +44 -0
  26. package/dist/logger.js.map +1 -0
  27. package/dist/ml/bash-classifier.js +121 -0
  28. package/dist/ml/bash-classifier.js.map +1 -0
  29. package/dist/ml/embedder.js +79 -0
  30. package/dist/ml/embedder.js.map +1 -0
  31. package/dist/ml/prototypes.js +707 -0
  32. package/dist/ml/prototypes.js.map +1 -0
  33. package/dist/policies.js +289 -0
  34. package/dist/policies.js.map +1 -0
  35. package/dist/slack.js +149 -0
  36. package/dist/slack.js.map +1 -0
  37. package/dist/tools/bash.js +134 -0
  38. package/dist/tools/bash.js.map +1 -0
  39. package/dist/tools/conversation.js +36 -0
  40. package/dist/tools/conversation.js.map +1 -0
  41. package/dist/tools/filesystem.js +243 -0
  42. package/dist/tools/filesystem.js.map +1 -0
  43. package/dist/tools/introspect.js +187 -0
  44. package/dist/tools/introspect.js.map +1 -0
  45. package/dist/tools/network.js +152 -0
  46. package/dist/tools/network.js.map +1 -0
  47. package/dist/tools/policies.js +107 -0
  48. package/dist/tools/policies.js.map +1 -0
  49. package/package.json +61 -0
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { logEvent } from "../logger.js";
3
+ // ---------------------------------------------------------------------------
4
+ // audit_message — persist chat turns in the same SQLite audit stream as tools
5
+ // ---------------------------------------------------------------------------
6
+ export const auditMessageToolDefinition = {
7
+ name: "audit_message",
8
+ description: "Record a user or assistant message in the local audit database (dashboard + recent_events). Cursor does not send chat through MCP by default — only tool calls hit this server — so prompts are NOT logged unless you call this tool or use POST /api/chat/log from a Cursor hook. Use when the user wants the conversation visible next to bash/filesystem/network events.",
9
+ schema: z.object({
10
+ role: z.enum(["user", "assistant"]).describe("Who said this line."),
11
+ text: z
12
+ .string()
13
+ .min(1)
14
+ .describe("Message body to store (long text is truncated for storage)."),
15
+ }),
16
+ };
17
+ export function auditMessage(args, session_id) {
18
+ const id = logChatTurn(session_id, args.role, args.text);
19
+ return { success: true, event_id: id };
20
+ }
21
+ /** Shared by MCP tool and dashboard HTTP ingest */
22
+ export function logChatTurn(session_id, role, text) {
23
+ const preview = text.length > 2000 ? text.slice(0, 1997) + "…" : text;
24
+ return logEvent({
25
+ session_id,
26
+ tool: "chat",
27
+ action_type: "message",
28
+ payload: { role, text: preview },
29
+ risk_level: "low",
30
+ decision: "allowed",
31
+ reason: `${role} message`,
32
+ result: "logged",
33
+ duration_ms: 0,
34
+ });
35
+ }
36
+ //# sourceMappingURL=conversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/tools/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAE9E,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,6WAA6W;IAC/W,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACnE,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CAAC,6DAA6D,CAAC;KAC3E,CAAC;CACH,CAAC;AAIF,MAAM,UAAU,YAAY,CAC1B,IAAsB,EACtB,UAAkB;IAElB,MAAM,EAAE,GAAG,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AACzC,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,IAA0B,EAC1B,IAAY;IAEZ,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACtE,OAAO,QAAQ,CAAC;QACd,UAAU;QACV,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;QAChC,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,GAAG,IAAI,UAAU;QACzB,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,CAAC;KACf,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,243 @@
1
+ import { z } from "zod";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { classifyFilePathAsync } from "../classifier.js";
5
+ import { logEvent, updateEventResult } from "../logger.js";
6
+ import { awaitApproval } from "../approvals.js";
7
+ async function gateFilesystem(classification, event_id, args, session_id) {
8
+ if (classification.decision === "denied") {
9
+ updateEventResult(event_id, "blocked by classifier", 0);
10
+ return {
11
+ proceed: false,
12
+ result: {
13
+ success: false,
14
+ blocked: true,
15
+ reason: classification.reason,
16
+ event_id,
17
+ },
18
+ };
19
+ }
20
+ if (classification.decision === "pending_approval") {
21
+ const verdict = await awaitApproval({
22
+ event_id,
23
+ tool: "filesystem",
24
+ summary: `${args.op} ${args.path}`,
25
+ reason: classification.reason,
26
+ payload: { path: args.path, operation: args.op },
27
+ session_id,
28
+ });
29
+ if (!verdict.approved) {
30
+ updateEventResult(event_id, verdict.reason, 0);
31
+ return {
32
+ proceed: false,
33
+ result: {
34
+ success: false,
35
+ blocked: true,
36
+ reason: verdict.reason,
37
+ event_id,
38
+ },
39
+ };
40
+ }
41
+ }
42
+ return { proceed: true };
43
+ }
44
+ // Cap the size of a single read so we never return multi-GB blobs through
45
+ // the MCP transport.
46
+ const MAX_READ_BYTES = 5 * 1024 * 1024; // 5 MB
47
+ export const readFileToolDefinition = {
48
+ name: "read_file",
49
+ description: "Read the contents of a file as UTF-8. Logged to the audit DB. Sensitive paths (e.g. ~/.ssh, .env) are flagged.",
50
+ schema: z.object({
51
+ path: z.string().min(1).describe("Absolute or relative path to the file."),
52
+ max_bytes: z
53
+ .number()
54
+ .int()
55
+ .positive()
56
+ .max(MAX_READ_BYTES)
57
+ .optional()
58
+ .describe(`Max bytes to read. Default ${MAX_READ_BYTES}.`),
59
+ }),
60
+ };
61
+ export const writeFileToolDefinition = {
62
+ name: "write_file",
63
+ description: "Write content to a file (creating parent dirs as needed). Logged. Writes to sensitive paths are denied.",
64
+ schema: z.object({
65
+ path: z.string().min(1),
66
+ content: z.string(),
67
+ append: z
68
+ .boolean()
69
+ .optional()
70
+ .default(false)
71
+ .describe("If true, append instead of overwrite."),
72
+ }),
73
+ };
74
+ export const deleteFileToolDefinition = {
75
+ name: "delete_file",
76
+ description: "Delete a file. Always flagged as high risk and logged.",
77
+ schema: z.object({
78
+ path: z.string().min(1),
79
+ }),
80
+ };
81
+ export const listDirectoryToolDefinition = {
82
+ name: "list_directory",
83
+ description: "List entries in a directory (non-recursive). Logged.",
84
+ schema: z.object({
85
+ path: z.string().min(1),
86
+ }),
87
+ };
88
+ export async function readFile(args, session_id) {
89
+ const classification = await classifyFilePathAsync(args.path, "read");
90
+ const max = args.max_bytes ?? MAX_READ_BYTES;
91
+ const event_id = logEvent({
92
+ session_id,
93
+ tool: "filesystem",
94
+ action_type: "read",
95
+ payload: { path: args.path, max_bytes: max },
96
+ risk_level: classification.risk_level,
97
+ decision: classification.decision,
98
+ reason: classification.reason,
99
+ });
100
+ const gate = await gateFilesystem(classification, event_id, { path: args.path, op: "read" }, session_id);
101
+ if (!gate.proceed)
102
+ return gate.result;
103
+ const start = Date.now();
104
+ try {
105
+ const buf = await fs.readFile(args.path);
106
+ const truncated = buf.length > max;
107
+ const slice = truncated ? buf.subarray(0, max) : buf;
108
+ const content = slice.toString("utf-8");
109
+ updateEventResult(event_id, `read ${slice.length}/${buf.length} bytes${truncated ? " (truncated)" : ""}`, Date.now() - start);
110
+ return {
111
+ success: true,
112
+ content,
113
+ bytes_read: slice.length,
114
+ total_bytes: buf.length,
115
+ truncated,
116
+ event_id,
117
+ flagged: classification.decision === "flagged",
118
+ flag_reason: classification.decision === "flagged"
119
+ ? classification.reason
120
+ : undefined,
121
+ };
122
+ }
123
+ catch (err) {
124
+ const e = err;
125
+ updateEventResult(event_id, "", Date.now() - start, e.message);
126
+ return { success: false, error: e.message, event_id };
127
+ }
128
+ }
129
+ export async function writeFile(args, session_id) {
130
+ const classification = await classifyFilePathAsync(args.path, "write");
131
+ const event_id = logEvent({
132
+ session_id,
133
+ tool: "filesystem",
134
+ action_type: args.append ? "append" : "write",
135
+ payload: {
136
+ path: args.path,
137
+ content_length: args.content.length,
138
+ append: args.append,
139
+ },
140
+ risk_level: classification.risk_level,
141
+ decision: classification.decision,
142
+ reason: classification.reason,
143
+ });
144
+ const gate = await gateFilesystem(classification, event_id, { path: args.path, op: args.append ? "append" : "write" }, session_id);
145
+ if (!gate.proceed)
146
+ return gate.result;
147
+ const start = Date.now();
148
+ try {
149
+ const dir = path.dirname(args.path);
150
+ if (dir && dir !== "." && dir !== "/") {
151
+ await fs.mkdir(dir, { recursive: true });
152
+ }
153
+ if (args.append) {
154
+ await fs.appendFile(args.path, args.content, "utf-8");
155
+ }
156
+ else {
157
+ await fs.writeFile(args.path, args.content, "utf-8");
158
+ }
159
+ updateEventResult(event_id, `${args.append ? "appended" : "wrote"} ${args.content.length} bytes`, Date.now() - start);
160
+ return {
161
+ success: true,
162
+ bytes_written: args.content.length,
163
+ event_id,
164
+ flagged: classification.decision === "flagged",
165
+ flag_reason: classification.decision === "flagged"
166
+ ? classification.reason
167
+ : undefined,
168
+ };
169
+ }
170
+ catch (err) {
171
+ const e = err;
172
+ updateEventResult(event_id, "", Date.now() - start, e.message);
173
+ return { success: false, error: e.message, event_id };
174
+ }
175
+ }
176
+ export async function deleteFile(args, session_id) {
177
+ const classification = await classifyFilePathAsync(args.path, "delete");
178
+ const event_id = logEvent({
179
+ session_id,
180
+ tool: "filesystem",
181
+ action_type: "delete",
182
+ payload: { path: args.path },
183
+ risk_level: classification.risk_level,
184
+ decision: classification.decision,
185
+ reason: classification.reason,
186
+ });
187
+ const gate = await gateFilesystem(classification, event_id, { path: args.path, op: "delete" }, session_id);
188
+ if (!gate.proceed)
189
+ return gate.result;
190
+ const start = Date.now();
191
+ try {
192
+ await fs.unlink(args.path);
193
+ updateEventResult(event_id, "deleted", Date.now() - start);
194
+ return {
195
+ success: true,
196
+ event_id,
197
+ flagged: true,
198
+ flag_reason: classification.reason,
199
+ };
200
+ }
201
+ catch (err) {
202
+ const e = err;
203
+ updateEventResult(event_id, "", Date.now() - start, e.message);
204
+ return { success: false, error: e.message, event_id };
205
+ }
206
+ }
207
+ export async function listDirectory(args, session_id) {
208
+ const classification = await classifyFilePathAsync(args.path, "list");
209
+ const event_id = logEvent({
210
+ session_id,
211
+ tool: "filesystem",
212
+ action_type: "list",
213
+ payload: { path: args.path },
214
+ risk_level: classification.risk_level,
215
+ decision: classification.decision,
216
+ reason: classification.reason,
217
+ });
218
+ const gate = await gateFilesystem(classification, event_id, { path: args.path, op: "list" }, session_id);
219
+ if (!gate.proceed)
220
+ return gate.result;
221
+ const start = Date.now();
222
+ try {
223
+ const entries = await fs.readdir(args.path, { withFileTypes: true });
224
+ const items = entries.map((e) => ({
225
+ name: e.name,
226
+ type: e.isDirectory()
227
+ ? "dir"
228
+ : e.isSymbolicLink()
229
+ ? "symlink"
230
+ : e.isFile()
231
+ ? "file"
232
+ : "other",
233
+ }));
234
+ updateEventResult(event_id, `${items.length} entries`, Date.now() - start);
235
+ return { success: true, entries: items, event_id };
236
+ }
237
+ catch (err) {
238
+ const e = err;
239
+ updateEventResult(event_id, "", Date.now() - start, e.message);
240
+ return { success: false, error: e.message, event_id };
241
+ }
242
+ }
243
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/tools/filesystem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,187 @@
1
+ // Introspection tools — let the agent query its own audit trail and
2
+ // pre-flight-check actions before committing to them. These tools are
3
+ // read-only and never produce side effects, so they are not themselves
4
+ // audited.
5
+ import { z } from "zod";
6
+ import { classifyBashCommandAsync, classifyFilePathAsync, classifyNetworkRequestAsync, } from "../classifier.js";
7
+ import { getRecentEvents, getEvent, getStats, getSessionEvents, } from "../db.js";
8
+ import { isBashMLReady } from "../ml/bash-classifier.js";
9
+ // ---------------------------------------------------------------------------
10
+ // classify — pre-flight risk check (no side effects)
11
+ // ---------------------------------------------------------------------------
12
+ export const classifyToolDefinition = {
13
+ name: "classify",
14
+ description: "Pre-flight risk check. Returns the proxy's risk classification (low/medium/high) and decision (allowed/flagged/denied) for a hypothetical bash command, filesystem path, or network URL — WITHOUT executing it. Use this to preview whether a planned action would be blocked or flagged before committing to it. Especially useful when the user asks you to do something that might be destructive: classify it first, then explain the verdict.",
15
+ schema: z.object({
16
+ tool: z
17
+ .enum(["bash", "filesystem", "network"])
18
+ .describe("Which tool the action would run through: 'bash', 'filesystem', or 'network'."),
19
+ command: z
20
+ .string()
21
+ .optional()
22
+ .describe("Bash command to classify (when tool='bash')."),
23
+ path: z
24
+ .string()
25
+ .optional()
26
+ .describe("Filesystem path to classify (when tool='filesystem')."),
27
+ operation: z
28
+ .enum(["read", "write", "delete", "list"])
29
+ .optional()
30
+ .describe("Filesystem operation (when tool='filesystem'). Defaults to 'read'."),
31
+ url: z
32
+ .string()
33
+ .optional()
34
+ .describe("URL to classify (when tool='network')."),
35
+ method: z
36
+ .string()
37
+ .optional()
38
+ .describe("HTTP method (when tool='network'). Defaults to 'GET'."),
39
+ }),
40
+ };
41
+ export async function classify(args) {
42
+ let cls;
43
+ switch (args.tool) {
44
+ case "bash": {
45
+ if (!args.command)
46
+ throw new Error("`command` is required when tool='bash'");
47
+ cls = await classifyBashCommandAsync(args.command);
48
+ break;
49
+ }
50
+ case "filesystem": {
51
+ if (!args.path)
52
+ throw new Error("`path` is required when tool='filesystem'");
53
+ cls = await classifyFilePathAsync(args.path, args.operation ?? "read");
54
+ break;
55
+ }
56
+ case "network": {
57
+ if (!args.url)
58
+ throw new Error("`url` is required when tool='network'");
59
+ cls = await classifyNetworkRequestAsync(args.url, args.method ?? "GET");
60
+ break;
61
+ }
62
+ }
63
+ const would_be = cls.decision === "denied"
64
+ ? "blocked"
65
+ : cls.decision === "flagged"
66
+ ? "executed_with_flag"
67
+ : "executed";
68
+ return {
69
+ ...cls,
70
+ tool: args.tool,
71
+ ml_ready: isBashMLReady(),
72
+ would_be,
73
+ };
74
+ }
75
+ // ---------------------------------------------------------------------------
76
+ // recent_events — read the audit trail
77
+ // ---------------------------------------------------------------------------
78
+ export const recentEventsToolDefinition = {
79
+ name: "recent_events",
80
+ description: "Return the most recent audited tool calls. Use this to answer 'what did I just do?', summarize a session, or investigate a flagged event. Optional filters narrow by tool, decision, or session. The current session's events are most useful for self-audit.",
81
+ schema: z.object({
82
+ limit: z
83
+ .number()
84
+ .int()
85
+ .positive()
86
+ .max(500)
87
+ .optional()
88
+ .describe("Max events to return (default 25, max 500)."),
89
+ tool: z
90
+ .enum(["bash", "filesystem", "network"])
91
+ .optional()
92
+ .describe("Only return events for this tool."),
93
+ decision: z
94
+ .enum(["allowed", "flagged", "denied"])
95
+ .optional()
96
+ .describe("Only return events with this decision."),
97
+ session_id: z
98
+ .string()
99
+ .optional()
100
+ .describe("Only return events from this session. Pass 'current' to use the proxy server's current session."),
101
+ event_id: z
102
+ .number()
103
+ .int()
104
+ .positive()
105
+ .optional()
106
+ .describe("Fetch a single event by id. Overrides other filters."),
107
+ }),
108
+ };
109
+ function summarizeEvent(e) {
110
+ let payload = {};
111
+ try {
112
+ payload = JSON.parse(e.payload);
113
+ }
114
+ catch {
115
+ /* ignore */
116
+ }
117
+ if (e.tool === "bash" && payload.command)
118
+ return payload.command;
119
+ if (e.tool === "filesystem" && payload.path) {
120
+ return `${e.action_type} ${payload.path}`;
121
+ }
122
+ if (e.tool === "network" && payload.url)
123
+ return payload.url;
124
+ return e.action_type;
125
+ }
126
+ function toSlim(e) {
127
+ let payload = e.payload;
128
+ try {
129
+ payload = JSON.parse(e.payload);
130
+ }
131
+ catch {
132
+ /* keep raw */
133
+ }
134
+ return {
135
+ id: e.id,
136
+ timestamp: e.timestamp,
137
+ session_id: e.session_id,
138
+ tool: e.tool,
139
+ action_type: e.action_type,
140
+ summary: summarizeEvent(e),
141
+ payload,
142
+ risk_level: e.risk_level,
143
+ decision: e.decision,
144
+ reason: e.reason,
145
+ duration_ms: e.duration_ms,
146
+ error: e.error,
147
+ result: e.result,
148
+ };
149
+ }
150
+ export function recentEvents(args, current_session_id) {
151
+ if (args.event_id) {
152
+ const e = getEvent(args.event_id);
153
+ return { events: e ? [toSlim(e)] : [], count: e ? 1 : 0 };
154
+ }
155
+ const limit = args.limit ?? 25;
156
+ let rows;
157
+ const session = args.session_id === "current" ? current_session_id : args.session_id;
158
+ if (session) {
159
+ rows = getSessionEvents(session).slice(-limit).reverse();
160
+ }
161
+ else {
162
+ rows = getRecentEvents(Math.max(limit, 200));
163
+ }
164
+ if (args.tool)
165
+ rows = rows.filter((r) => r.tool === args.tool);
166
+ if (args.decision)
167
+ rows = rows.filter((r) => r.decision === args.decision);
168
+ rows = rows.slice(0, limit);
169
+ return { events: rows.map(toSlim), count: rows.length };
170
+ }
171
+ // ---------------------------------------------------------------------------
172
+ // proxy_stats — heartbeat / aggregate counters
173
+ // ---------------------------------------------------------------------------
174
+ export const proxyStatsToolDefinition = {
175
+ name: "proxy_stats",
176
+ description: "Return aggregate audit counters and proxy health (total events, decisions breakdown, ML readiness, current session). Useful as a heartbeat or to summarize the audit log at a glance.",
177
+ schema: z.object({}),
178
+ };
179
+ export function proxyStats(current_session_id, dashboard_port) {
180
+ return {
181
+ current_session_id,
182
+ ml_ready: isBashMLReady(),
183
+ dashboard_url: `http://localhost:${dashboard_port}`,
184
+ totals: getStats(),
185
+ };
186
+ }
187
+ //# sourceMappingURL=introspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/tools/introspect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ import { z } from "zod";
2
+ import { classifyNetworkRequestAsync } from "../classifier.js";
3
+ import { logEvent, updateEventResult } from "../logger.js";
4
+ import { awaitApproval } from "../approvals.js";
5
+ const MAX_BODY_BYTES = 2 * 1024 * 1024; // 2 MB
6
+ const DEFAULT_TIMEOUT_MS = 30_000;
7
+ // Headers that should never be echoed back to the agent or stored in the
8
+ // audit DB unredacted.
9
+ const SENSITIVE_HEADER_NAMES = new Set([
10
+ "authorization",
11
+ "proxy-authorization",
12
+ "cookie",
13
+ "set-cookie",
14
+ "x-api-key",
15
+ "x-auth-token",
16
+ ]);
17
+ export const networkToolDefinition = {
18
+ name: "fetch_url",
19
+ description: "Make an HTTP(S) request to a URL. All requests are risk-classified and logged. External URLs and mutating methods are flagged. Fetching executable scripts is denied.",
20
+ schema: z.object({
21
+ url: z.string().url(),
22
+ method: z
23
+ .enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"])
24
+ .optional()
25
+ .default("GET"),
26
+ headers: z.record(z.string(), z.string()).optional(),
27
+ body: z.string().optional(),
28
+ timeout_ms: z
29
+ .number()
30
+ .int()
31
+ .positive()
32
+ .max(5 * 60 * 1000)
33
+ .optional()
34
+ .describe(`Timeout in ms (default ${DEFAULT_TIMEOUT_MS}).`),
35
+ }),
36
+ };
37
+ export async function fetchUrl(args, session_id) {
38
+ const method = args.method ?? "GET";
39
+ const classification = await classifyNetworkRequestAsync(args.url, method);
40
+ const timeout = args.timeout_ms ?? DEFAULT_TIMEOUT_MS;
41
+ const event_id = logEvent({
42
+ session_id,
43
+ tool: "network",
44
+ action_type: `http_${method.toLowerCase()}`,
45
+ payload: {
46
+ url: args.url,
47
+ method,
48
+ has_body: !!args.body,
49
+ header_names: args.headers ? redactHeaderNames(args.headers) : [],
50
+ timeout_ms: timeout,
51
+ },
52
+ risk_level: classification.risk_level,
53
+ decision: classification.decision,
54
+ reason: classification.reason,
55
+ });
56
+ if (classification.decision === "denied") {
57
+ updateEventResult(event_id, "blocked by classifier", 0);
58
+ return {
59
+ success: false,
60
+ blocked: true,
61
+ reason: classification.reason,
62
+ event_id,
63
+ };
64
+ }
65
+ if (classification.decision === "pending_approval") {
66
+ const verdict = await awaitApproval({
67
+ event_id,
68
+ tool: "network",
69
+ summary: `${method} ${args.url}`,
70
+ reason: classification.reason,
71
+ payload: { url: args.url, method },
72
+ session_id,
73
+ });
74
+ if (!verdict.approved) {
75
+ updateEventResult(event_id, verdict.reason, 0);
76
+ return {
77
+ success: false,
78
+ blocked: true,
79
+ reason: verdict.reason,
80
+ event_id,
81
+ };
82
+ }
83
+ }
84
+ const start = Date.now();
85
+ const controller = new AbortController();
86
+ const timer = setTimeout(() => controller.abort(), timeout);
87
+ try {
88
+ const response = await fetch(args.url, {
89
+ method,
90
+ headers: args.headers,
91
+ body: args.body,
92
+ signal: controller.signal,
93
+ redirect: "follow",
94
+ });
95
+ const buf = Buffer.from(await response.arrayBuffer());
96
+ const truncated = buf.length > MAX_BODY_BYTES;
97
+ const slice = truncated ? buf.subarray(0, MAX_BODY_BYTES) : buf;
98
+ const text = safeUtf8(slice);
99
+ const duration_ms = Date.now() - start;
100
+ updateEventResult(event_id, `${response.status} ${response.statusText} — ${buf.length}B${truncated ? " (truncated)" : ""}`, duration_ms);
101
+ return {
102
+ success: response.ok,
103
+ status: response.status,
104
+ status_text: response.statusText,
105
+ headers: redactResponseHeaders(response.headers),
106
+ body: text,
107
+ bytes_received: buf.length,
108
+ truncated,
109
+ duration_ms,
110
+ event_id,
111
+ flagged: classification.decision === "flagged",
112
+ flag_reason: classification.decision === "flagged"
113
+ ? classification.reason
114
+ : undefined,
115
+ };
116
+ }
117
+ catch (err) {
118
+ const duration_ms = Date.now() - start;
119
+ const e = err;
120
+ const msg = controller.signal.aborted && e.name === "AbortError"
121
+ ? `timeout after ${timeout}ms`
122
+ : e.message;
123
+ updateEventResult(event_id, "", duration_ms, msg);
124
+ return { success: false, error: msg, duration_ms, event_id };
125
+ }
126
+ finally {
127
+ clearTimeout(timer);
128
+ }
129
+ }
130
+ function redactHeaderNames(headers) {
131
+ return Object.keys(headers).map((name) => SENSITIVE_HEADER_NAMES.has(name.toLowerCase())
132
+ ? `${name} (redacted)`
133
+ : name);
134
+ }
135
+ function redactResponseHeaders(headers) {
136
+ const out = {};
137
+ headers.forEach((value, key) => {
138
+ out[key] = SENSITIVE_HEADER_NAMES.has(key.toLowerCase())
139
+ ? "[redacted]"
140
+ : value;
141
+ });
142
+ return out;
143
+ }
144
+ function safeUtf8(buf) {
145
+ try {
146
+ return buf.toString("utf-8");
147
+ }
148
+ catch {
149
+ return `[non-utf8 body, ${buf.length} bytes]`;
150
+ }
151
+ }
152
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/tools/network.ts"],"names":[],"mappings":"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"}