Tales Cunha commited on
Commit
63d8812
·
2 Parent(s): 194f5ac7e6051f

Merge pull request #4 from uandersonricardo/teste_agent

Browse files
.gitignore CHANGED
@@ -141,3 +141,5 @@ dist
141
  vite.config.js.timestamp-*
142
  vite.config.ts.timestamp-*
143
  .vite/
 
 
 
141
  vite.config.js.timestamp-*
142
  vite.config.ts.timestamp-*
143
  .vite/
144
+
145
+
Dockerfile CHANGED
@@ -20,6 +20,14 @@ RUN npm run build
20
  FROM node:22-slim
21
  WORKDIR /app
22
 
 
 
 
 
 
 
 
 
23
  COPY package.json package-lock.json* ./
24
  COPY patches/ ./patches/
25
  RUN npm install --omit=dev --ignore-scripts
@@ -30,4 +38,11 @@ COPY --from=frontend-build /app/frontend/dist ./frontend/dist
30
  ENV PORT=7860
31
  EXPOSE 7860
32
 
 
 
 
 
 
 
 
33
  CMD ["node", "dist/server.js"]
 
20
  FROM node:22-slim
21
  WORKDIR /app
22
 
23
+ # Install Foundry dependencies
24
+ RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/*
25
+
26
+ # Install Foundry
27
+ RUN curl -L https://foundry.paradigm.xyz | bash
28
+ ENV PATH="/root/.foundry/bin:${PATH}"
29
+ RUN foundryup
30
+
31
  COPY package.json package-lock.json* ./
32
  COPY patches/ ./patches/
33
  RUN npm install --omit=dev --ignore-scripts
 
38
  ENV PORT=7860
39
  EXPOSE 7860
40
 
41
+ # Ensure scripts are executable
42
+ COPY scripts/ ./scripts/
43
+ RUN chmod +x scripts/*.sh
44
+
45
+ # Run sandbox setup once during image build to cache it
46
+ RUN ./scripts/setup-sandbox.sh
47
+
48
  CMD ["node", "dist/server.js"]
frontend/src/App.tsx CHANGED
@@ -20,7 +20,11 @@ interface AgentResult {
20
  compilationErrors?: string[];
21
  reviewSummary?: string;
22
  findings?: Finding[];
23
- results?: unknown[];
 
 
 
 
24
  }
25
 
26
  export function App() {
@@ -60,6 +64,7 @@ export function App() {
60
 
61
  const decoder = new TextDecoder();
62
  let buffer = "";
 
63
 
64
  while (true) {
65
  const { done, value } = await reader.read();
@@ -69,7 +74,6 @@ export function App() {
69
  const lines = buffer.split("\n");
70
  buffer = lines.pop() || "";
71
 
72
- let currentEvent = "";
73
  for (const line of lines) {
74
  if (line.startsWith("event:")) {
75
  currentEvent = line.slice(6).trim();
@@ -94,6 +98,7 @@ export function App() {
94
  appendLog(`❌ ERRO: ${data}`);
95
  break;
96
  }
 
97
  }
98
  }
99
  }
@@ -220,13 +225,38 @@ export function App() {
220
  {testerResult && (
221
  <section style={styles.section}>
222
  <h2 style={styles.sectionTitle}>🧪 Agente Tester</h2>
223
- <div style={styles.codeBox}>
224
- <pre style={styles.code}>
225
- {testerResult.results && testerResult.results.length > 0
226
- ? JSON.stringify(testerResult.results, null, 2)
227
- : "Nenhum resultado de teste gerado."}
228
- </pre>
 
 
 
229
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </section>
231
  )}
232
  </div>
 
20
  compilationErrors?: string[];
21
  reviewSummary?: string;
22
  findings?: Finding[];
23
+ // Tester fields
24
+ status?: string;
25
+ pocCode?: string;
26
+ executionLogs?: string[];
27
+ iterations?: number;
28
  }
29
 
30
  export function App() {
 
64
 
65
  const decoder = new TextDecoder();
66
  let buffer = "";
67
+ let currentEvent = "";
68
 
69
  while (true) {
70
  const { done, value } = await reader.read();
 
74
  const lines = buffer.split("\n");
75
  buffer = lines.pop() || "";
76
 
 
77
  for (const line of lines) {
78
  if (line.startsWith("event:")) {
79
  currentEvent = line.slice(6).trim();
 
98
  appendLog(`❌ ERRO: ${data}`);
99
  break;
100
  }
101
+ currentEvent = "";
102
  }
103
  }
104
  }
 
225
  {testerResult && (
226
  <section style={styles.section}>
227
  <h2 style={styles.sectionTitle}>🧪 Agente Tester</h2>
228
+ <div style={styles.resultBox}>
229
+ <p style={styles.resultText}>
230
+ <strong>Status:</strong>{" "}
231
+ <span style={{ color: testerResult.status === "success" ? "#22c55e" : "#ef4444" }}>
232
+ {testerResult.status?.toUpperCase()}
233
+ </span>
234
+ <br />
235
+ <strong>Iterações:</strong> {testerResult.iterations}
236
+ </p>
237
  </div>
238
+
239
+ {testerResult.pocCode && (
240
+ <>
241
+ <h3 style={styles.subTitle}>Proof of Concept (Exploit)</h3>
242
+ <div style={styles.codeBox}>
243
+ <pre style={styles.code}>{testerResult.pocCode}</pre>
244
+ </div>
245
+ </>
246
+ )}
247
+
248
+ {testerResult.executionLogs && testerResult.executionLogs.length > 0 && (
249
+ <>
250
+ <h3 style={styles.subTitle}>Logs de Execução (Foundry)</h3>
251
+ <div style={styles.logBox}>
252
+ {testerResult.executionLogs.map((log, i) => (
253
+ <div key={i} style={styles.logLine}>
254
+ {log}
255
+ </div>
256
+ ))}
257
+ </div>
258
+ </>
259
+ )}
260
  </section>
261
  )}
262
  </div>
package-lock.json CHANGED
@@ -14,7 +14,8 @@
14
  "@langchain/anthropic": "^1.3.29",
15
  "@langchain/core": "^1.1.45",
16
  "@langchain/google-genai": "^2.1.31",
17
- "@langchain/langgraph": "^1.3.0",
 
18
  "@langchain/openrouter": "^0.2.4",
19
  "@solidity-parser/parser": "^0.20.2",
20
  "dotenv": "^17.4.2",
@@ -27,19 +28,21 @@
27
  },
28
  "devDependencies": {
29
  "@biomejs/biome": "2.4.14",
30
- "@types/node": "^25.6.0",
31
  "patch-package": "^8.0.1",
 
32
  "typescript": "^6.0.3",
33
  "vitest": "^4.1.5"
34
  }
35
  },
36
  "node_modules/@anthropic-ai/sdk": {
37
- "version": "0.91.1",
38
- "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz",
39
- "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==",
40
  "license": "MIT",
41
  "dependencies": {
42
- "json-schema-to-ts": "^3.1.1"
 
43
  },
44
  "bin": {
45
  "anthropic-ai-sdk": "bin/cli"
@@ -54,9 +57,9 @@
54
  }
55
  },
56
  "node_modules/@babel/runtime": {
57
- "version": "7.29.2",
58
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
59
- "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
60
  "license": "MIT",
61
  "engines": {
62
  "node": ">=6.9.0"
@@ -64,8 +67,6 @@
64
  },
65
  "node_modules/@biomejs/biome": {
66
  "version": "2.4.14",
67
- "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.14.tgz",
68
- "integrity": "sha512-TmAvxOEgrpLypzVGJ8FulIZnlyA9TxrO1hyqYrCz9r+bwma9xXxuLA5IuYnj55XQneFx460KjRbx6SWGLkg3bQ==",
69
  "dev": true,
70
  "license": "MIT OR Apache-2.0",
71
  "bin": {
@@ -89,78 +90,8 @@
89
  "@biomejs/cli-win32-x64": "2.4.14"
90
  }
91
  },
92
- "node_modules/@biomejs/cli-darwin-arm64": {
93
- "version": "2.4.14",
94
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.14.tgz",
95
- "integrity": "sha512-XvgoE9XOawUOQPdmvs4J7wPhi/DLwSCGks3AlPJDmh34O0awRTqCED1HRcRDdpf1Zrp4us4MGOOdIxNpbqNF5Q==",
96
- "cpu": [
97
- "arm64"
98
- ],
99
- "dev": true,
100
- "license": "MIT OR Apache-2.0",
101
- "optional": true,
102
- "os": [
103
- "darwin"
104
- ],
105
- "engines": {
106
- "node": ">=14.21.3"
107
- }
108
- },
109
- "node_modules/@biomejs/cli-darwin-x64": {
110
- "version": "2.4.14",
111
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.14.tgz",
112
- "integrity": "sha512-jE7hKBCFhOx3uUh+ZkWBfOHxAcILPfhFplNkuID/eZeSTLHzfZzoZxW8fbqY9xXRnPi7jGNAf1iPVR+0yWsM/Q==",
113
- "cpu": [
114
- "x64"
115
- ],
116
- "dev": true,
117
- "license": "MIT OR Apache-2.0",
118
- "optional": true,
119
- "os": [
120
- "darwin"
121
- ],
122
- "engines": {
123
- "node": ">=14.21.3"
124
- }
125
- },
126
- "node_modules/@biomejs/cli-linux-arm64": {
127
- "version": "2.4.14",
128
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.14.tgz",
129
- "integrity": "sha512-2TELhZnW5RSLL063l9rc5xLpA0ZIw0Ccwy/0q384rvNAgFw3yI76bd59547yxowdQr5MNPET/xDLrLuvgSeeWQ==",
130
- "cpu": [
131
- "arm64"
132
- ],
133
- "dev": true,
134
- "license": "MIT OR Apache-2.0",
135
- "optional": true,
136
- "os": [
137
- "linux"
138
- ],
139
- "engines": {
140
- "node": ">=14.21.3"
141
- }
142
- },
143
- "node_modules/@biomejs/cli-linux-arm64-musl": {
144
- "version": "2.4.14",
145
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.14.tgz",
146
- "integrity": "sha512-/z+6gqAqqUQTHazwStxSXKHg9b8UvqBmDFRp+c4wYbq2KXhELQDon9EoC9RpmQ8JWkqQx/lIUy/cs+MhzDZp6A==",
147
- "cpu": [
148
- "arm64"
149
- ],
150
- "dev": true,
151
- "license": "MIT OR Apache-2.0",
152
- "optional": true,
153
- "os": [
154
- "linux"
155
- ],
156
- "engines": {
157
- "node": ">=14.21.3"
158
- }
159
- },
160
  "node_modules/@biomejs/cli-linux-x64": {
161
  "version": "2.4.14",
162
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.14.tgz",
163
- "integrity": "sha512-zHrlQZDBDUz4OLAraYpWKcnLS6HOewBFWYOzY91d1ZjdqZwibOyb6BEu6WuWLugyo0P3riCmsbV9UqV1cSXwQg==",
164
  "cpu": [
165
  "x64"
166
  ],
@@ -176,8 +107,6 @@
176
  },
177
  "node_modules/@biomejs/cli-linux-x64-musl": {
178
  "version": "2.4.14",
179
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.14.tgz",
180
- "integrity": "sha512-R6BWgJdQOwW9ulJatuTVrQkjnODjqHZkKNOqb1sz++3Noe5LYd0i3PchnOBUCYAPHoPWHhjJqbdZlHEu0hpjdA==",
181
  "cpu": [
182
  "x64"
183
  ],
@@ -191,44 +120,8 @@
191
  "node": ">=14.21.3"
192
  }
193
  },
194
- "node_modules/@biomejs/cli-win32-arm64": {
195
- "version": "2.4.14",
196
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.14.tgz",
197
- "integrity": "sha512-M3EH5hqOI/F/FUA2u4xcLoUgmxd218mvuj/6JL7Hv2toQvr2/AdOvKSpGkoRuWFCtQPVa+ZqkEV3Q5xBA9+XSA==",
198
- "cpu": [
199
- "arm64"
200
- ],
201
- "dev": true,
202
- "license": "MIT OR Apache-2.0",
203
- "optional": true,
204
- "os": [
205
- "win32"
206
- ],
207
- "engines": {
208
- "node": ">=14.21.3"
209
- }
210
- },
211
- "node_modules/@biomejs/cli-win32-x64": {
212
- "version": "2.4.14",
213
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.14.tgz",
214
- "integrity": "sha512-WL0EG5qE+EAKomGXbf2g6VnSKJhTL3tXC0QRzWRwA5VpjxNYa6H4P7ZWfymbGE4IhZZQi1KXQ2R0YjwInmz2fA==",
215
- "cpu": [
216
- "x64"
217
- ],
218
- "dev": true,
219
- "license": "MIT OR Apache-2.0",
220
- "optional": true,
221
- "os": [
222
- "win32"
223
- ],
224
- "engines": {
225
- "node": ">=14.21.3"
226
- }
227
- },
228
  "node_modules/@cfworker/json-schema": {
229
  "version": "4.1.1",
230
- "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
231
- "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
232
  "license": "MIT"
233
  },
234
  "node_modules/@colors/colors": {
@@ -240,6 +133,17 @@
240
  "node": ">=0.1.90"
241
  }
242
  },
 
 
 
 
 
 
 
 
 
 
 
243
  "node_modules/@dabh/diagnostics": {
244
  "version": "2.0.8",
245
  "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
@@ -251,40 +155,6 @@
251
  "kuler": "^2.0.0"
252
  }
253
  },
254
- "node_modules/@emnapi/core": {
255
- "version": "1.10.0",
256
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
257
- "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
258
- "dev": true,
259
- "license": "MIT",
260
- "optional": true,
261
- "dependencies": {
262
- "@emnapi/wasi-threads": "1.2.1",
263
- "tslib": "^2.4.0"
264
- }
265
- },
266
- "node_modules/@emnapi/runtime": {
267
- "version": "1.10.0",
268
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
269
- "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
270
- "dev": true,
271
- "license": "MIT",
272
- "optional": true,
273
- "dependencies": {
274
- "tslib": "^2.4.0"
275
- }
276
- },
277
- "node_modules/@emnapi/wasi-threads": {
278
- "version": "1.2.1",
279
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
280
- "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
281
- "dev": true,
282
- "license": "MIT",
283
- "optional": true,
284
- "dependencies": {
285
- "tslib": "^2.4.0"
286
- }
287
- },
288
  "node_modules/@google/generative-ai": {
289
  "version": "0.24.1",
290
  "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz",
@@ -295,9 +165,9 @@
295
  }
296
  },
297
  "node_modules/@hono/node-server": {
298
- "version": "2.0.3",
299
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.3.tgz",
300
- "integrity": "sha512-a0jV+/HRe3G5zjFID3zObAQFdkl6zpxTuqktdDDXS3MJKcrZIkB8OkLpNBlY/WXFqv2HF4a0takPej+aNFczWA==",
301
  "license": "MIT",
302
  "engines": {
303
  "node": ">=20"
@@ -306,33 +176,46 @@
306
  "hono": "^4"
307
  }
308
  },
 
 
 
 
 
 
 
 
309
  "node_modules/@jridgewell/sourcemap-codec": {
310
  "version": "1.5.5",
311
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
312
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
313
  "dev": true,
314
  "license": "MIT"
315
  },
 
 
 
 
 
 
 
 
 
316
  "node_modules/@langchain/anthropic": {
317
- "version": "1.3.29",
318
- "resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-1.3.29.tgz",
319
- "integrity": "sha512-ep1qBIcV07bajsg3fDqMd39rYwoRLOEK/6lk+MCxlm1YB5SRoKKJAZANrblQ/4RYhZJnxf95c6BSQu8VoNbVAQ==",
320
  "license": "MIT",
321
  "dependencies": {
322
- "@anthropic-ai/sdk": "^0.91.1",
323
  "zod": "^3.25.76 || ^4"
324
  },
325
  "engines": {
326
  "node": ">=20"
327
  },
328
  "peerDependencies": {
329
- "@langchain/core": "^1.1.45"
330
  }
331
  },
332
  "node_modules/@langchain/core": {
333
- "version": "1.1.48",
334
- "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.48.tgz",
335
- "integrity": "sha512-fQU6Guyb1pwc2fEplmA8FPbKfOMAofjnyJzExevro0FxEiuGHE18Ov/ZHmT9trWCDTZRI9eW1VIc6aChxV8pAQ==",
336
  "license": "MIT",
337
  "dependencies": {
338
  "@cfworker/json-schema": "^4.0.2",
@@ -364,8 +247,6 @@
364
  },
365
  "node_modules/@langchain/langgraph": {
366
  "version": "1.3.2",
367
- "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.3.2.tgz",
368
- "integrity": "sha512-SL7Ktsr681R7da+1b2MVOWEbaCoFJOXEJPTGOjg4JIG4C7quWbTYC8DzxhcCxte6D/8cGp0rYDBnbKLXEpNqlA==",
369
  "license": "MIT",
370
  "dependencies": {
371
  "@langchain/langgraph-checkpoint": "^1.0.2",
@@ -390,8 +271,6 @@
390
  },
391
  "node_modules/@langchain/langgraph-checkpoint": {
392
  "version": "1.0.2",
393
- "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.2.tgz",
394
- "integrity": "sha512-F4E5Tr0nt8FGghgdscJtHw+ABzChOHeI80R7Y1pjIHdiJom6c2ieo76vL+FWiny80JmoGqhrVAEIWrw0cXKPxg==",
395
  "license": "MIT",
396
  "dependencies": {
397
  "uuid": "^10.0.0"
@@ -418,9 +297,7 @@
418
  }
419
  },
420
  "node_modules/@langchain/langgraph-sdk": {
421
- "version": "1.9.6",
422
- "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.9.6.tgz",
423
- "integrity": "sha512-cem5LckknNULd8o4WiOpf+rv+Qxvfpf5MzXiCQb9JnEfJdCRrWsl0/qBeZRpAXMf/1Va3uDMZouTmw9odmI0Hw==",
424
  "license": "MIT",
425
  "dependencies": {
426
  "@langchain/protocol": "^0.0.15",
@@ -453,14 +330,10 @@
453
  },
454
  "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": {
455
  "version": "5.0.4",
456
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
457
- "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
458
  "license": "MIT"
459
  },
460
  "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": {
461
  "version": "9.3.0",
462
- "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.0.tgz",
463
- "integrity": "sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==",
464
  "license": "MIT",
465
  "dependencies": {
466
  "eventemitter3": "^5.0.4",
@@ -475,8 +348,6 @@
475
  },
476
  "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": {
477
  "version": "7.0.1",
478
- "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz",
479
- "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==",
480
  "license": "MIT",
481
  "engines": {
482
  "node": ">=20"
@@ -487,8 +358,6 @@
487
  },
488
  "node_modules/@langchain/langgraph-sdk/node_modules/uuid": {
489
  "version": "13.0.2",
490
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz",
491
- "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==",
492
  "funding": [
493
  "https://github.com/sponsors/broofa",
494
  "https://github.com/sponsors/ctavan"
@@ -513,26 +382,22 @@
513
  }
514
  },
515
  "node_modules/@langchain/openai": {
516
- "version": "1.4.5",
517
- "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.4.5.tgz",
518
- "integrity": "sha512-bQ2WMIZfSh02trJLYSAtiIcD3j6EBCiAm9nw0dZWQsVaUxmWc3JJqs8uUte6AkMazmLHzcUIw+14UkXO5fRJvQ==",
519
  "license": "MIT",
520
  "dependencies": {
521
  "js-tiktoken": "^1.0.12",
522
- "openai": "^6.34.0",
523
  "zod": "^3.25.76 || ^4"
524
  },
525
  "engines": {
526
  "node": ">=20"
527
  },
528
  "peerDependencies": {
529
- "@langchain/core": "^1.1.42"
530
  }
531
  },
532
  "node_modules/@langchain/openrouter": {
533
  "version": "0.2.4",
534
- "resolved": "https://registry.npmjs.org/@langchain/openrouter/-/openrouter-0.2.4.tgz",
535
- "integrity": "sha512-FzoHUwIM4eE3rcMKZ1dL9a3vgVrz+6WiS5ygIMyHagNlPDUxpdg1V5LnA+ZVWosNyAtAkFYmz+DQZys9nM9oQQ==",
536
  "license": "MIT",
537
  "dependencies": {
538
  "@langchain/openai": "1.4.5",
@@ -546,198 +411,35 @@
546
  "@langchain/core": "^1.0.0"
547
  }
548
  },
549
- "node_modules/@langchain/protocol": {
550
- "version": "0.0.15",
551
- "resolved": "https://registry.npmjs.org/@langchain/protocol/-/protocol-0.0.15.tgz",
552
- "integrity": "sha512-MllvbpMjqHevUm+v94M422mH7XKN+wGCvJRBVROTWBotEDOATYB4Ktk2UheYP859y9o2LlhtPek5t1T9eyfAbQ==",
553
- "license": "MIT"
554
- },
555
- "node_modules/@napi-rs/wasm-runtime": {
556
- "version": "1.1.4",
557
- "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
558
- "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
559
- "dev": true,
560
  "license": "MIT",
561
- "optional": true,
562
  "dependencies": {
563
- "@tybys/wasm-util": "^0.10.1"
 
 
564
  },
565
- "funding": {
566
- "type": "github",
567
- "url": "https://github.com/sponsors/Brooooooklyn"
568
  },
569
  "peerDependencies": {
570
- "@emnapi/core": "^1.7.1",
571
- "@emnapi/runtime": "^1.7.1"
572
  }
573
  },
 
 
 
 
574
  "node_modules/@oxc-project/types": {
575
  "version": "0.128.0",
576
- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz",
577
- "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==",
578
  "dev": true,
579
  "license": "MIT",
580
  "funding": {
581
  "url": "https://github.com/sponsors/Boshen"
582
  }
583
  },
584
- "node_modules/@rolldown/binding-android-arm64": {
585
- "version": "1.0.0-rc.18",
586
- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz",
587
- "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==",
588
- "cpu": [
589
- "arm64"
590
- ],
591
- "dev": true,
592
- "license": "MIT",
593
- "optional": true,
594
- "os": [
595
- "android"
596
- ],
597
- "engines": {
598
- "node": "^20.19.0 || >=22.12.0"
599
- }
600
- },
601
- "node_modules/@rolldown/binding-darwin-arm64": {
602
- "version": "1.0.0-rc.18",
603
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz",
604
- "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==",
605
- "cpu": [
606
- "arm64"
607
- ],
608
- "dev": true,
609
- "license": "MIT",
610
- "optional": true,
611
- "os": [
612
- "darwin"
613
- ],
614
- "engines": {
615
- "node": "^20.19.0 || >=22.12.0"
616
- }
617
- },
618
- "node_modules/@rolldown/binding-darwin-x64": {
619
- "version": "1.0.0-rc.18",
620
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz",
621
- "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==",
622
- "cpu": [
623
- "x64"
624
- ],
625
- "dev": true,
626
- "license": "MIT",
627
- "optional": true,
628
- "os": [
629
- "darwin"
630
- ],
631
- "engines": {
632
- "node": "^20.19.0 || >=22.12.0"
633
- }
634
- },
635
- "node_modules/@rolldown/binding-freebsd-x64": {
636
- "version": "1.0.0-rc.18",
637
- "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz",
638
- "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==",
639
- "cpu": [
640
- "x64"
641
- ],
642
- "dev": true,
643
- "license": "MIT",
644
- "optional": true,
645
- "os": [
646
- "freebsd"
647
- ],
648
- "engines": {
649
- "node": "^20.19.0 || >=22.12.0"
650
- }
651
- },
652
- "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
653
- "version": "1.0.0-rc.18",
654
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz",
655
- "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==",
656
- "cpu": [
657
- "arm"
658
- ],
659
- "dev": true,
660
- "license": "MIT",
661
- "optional": true,
662
- "os": [
663
- "linux"
664
- ],
665
- "engines": {
666
- "node": "^20.19.0 || >=22.12.0"
667
- }
668
- },
669
- "node_modules/@rolldown/binding-linux-arm64-gnu": {
670
- "version": "1.0.0-rc.18",
671
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz",
672
- "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==",
673
- "cpu": [
674
- "arm64"
675
- ],
676
- "dev": true,
677
- "license": "MIT",
678
- "optional": true,
679
- "os": [
680
- "linux"
681
- ],
682
- "engines": {
683
- "node": "^20.19.0 || >=22.12.0"
684
- }
685
- },
686
- "node_modules/@rolldown/binding-linux-arm64-musl": {
687
- "version": "1.0.0-rc.18",
688
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz",
689
- "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==",
690
- "cpu": [
691
- "arm64"
692
- ],
693
- "dev": true,
694
- "license": "MIT",
695
- "optional": true,
696
- "os": [
697
- "linux"
698
- ],
699
- "engines": {
700
- "node": "^20.19.0 || >=22.12.0"
701
- }
702
- },
703
- "node_modules/@rolldown/binding-linux-ppc64-gnu": {
704
- "version": "1.0.0-rc.18",
705
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz",
706
- "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==",
707
- "cpu": [
708
- "ppc64"
709
- ],
710
- "dev": true,
711
- "license": "MIT",
712
- "optional": true,
713
- "os": [
714
- "linux"
715
- ],
716
- "engines": {
717
- "node": "^20.19.0 || >=22.12.0"
718
- }
719
- },
720
- "node_modules/@rolldown/binding-linux-s390x-gnu": {
721
- "version": "1.0.0-rc.18",
722
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz",
723
- "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==",
724
- "cpu": [
725
- "s390x"
726
- ],
727
- "dev": true,
728
- "license": "MIT",
729
- "optional": true,
730
- "os": [
731
- "linux"
732
- ],
733
- "engines": {
734
- "node": "^20.19.0 || >=22.12.0"
735
- }
736
- },
737
  "node_modules/@rolldown/binding-linux-x64-gnu": {
738
  "version": "1.0.0-rc.18",
739
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz",
740
- "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==",
741
  "cpu": [
742
  "x64"
743
  ],
@@ -753,8 +455,6 @@
753
  },
754
  "node_modules/@rolldown/binding-linux-x64-musl": {
755
  "version": "1.0.0-rc.18",
756
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz",
757
- "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==",
758
  "cpu": [
759
  "x64"
760
  ],
@@ -768,80 +468,8 @@
768
  "node": "^20.19.0 || >=22.12.0"
769
  }
770
  },
771
- "node_modules/@rolldown/binding-openharmony-arm64": {
772
- "version": "1.0.0-rc.18",
773
- "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz",
774
- "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==",
775
- "cpu": [
776
- "arm64"
777
- ],
778
- "dev": true,
779
- "license": "MIT",
780
- "optional": true,
781
- "os": [
782
- "openharmony"
783
- ],
784
- "engines": {
785
- "node": "^20.19.0 || >=22.12.0"
786
- }
787
- },
788
- "node_modules/@rolldown/binding-wasm32-wasi": {
789
- "version": "1.0.0-rc.18",
790
- "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz",
791
- "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==",
792
- "cpu": [
793
- "wasm32"
794
- ],
795
- "dev": true,
796
- "license": "MIT",
797
- "optional": true,
798
- "dependencies": {
799
- "@emnapi/core": "1.10.0",
800
- "@emnapi/runtime": "1.10.0",
801
- "@napi-rs/wasm-runtime": "^1.1.4"
802
- },
803
- "engines": {
804
- "node": "^20.19.0 || >=22.12.0"
805
- }
806
- },
807
- "node_modules/@rolldown/binding-win32-arm64-msvc": {
808
- "version": "1.0.0-rc.18",
809
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz",
810
- "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==",
811
- "cpu": [
812
- "arm64"
813
- ],
814
- "dev": true,
815
- "license": "MIT",
816
- "optional": true,
817
- "os": [
818
- "win32"
819
- ],
820
- "engines": {
821
- "node": "^20.19.0 || >=22.12.0"
822
- }
823
- },
824
- "node_modules/@rolldown/binding-win32-x64-msvc": {
825
- "version": "1.0.0-rc.18",
826
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz",
827
- "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==",
828
- "cpu": [
829
- "x64"
830
- ],
831
- "dev": true,
832
- "license": "MIT",
833
- "optional": true,
834
- "os": [
835
- "win32"
836
- ],
837
- "engines": {
838
- "node": "^20.19.0 || >=22.12.0"
839
- }
840
- },
841
  "node_modules/@rolldown/pluginutils": {
842
  "version": "1.0.0-rc.18",
843
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz",
844
- "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==",
845
  "dev": true,
846
  "license": "MIT"
847
  },
@@ -861,27 +489,38 @@
861
  "integrity": "sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==",
862
  "license": "MIT"
863
  },
 
 
 
 
 
 
864
  "node_modules/@standard-schema/spec": {
865
  "version": "1.1.0",
866
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
867
- "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
868
  "license": "MIT"
869
  },
870
- "node_modules/@tybys/wasm-util": {
871
- "version": "0.10.2",
872
- "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
873
- "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
874
  "dev": true,
875
- "license": "MIT",
876
- "optional": true,
877
- "dependencies": {
878
- "tslib": "^2.4.0"
879
- }
 
 
 
 
 
 
 
 
 
 
 
880
  },
881
  "node_modules/@types/chai": {
882
  "version": "5.2.3",
883
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
884
- "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
885
  "dev": true,
886
  "license": "MIT",
887
  "dependencies": {
@@ -891,32 +530,24 @@
891
  },
892
  "node_modules/@types/deep-eql": {
893
  "version": "4.0.2",
894
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
895
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
896
  "dev": true,
897
  "license": "MIT"
898
  },
899
  "node_modules/@types/estree": {
900
  "version": "1.0.9",
901
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
902
- "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
903
  "dev": true,
904
  "license": "MIT"
905
  },
906
  "node_modules/@types/json-schema": {
907
  "version": "7.0.15",
908
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
909
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
910
  "license": "MIT"
911
  },
912
  "node_modules/@types/node": {
913
- "version": "25.6.0",
914
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
915
- "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
916
  "dev": true,
917
  "license": "MIT",
918
  "dependencies": {
919
- "undici-types": "~7.19.0"
920
  }
921
  },
922
  "node_modules/@types/triple-beam": {
@@ -927,8 +558,6 @@
927
  },
928
  "node_modules/@vitest/expect": {
929
  "version": "4.1.5",
930
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
931
- "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==",
932
  "dev": true,
933
  "license": "MIT",
934
  "dependencies": {
@@ -945,8 +574,6 @@
945
  },
946
  "node_modules/@vitest/mocker": {
947
  "version": "4.1.5",
948
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz",
949
- "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==",
950
  "dev": true,
951
  "license": "MIT",
952
  "dependencies": {
@@ -972,8 +599,6 @@
972
  },
973
  "node_modules/@vitest/pretty-format": {
974
  "version": "4.1.5",
975
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz",
976
- "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==",
977
  "dev": true,
978
  "license": "MIT",
979
  "dependencies": {
@@ -985,8 +610,6 @@
985
  },
986
  "node_modules/@vitest/runner": {
987
  "version": "4.1.5",
988
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz",
989
- "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==",
990
  "dev": true,
991
  "license": "MIT",
992
  "dependencies": {
@@ -999,8 +622,6 @@
999
  },
1000
  "node_modules/@vitest/snapshot": {
1001
  "version": "4.1.5",
1002
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz",
1003
- "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==",
1004
  "dev": true,
1005
  "license": "MIT",
1006
  "dependencies": {
@@ -1015,8 +636,6 @@
1015
  },
1016
  "node_modules/@vitest/spy": {
1017
  "version": "4.1.5",
1018
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz",
1019
- "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==",
1020
  "dev": true,
1021
  "license": "MIT",
1022
  "funding": {
@@ -1025,8 +644,6 @@
1025
  },
1026
  "node_modules/@vitest/utils": {
1027
  "version": "4.1.5",
1028
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz",
1029
- "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==",
1030
  "dev": true,
1031
  "license": "MIT",
1032
  "dependencies": {
@@ -1045,6 +662,28 @@
1045
  "dev": true,
1046
  "license": "BSD-2-Clause"
1047
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1048
  "node_modules/ansi-styles": {
1049
  "version": "4.3.0",
1050
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -1061,30 +700,13 @@
1061
  "url": "https://github.com/chalk/ansi-styles?sponsor=1"
1062
  }
1063
  },
1064
- "node_modules/ansi-styles/node_modules/color-convert": {
1065
- "version": "2.0.1",
1066
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
1067
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
1068
- "dev": true,
1069
- "license": "MIT",
1070
- "dependencies": {
1071
- "color-name": "~1.1.4"
1072
- },
1073
- "engines": {
1074
- "node": ">=7.0.0"
1075
- }
1076
- },
1077
- "node_modules/ansi-styles/node_modules/color-name": {
1078
- "version": "1.1.4",
1079
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
1080
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
1081
  "dev": true,
1082
  "license": "MIT"
1083
  },
1084
  "node_modules/assertion-error": {
1085
  "version": "2.0.1",
1086
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
1087
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
1088
  "dev": true,
1089
  "license": "MIT",
1090
  "engines": {
@@ -1099,8 +721,6 @@
1099
  },
1100
  "node_modules/base64-js": {
1101
  "version": "1.5.1",
1102
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
1103
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
1104
  "funding": [
1105
  {
1106
  "type": "github",
@@ -1182,8 +802,6 @@
1182
  },
1183
  "node_modules/chai": {
1184
  "version": "6.2.2",
1185
- "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
1186
- "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
1187
  "dev": true,
1188
  "license": "MIT",
1189
  "engines": {
@@ -1237,18 +855,38 @@
1237
  }
1238
  },
1239
  "node_modules/color-convert": {
1240
- "version": "3.1.3",
1241
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
1242
- "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
 
1243
  "license": "MIT",
1244
  "dependencies": {
1245
- "color-name": "^2.0.0"
1246
  },
1247
  "engines": {
1248
- "node": ">=14.6"
1249
  }
1250
  },
1251
  "node_modules/color-name": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1252
  "version": "2.1.0",
1253
  "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
1254
  "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
@@ -1257,16 +895,25 @@
1257
  "node": ">=12.20"
1258
  }
1259
  },
1260
- "node_modules/color-string": {
1261
- "version": "2.1.4",
1262
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
1263
- "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
1264
  "license": "MIT",
1265
  "dependencies": {
1266
  "color-name": "^2.0.0"
1267
  },
1268
  "engines": {
1269
- "node": ">=18"
 
 
 
 
 
 
 
 
 
1270
  }
1271
  },
1272
  "node_modules/command-exists": {
@@ -1286,8 +933,11 @@
1286
  },
1287
  "node_modules/convert-source-map": {
1288
  "version": "2.0.0",
1289
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1290
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
 
 
 
1291
  "dev": true,
1292
  "license": "MIT"
1293
  },
@@ -1326,18 +976,22 @@
1326
  },
1327
  "node_modules/detect-libc": {
1328
  "version": "2.1.2",
1329
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
1330
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
1331
  "dev": true,
1332
  "license": "Apache-2.0",
1333
  "engines": {
1334
- "node": ">=8"
 
 
 
 
 
 
 
 
1335
  }
1336
  },
1337
  "node_modules/dotenv": {
1338
  "version": "17.4.2",
1339
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
1340
- "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
1341
  "license": "BSD-2-Clause",
1342
  "engines": {
1343
  "node": ">=12"
@@ -1389,15 +1043,13 @@
1389
  },
1390
  "node_modules/es-module-lexer": {
1391
  "version": "2.1.0",
1392
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
1393
- "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
1394
  "dev": true,
1395
  "license": "MIT"
1396
  },
1397
  "node_modules/es-object-atoms": {
1398
- "version": "1.1.1",
1399
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
1400
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
1401
  "dev": true,
1402
  "license": "MIT",
1403
  "dependencies": {
@@ -1409,8 +1061,6 @@
1409
  },
1410
  "node_modules/estree-walker": {
1411
  "version": "3.0.3",
1412
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
1413
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
1414
  "dev": true,
1415
  "license": "MIT",
1416
  "dependencies": {
@@ -1419,14 +1069,10 @@
1419
  },
1420
  "node_modules/eventemitter3": {
1421
  "version": "4.0.7",
1422
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
1423
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
1424
  "license": "MIT"
1425
  },
1426
  "node_modules/eventsource-parser": {
1427
  "version": "3.0.8",
1428
- "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz",
1429
- "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==",
1430
  "license": "MIT",
1431
  "engines": {
1432
  "node": ">=18.0.0"
@@ -1434,18 +1080,20 @@
1434
  },
1435
  "node_modules/expect-type": {
1436
  "version": "1.3.0",
1437
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
1438
- "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
1439
  "dev": true,
1440
  "license": "Apache-2.0",
1441
  "engines": {
1442
  "node": ">=12.0.0"
1443
  }
1444
  },
 
 
 
 
 
 
1445
  "node_modules/fdir": {
1446
  "version": "6.5.0",
1447
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1448
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1449
  "dev": true,
1450
  "license": "MIT",
1451
  "engines": {
@@ -1530,21 +1178,6 @@
1530
  "node": ">=12"
1531
  }
1532
  },
1533
- "node_modules/fsevents": {
1534
- "version": "2.3.3",
1535
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1536
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1537
- "dev": true,
1538
- "hasInstallScript": true,
1539
- "license": "MIT",
1540
- "optional": true,
1541
- "os": [
1542
- "darwin"
1543
- ],
1544
- "engines": {
1545
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1546
- }
1547
- },
1548
  "node_modules/function-bind": {
1549
  "version": "1.1.2",
1550
  "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -1664,9 +1297,9 @@
1664
  }
1665
  },
1666
  "node_modules/hono": {
1667
- "version": "4.12.21",
1668
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.21.tgz",
1669
- "integrity": "sha512-uV63apnb0kyPtAUwoWgaGh9HyIFcv8lgmzPZSiTBQAFOFGIzka5EZ1dZocmGnn0XdX0+XTqJ6Tqv7selMuGLRQ==",
1670
  "license": "MIT",
1671
  "engines": {
1672
  "node": ">=16.9.0"
@@ -1696,8 +1329,6 @@
1696
  },
1697
  "node_modules/is-network-error": {
1698
  "version": "1.3.2",
1699
- "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz",
1700
- "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==",
1701
  "license": "MIT",
1702
  "engines": {
1703
  "node": ">=16"
@@ -1763,8 +1394,6 @@
1763
  },
1764
  "node_modules/js-tiktoken": {
1765
  "version": "1.0.21",
1766
- "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
1767
- "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
1768
  "license": "MIT",
1769
  "dependencies": {
1770
  "base64-js": "^1.5.1"
@@ -1843,12 +1472,10 @@
1843
  "license": "MIT"
1844
  },
1845
  "node_modules/langchain": {
1846
- "version": "1.4.2",
1847
- "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.4.2.tgz",
1848
- "integrity": "sha512-SLGipy0r4nqQD0aiUOBYLMeGFfB/QiYnMndfZ8sGN89vXDCIXbYqcE7G/4QDDX3nZsM7/emQpoScmlxEX6sDnQ==",
1849
  "license": "MIT",
1850
  "dependencies": {
1851
- "@langchain/langgraph": "^1.3.2",
1852
  "@langchain/langgraph-checkpoint": "^1.0.1",
1853
  "langsmith": ">=0.5.0 <1.0.0",
1854
  "zod": "^3.25.76 || ^4"
@@ -1857,13 +1484,11 @@
1857
  "node": ">=20"
1858
  },
1859
  "peerDependencies": {
1860
- "@langchain/core": "^1.1.48"
1861
  }
1862
  },
1863
  "node_modules/langsmith": {
1864
  "version": "0.6.2",
1865
- "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.6.2.tgz",
1866
- "integrity": "sha512-OrFt+a2P4UMaa2cSpp3fjYTJ+TWQFjnoz5j4njiZYWMpAJezTrMRN1mrNVzq/FACprgPwAMjq5YkZNRYJKorwg==",
1867
  "license": "MIT",
1868
  "dependencies": {
1869
  "p-queue": "6.6.2"
@@ -1895,8 +1520,6 @@
1895
  },
1896
  "node_modules/lightningcss": {
1897
  "version": "1.32.0",
1898
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
1899
- "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
1900
  "dev": true,
1901
  "license": "MPL-2.0",
1902
  "dependencies": {
@@ -1923,157 +1546,8 @@
1923
  "lightningcss-win32-x64-msvc": "1.32.0"
1924
  }
1925
  },
1926
- "node_modules/lightningcss-android-arm64": {
1927
- "version": "1.32.0",
1928
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
1929
- "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
1930
- "cpu": [
1931
- "arm64"
1932
- ],
1933
- "dev": true,
1934
- "license": "MPL-2.0",
1935
- "optional": true,
1936
- "os": [
1937
- "android"
1938
- ],
1939
- "engines": {
1940
- "node": ">= 12.0.0"
1941
- },
1942
- "funding": {
1943
- "type": "opencollective",
1944
- "url": "https://opencollective.com/parcel"
1945
- }
1946
- },
1947
- "node_modules/lightningcss-darwin-arm64": {
1948
- "version": "1.32.0",
1949
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
1950
- "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
1951
- "cpu": [
1952
- "arm64"
1953
- ],
1954
- "dev": true,
1955
- "license": "MPL-2.0",
1956
- "optional": true,
1957
- "os": [
1958
- "darwin"
1959
- ],
1960
- "engines": {
1961
- "node": ">= 12.0.0"
1962
- },
1963
- "funding": {
1964
- "type": "opencollective",
1965
- "url": "https://opencollective.com/parcel"
1966
- }
1967
- },
1968
- "node_modules/lightningcss-darwin-x64": {
1969
- "version": "1.32.0",
1970
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
1971
- "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
1972
- "cpu": [
1973
- "x64"
1974
- ],
1975
- "dev": true,
1976
- "license": "MPL-2.0",
1977
- "optional": true,
1978
- "os": [
1979
- "darwin"
1980
- ],
1981
- "engines": {
1982
- "node": ">= 12.0.0"
1983
- },
1984
- "funding": {
1985
- "type": "opencollective",
1986
- "url": "https://opencollective.com/parcel"
1987
- }
1988
- },
1989
- "node_modules/lightningcss-freebsd-x64": {
1990
- "version": "1.32.0",
1991
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
1992
- "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
1993
- "cpu": [
1994
- "x64"
1995
- ],
1996
- "dev": true,
1997
- "license": "MPL-2.0",
1998
- "optional": true,
1999
- "os": [
2000
- "freebsd"
2001
- ],
2002
- "engines": {
2003
- "node": ">= 12.0.0"
2004
- },
2005
- "funding": {
2006
- "type": "opencollective",
2007
- "url": "https://opencollective.com/parcel"
2008
- }
2009
- },
2010
- "node_modules/lightningcss-linux-arm-gnueabihf": {
2011
- "version": "1.32.0",
2012
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
2013
- "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
2014
- "cpu": [
2015
- "arm"
2016
- ],
2017
- "dev": true,
2018
- "license": "MPL-2.0",
2019
- "optional": true,
2020
- "os": [
2021
- "linux"
2022
- ],
2023
- "engines": {
2024
- "node": ">= 12.0.0"
2025
- },
2026
- "funding": {
2027
- "type": "opencollective",
2028
- "url": "https://opencollective.com/parcel"
2029
- }
2030
- },
2031
- "node_modules/lightningcss-linux-arm64-gnu": {
2032
- "version": "1.32.0",
2033
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
2034
- "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
2035
- "cpu": [
2036
- "arm64"
2037
- ],
2038
- "dev": true,
2039
- "license": "MPL-2.0",
2040
- "optional": true,
2041
- "os": [
2042
- "linux"
2043
- ],
2044
- "engines": {
2045
- "node": ">= 12.0.0"
2046
- },
2047
- "funding": {
2048
- "type": "opencollective",
2049
- "url": "https://opencollective.com/parcel"
2050
- }
2051
- },
2052
- "node_modules/lightningcss-linux-arm64-musl": {
2053
- "version": "1.32.0",
2054
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
2055
- "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
2056
- "cpu": [
2057
- "arm64"
2058
- ],
2059
- "dev": true,
2060
- "license": "MPL-2.0",
2061
- "optional": true,
2062
- "os": [
2063
- "linux"
2064
- ],
2065
- "engines": {
2066
- "node": ">= 12.0.0"
2067
- },
2068
- "funding": {
2069
- "type": "opencollective",
2070
- "url": "https://opencollective.com/parcel"
2071
- }
2072
- },
2073
  "node_modules/lightningcss-linux-x64-gnu": {
2074
  "version": "1.32.0",
2075
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
2076
- "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
2077
  "cpu": [
2078
  "x64"
2079
  ],
@@ -2093,8 +1567,6 @@
2093
  },
2094
  "node_modules/lightningcss-linux-x64-musl": {
2095
  "version": "1.32.0",
2096
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
2097
- "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
2098
  "cpu": [
2099
  "x64"
2100
  ],
@@ -2112,48 +1584,6 @@
2112
  "url": "https://opencollective.com/parcel"
2113
  }
2114
  },
2115
- "node_modules/lightningcss-win32-arm64-msvc": {
2116
- "version": "1.32.0",
2117
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
2118
- "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
2119
- "cpu": [
2120
- "arm64"
2121
- ],
2122
- "dev": true,
2123
- "license": "MPL-2.0",
2124
- "optional": true,
2125
- "os": [
2126
- "win32"
2127
- ],
2128
- "engines": {
2129
- "node": ">= 12.0.0"
2130
- },
2131
- "funding": {
2132
- "type": "opencollective",
2133
- "url": "https://opencollective.com/parcel"
2134
- }
2135
- },
2136
- "node_modules/lightningcss-win32-x64-msvc": {
2137
- "version": "1.32.0",
2138
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
2139
- "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
2140
- "cpu": [
2141
- "x64"
2142
- ],
2143
- "dev": true,
2144
- "license": "MPL-2.0",
2145
- "optional": true,
2146
- "os": [
2147
- "win32"
2148
- ],
2149
- "engines": {
2150
- "node": ">= 12.0.0"
2151
- },
2152
- "funding": {
2153
- "type": "opencollective",
2154
- "url": "https://opencollective.com/parcel"
2155
- }
2156
- },
2157
  "node_modules/logform": {
2158
  "version": "2.7.0",
2159
  "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
@@ -2173,14 +1603,17 @@
2173
  },
2174
  "node_modules/magic-string": {
2175
  "version": "0.30.21",
2176
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
2177
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
2178
  "dev": true,
2179
  "license": "MIT",
2180
  "dependencies": {
2181
  "@jridgewell/sourcemap-codec": "^1.5.5"
2182
  }
2183
  },
 
 
 
 
 
2184
  "node_modules/math-intrinsics": {
2185
  "version": "1.1.0",
2186
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -2244,8 +1677,6 @@
2244
  },
2245
  "node_modules/mustache": {
2246
  "version": "4.2.0",
2247
- "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
2248
- "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
2249
  "license": "MIT",
2250
  "bin": {
2251
  "mustache": "bin/mustache"
@@ -2253,8 +1684,6 @@
2253
  },
2254
  "node_modules/nanoid": {
2255
  "version": "3.3.12",
2256
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
2257
- "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
2258
  "dev": true,
2259
  "funding": [
2260
  {
@@ -2282,8 +1711,6 @@
2282
  },
2283
  "node_modules/obug": {
2284
  "version": "2.1.1",
2285
- "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
2286
- "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
2287
  "dev": true,
2288
  "funding": [
2289
  "https://github.com/sponsors/sxzz",
@@ -2318,9 +1745,7 @@
2318
  }
2319
  },
2320
  "node_modules/openai": {
2321
- "version": "6.36.0",
2322
- "resolved": "https://registry.npmjs.org/openai/-/openai-6.36.0.tgz",
2323
- "integrity": "sha512-Has2YbIusMq9wQEierFsgf9c783dy1y9arX459LmphNacEkkM5yxi2RIyXP0LmkOroQyW19iTwALHL8Yf26UKA==",
2324
  "license": "Apache-2.0",
2325
  "bin": {
2326
  "openai": "bin/cli"
@@ -2349,8 +1774,6 @@
2349
  },
2350
  "node_modules/p-finally": {
2351
  "version": "1.0.0",
2352
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
2353
- "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
2354
  "license": "MIT",
2355
  "engines": {
2356
  "node": ">=4"
@@ -2358,8 +1781,6 @@
2358
  },
2359
  "node_modules/p-queue": {
2360
  "version": "6.6.2",
2361
- "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
2362
- "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
2363
  "license": "MIT",
2364
  "dependencies": {
2365
  "eventemitter3": "^4.0.4",
@@ -2374,8 +1795,6 @@
2374
  },
2375
  "node_modules/p-retry": {
2376
  "version": "7.1.1",
2377
- "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz",
2378
- "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==",
2379
  "license": "MIT",
2380
  "dependencies": {
2381
  "is-network-error": "^1.1.0"
@@ -2389,8 +1808,6 @@
2389
  },
2390
  "node_modules/p-timeout": {
2391
  "version": "3.2.0",
2392
- "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
2393
- "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
2394
  "license": "MIT",
2395
  "dependencies": {
2396
  "p-finally": "^1.0.0"
@@ -2429,29 +1846,6 @@
2429
  "npm": ">5"
2430
  }
2431
  },
2432
- "node_modules/patch-package/node_modules/semver": {
2433
- "version": "7.8.0",
2434
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
2435
- "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
2436
- "dev": true,
2437
- "license": "ISC",
2438
- "bin": {
2439
- "semver": "bin/semver.js"
2440
- },
2441
- "engines": {
2442
- "node": ">=10"
2443
- }
2444
- },
2445
- "node_modules/patch-package/node_modules/tmp": {
2446
- "version": "0.2.5",
2447
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
2448
- "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
2449
- "dev": true,
2450
- "license": "MIT",
2451
- "engines": {
2452
- "node": ">=14.14"
2453
- }
2454
- },
2455
  "node_modules/path-key": {
2456
  "version": "3.1.1",
2457
  "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -2464,22 +1858,16 @@
2464
  },
2465
  "node_modules/pathe": {
2466
  "version": "2.0.3",
2467
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
2468
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
2469
  "dev": true,
2470
  "license": "MIT"
2471
  },
2472
  "node_modules/picocolors": {
2473
  "version": "1.1.1",
2474
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
2475
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
2476
  "dev": true,
2477
  "license": "ISC"
2478
  },
2479
  "node_modules/picomatch": {
2480
  "version": "4.0.4",
2481
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
2482
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
2483
  "dev": true,
2484
  "license": "MIT",
2485
  "engines": {
@@ -2491,8 +1879,6 @@
2491
  },
2492
  "node_modules/postcss": {
2493
  "version": "8.5.14",
2494
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
2495
- "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
2496
  "dev": true,
2497
  "funding": [
2498
  {
@@ -2534,8 +1920,6 @@
2534
  },
2535
  "node_modules/rolldown": {
2536
  "version": "1.0.0-rc.18",
2537
- "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
2538
- "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==",
2539
  "dev": true,
2540
  "license": "MIT",
2541
  "dependencies": {
@@ -2596,12 +1980,16 @@
2596
  }
2597
  },
2598
  "node_modules/semver": {
2599
- "version": "5.7.2",
2600
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
2601
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
 
2602
  "license": "ISC",
2603
  "bin": {
2604
- "semver": "bin/semver"
 
 
 
2605
  }
2606
  },
2607
  "node_modules/set-function-length": {
@@ -2647,8 +2035,6 @@
2647
  },
2648
  "node_modules/siginfo": {
2649
  "version": "2.0.0",
2650
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
2651
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
2652
  "dev": true,
2653
  "license": "ISC"
2654
  },
@@ -2683,10 +2069,29 @@
2683
  "node": ">=12.0.0"
2684
  }
2685
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2686
  "node_modules/source-map-js": {
2687
  "version": "1.2.1",
2688
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
2689
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
2690
  "dev": true,
2691
  "license": "BSD-3-Clause",
2692
  "engines": {
@@ -2704,15 +2109,21 @@
2704
  },
2705
  "node_modules/stackback": {
2706
  "version": "0.0.2",
2707
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
2708
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
2709
  "dev": true,
2710
  "license": "MIT"
2711
  },
 
 
 
 
 
 
 
 
 
 
2712
  "node_modules/std-env": {
2713
  "version": "4.1.0",
2714
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
2715
- "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
2716
  "dev": true,
2717
  "license": "MIT"
2718
  },
@@ -2746,15 +2157,11 @@
2746
  },
2747
  "node_modules/tinybench": {
2748
  "version": "2.9.0",
2749
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
2750
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
2751
  "dev": true,
2752
  "license": "MIT"
2753
  },
2754
  "node_modules/tinyexec": {
2755
  "version": "1.1.2",
2756
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz",
2757
- "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==",
2758
  "dev": true,
2759
  "license": "MIT",
2760
  "engines": {
@@ -2763,8 +2170,6 @@
2763
  },
2764
  "node_modules/tinyglobby": {
2765
  "version": "0.2.16",
2766
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
2767
- "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
2768
  "dev": true,
2769
  "license": "MIT",
2770
  "dependencies": {
@@ -2780,8 +2185,6 @@
2780
  },
2781
  "node_modules/tinyrainbow": {
2782
  "version": "3.1.0",
2783
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
2784
- "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
2785
  "dev": true,
2786
  "license": "MIT",
2787
  "engines": {
@@ -2789,15 +2192,13 @@
2789
  }
2790
  },
2791
  "node_modules/tmp": {
2792
- "version": "0.0.33",
2793
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
2794
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
 
2795
  "license": "MIT",
2796
- "dependencies": {
2797
- "os-tmpdir": "~1.0.2"
2798
- },
2799
  "engines": {
2800
- "node": ">=0.6.0"
2801
  }
2802
  },
2803
  "node_modules/to-regex-range": {
@@ -2828,18 +2229,50 @@
2828
  "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==",
2829
  "license": "MIT"
2830
  },
2831
- "node_modules/tslib": {
2832
- "version": "2.8.1",
2833
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
2834
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
2835
  "dev": true,
2836
- "license": "0BSD",
2837
- "optional": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2838
  },
2839
  "node_modules/typescript": {
2840
  "version": "6.0.3",
2841
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
2842
- "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
2843
  "dev": true,
2844
  "license": "Apache-2.0",
2845
  "bin": {
@@ -2851,9 +2284,7 @@
2851
  }
2852
  },
2853
  "node_modules/undici-types": {
2854
- "version": "7.19.2",
2855
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
2856
- "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
2857
  "dev": true,
2858
  "license": "MIT"
2859
  },
@@ -2886,10 +2317,13 @@
2886
  "uuid": "dist/esm/bin/uuid"
2887
  }
2888
  },
 
 
 
 
 
2889
  "node_modules/vite": {
2890
  "version": "8.0.11",
2891
- "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz",
2892
- "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==",
2893
  "dev": true,
2894
  "license": "MIT",
2895
  "dependencies": {
@@ -2966,8 +2400,6 @@
2966
  },
2967
  "node_modules/vitest": {
2968
  "version": "4.1.5",
2969
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz",
2970
- "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==",
2971
  "dev": true,
2972
  "license": "MIT",
2973
  "dependencies": {
@@ -3072,8 +2504,6 @@
3072
  },
3073
  "node_modules/why-is-node-running": {
3074
  "version": "2.3.0",
3075
- "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
3076
- "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
3077
  "dev": true,
3078
  "license": "MIT",
3079
  "dependencies": {
@@ -3139,10 +2569,16 @@
3139
  "url": "https://github.com/sponsors/eemeli"
3140
  }
3141
  },
 
 
 
 
 
 
 
 
3142
  "node_modules/zod": {
3143
  "version": "4.4.3",
3144
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
3145
- "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
3146
  "license": "MIT",
3147
  "funding": {
3148
  "url": "https://github.com/sponsors/colinhacks"
 
14
  "@langchain/anthropic": "^1.3.29",
15
  "@langchain/core": "^1.1.45",
16
  "@langchain/google-genai": "^2.1.31",
17
+ "@langchain/langgraph": "^1.3.2",
18
+ "@langchain/openai": "^1.4.6",
19
  "@langchain/openrouter": "^0.2.4",
20
  "@solidity-parser/parser": "^0.20.2",
21
  "dotenv": "^17.4.2",
 
28
  },
29
  "devDependencies": {
30
  "@biomejs/biome": "2.4.14",
31
+ "@types/node": "^25.9.1",
32
  "patch-package": "^8.0.1",
33
+ "ts-node": "^10.9.2",
34
  "typescript": "^6.0.3",
35
  "vitest": "^4.1.5"
36
  }
37
  },
38
  "node_modules/@anthropic-ai/sdk": {
39
+ "version": "0.95.2",
40
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.95.2.tgz",
41
+ "integrity": "sha512-Egddwo3sheo1PzUrMkZnH6VkQYwS0h/b/i8vSK8Ta9M45UQipAMeDFH57dYuDAfXMEUUGeKw6CMlremgMZgrSQ==",
42
  "license": "MIT",
43
  "dependencies": {
44
+ "json-schema-to-ts": "^3.1.1",
45
+ "standardwebhooks": "^1.0.0"
46
  },
47
  "bin": {
48
  "anthropic-ai-sdk": "bin/cli"
 
57
  }
58
  },
59
  "node_modules/@babel/runtime": {
60
+ "version": "7.29.7",
61
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
62
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
63
  "license": "MIT",
64
  "engines": {
65
  "node": ">=6.9.0"
 
67
  },
68
  "node_modules/@biomejs/biome": {
69
  "version": "2.4.14",
 
 
70
  "dev": true,
71
  "license": "MIT OR Apache-2.0",
72
  "bin": {
 
90
  "@biomejs/cli-win32-x64": "2.4.14"
91
  }
92
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  "node_modules/@biomejs/cli-linux-x64": {
94
  "version": "2.4.14",
 
 
95
  "cpu": [
96
  "x64"
97
  ],
 
107
  },
108
  "node_modules/@biomejs/cli-linux-x64-musl": {
109
  "version": "2.4.14",
 
 
110
  "cpu": [
111
  "x64"
112
  ],
 
120
  "node": ">=14.21.3"
121
  }
122
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  "node_modules/@cfworker/json-schema": {
124
  "version": "4.1.1",
 
 
125
  "license": "MIT"
126
  },
127
  "node_modules/@colors/colors": {
 
133
  "node": ">=0.1.90"
134
  }
135
  },
136
+ "node_modules/@cspotcode/source-map-support": {
137
+ "version": "0.8.1",
138
+ "dev": true,
139
+ "license": "MIT",
140
+ "dependencies": {
141
+ "@jridgewell/trace-mapping": "0.3.9"
142
+ },
143
+ "engines": {
144
+ "node": ">=12"
145
+ }
146
+ },
147
  "node_modules/@dabh/diagnostics": {
148
  "version": "2.0.8",
149
  "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
 
155
  "kuler": "^2.0.0"
156
  }
157
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  "node_modules/@google/generative-ai": {
159
  "version": "0.24.1",
160
  "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz",
 
165
  }
166
  },
167
  "node_modules/@hono/node-server": {
168
+ "version": "2.0.4",
169
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.4.tgz",
170
+ "integrity": "sha512-Ut3y0dMMPWy6bZ2kVfx25EOVbZlm15dhF4mOsezMlhpNHy+4MkU1qN9Y6lnruYi4wPmFzimGX2X7LF/FwHli4A==",
171
  "license": "MIT",
172
  "engines": {
173
  "node": ">=20"
 
176
  "hono": "^4"
177
  }
178
  },
179
+ "node_modules/@jridgewell/resolve-uri": {
180
+ "version": "3.1.2",
181
+ "dev": true,
182
+ "license": "MIT",
183
+ "engines": {
184
+ "node": ">=6.0.0"
185
+ }
186
+ },
187
  "node_modules/@jridgewell/sourcemap-codec": {
188
  "version": "1.5.5",
 
 
189
  "dev": true,
190
  "license": "MIT"
191
  },
192
+ "node_modules/@jridgewell/trace-mapping": {
193
+ "version": "0.3.9",
194
+ "dev": true,
195
+ "license": "MIT",
196
+ "dependencies": {
197
+ "@jridgewell/resolve-uri": "^3.0.3",
198
+ "@jridgewell/sourcemap-codec": "^1.4.10"
199
+ }
200
+ },
201
  "node_modules/@langchain/anthropic": {
202
+ "version": "1.4.0",
203
+ "resolved": "https://registry.npmjs.org/@langchain/anthropic/-/anthropic-1.4.0.tgz",
204
+ "integrity": "sha512-rs1yVydrHjyiD31uChdCnKZpmDuKa0Bpz8Raiy9GvqnqmfXPMe0oOrap/2paE+NRSinDbtax8mMpP/yv8EbO1A==",
205
  "license": "MIT",
206
  "dependencies": {
207
+ "@anthropic-ai/sdk": "^0.95.1",
208
  "zod": "^3.25.76 || ^4"
209
  },
210
  "engines": {
211
  "node": ">=20"
212
  },
213
  "peerDependencies": {
214
+ "@langchain/core": "^1.1.47"
215
  }
216
  },
217
  "node_modules/@langchain/core": {
218
+ "version": "1.1.47",
 
 
219
  "license": "MIT",
220
  "dependencies": {
221
  "@cfworker/json-schema": "^4.0.2",
 
247
  },
248
  "node_modules/@langchain/langgraph": {
249
  "version": "1.3.2",
 
 
250
  "license": "MIT",
251
  "dependencies": {
252
  "@langchain/langgraph-checkpoint": "^1.0.2",
 
271
  },
272
  "node_modules/@langchain/langgraph-checkpoint": {
273
  "version": "1.0.2",
 
 
274
  "license": "MIT",
275
  "dependencies": {
276
  "uuid": "^10.0.0"
 
297
  }
298
  },
299
  "node_modules/@langchain/langgraph-sdk": {
300
+ "version": "1.9.4",
 
 
301
  "license": "MIT",
302
  "dependencies": {
303
  "@langchain/protocol": "^0.0.15",
 
330
  },
331
  "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": {
332
  "version": "5.0.4",
 
 
333
  "license": "MIT"
334
  },
335
  "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": {
336
  "version": "9.3.0",
 
 
337
  "license": "MIT",
338
  "dependencies": {
339
  "eventemitter3": "^5.0.4",
 
348
  },
349
  "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": {
350
  "version": "7.0.1",
 
 
351
  "license": "MIT",
352
  "engines": {
353
  "node": ">=20"
 
358
  },
359
  "node_modules/@langchain/langgraph-sdk/node_modules/uuid": {
360
  "version": "13.0.2",
 
 
361
  "funding": [
362
  "https://github.com/sponsors/broofa",
363
  "https://github.com/sponsors/ctavan"
 
382
  }
383
  },
384
  "node_modules/@langchain/openai": {
385
+ "version": "1.4.6",
 
 
386
  "license": "MIT",
387
  "dependencies": {
388
  "js-tiktoken": "^1.0.12",
389
+ "openai": "^6.37.0",
390
  "zod": "^3.25.76 || ^4"
391
  },
392
  "engines": {
393
  "node": ">=20"
394
  },
395
  "peerDependencies": {
396
+ "@langchain/core": "^1.1.47"
397
  }
398
  },
399
  "node_modules/@langchain/openrouter": {
400
  "version": "0.2.4",
 
 
401
  "license": "MIT",
402
  "dependencies": {
403
  "@langchain/openai": "1.4.5",
 
411
  "@langchain/core": "^1.0.0"
412
  }
413
  },
414
+ "node_modules/@langchain/openrouter/node_modules/@langchain/openai": {
415
+ "version": "1.4.5",
 
 
 
 
 
 
 
 
 
416
  "license": "MIT",
 
417
  "dependencies": {
418
+ "js-tiktoken": "^1.0.12",
419
+ "openai": "^6.34.0",
420
+ "zod": "^3.25.76 || ^4"
421
  },
422
+ "engines": {
423
+ "node": ">=20"
 
424
  },
425
  "peerDependencies": {
426
+ "@langchain/core": "^1.1.42"
 
427
  }
428
  },
429
+ "node_modules/@langchain/protocol": {
430
+ "version": "0.0.15",
431
+ "license": "MIT"
432
+ },
433
  "node_modules/@oxc-project/types": {
434
  "version": "0.128.0",
 
 
435
  "dev": true,
436
  "license": "MIT",
437
  "funding": {
438
  "url": "https://github.com/sponsors/Boshen"
439
  }
440
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  "node_modules/@rolldown/binding-linux-x64-gnu": {
442
  "version": "1.0.0-rc.18",
 
 
443
  "cpu": [
444
  "x64"
445
  ],
 
455
  },
456
  "node_modules/@rolldown/binding-linux-x64-musl": {
457
  "version": "1.0.0-rc.18",
 
 
458
  "cpu": [
459
  "x64"
460
  ],
 
468
  "node": "^20.19.0 || >=22.12.0"
469
  }
470
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  "node_modules/@rolldown/pluginutils": {
472
  "version": "1.0.0-rc.18",
 
 
473
  "dev": true,
474
  "license": "MIT"
475
  },
 
489
  "integrity": "sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==",
490
  "license": "MIT"
491
  },
492
+ "node_modules/@stablelib/base64": {
493
+ "version": "1.0.1",
494
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
495
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
496
+ "license": "MIT"
497
+ },
498
  "node_modules/@standard-schema/spec": {
499
  "version": "1.1.0",
 
 
500
  "license": "MIT"
501
  },
502
+ "node_modules/@tsconfig/node10": {
503
+ "version": "1.0.12",
 
 
504
  "dev": true,
505
+ "license": "MIT"
506
+ },
507
+ "node_modules/@tsconfig/node12": {
508
+ "version": "1.0.11",
509
+ "dev": true,
510
+ "license": "MIT"
511
+ },
512
+ "node_modules/@tsconfig/node14": {
513
+ "version": "1.0.3",
514
+ "dev": true,
515
+ "license": "MIT"
516
+ },
517
+ "node_modules/@tsconfig/node16": {
518
+ "version": "1.0.4",
519
+ "dev": true,
520
+ "license": "MIT"
521
  },
522
  "node_modules/@types/chai": {
523
  "version": "5.2.3",
 
 
524
  "dev": true,
525
  "license": "MIT",
526
  "dependencies": {
 
530
  },
531
  "node_modules/@types/deep-eql": {
532
  "version": "4.0.2",
 
 
533
  "dev": true,
534
  "license": "MIT"
535
  },
536
  "node_modules/@types/estree": {
537
  "version": "1.0.9",
 
 
538
  "dev": true,
539
  "license": "MIT"
540
  },
541
  "node_modules/@types/json-schema": {
542
  "version": "7.0.15",
 
 
543
  "license": "MIT"
544
  },
545
  "node_modules/@types/node": {
546
+ "version": "25.9.1",
 
 
547
  "dev": true,
548
  "license": "MIT",
549
  "dependencies": {
550
+ "undici-types": ">=7.24.0 <7.24.7"
551
  }
552
  },
553
  "node_modules/@types/triple-beam": {
 
558
  },
559
  "node_modules/@vitest/expect": {
560
  "version": "4.1.5",
 
 
561
  "dev": true,
562
  "license": "MIT",
563
  "dependencies": {
 
574
  },
575
  "node_modules/@vitest/mocker": {
576
  "version": "4.1.5",
 
 
577
  "dev": true,
578
  "license": "MIT",
579
  "dependencies": {
 
599
  },
600
  "node_modules/@vitest/pretty-format": {
601
  "version": "4.1.5",
 
 
602
  "dev": true,
603
  "license": "MIT",
604
  "dependencies": {
 
610
  },
611
  "node_modules/@vitest/runner": {
612
  "version": "4.1.5",
 
 
613
  "dev": true,
614
  "license": "MIT",
615
  "dependencies": {
 
622
  },
623
  "node_modules/@vitest/snapshot": {
624
  "version": "4.1.5",
 
 
625
  "dev": true,
626
  "license": "MIT",
627
  "dependencies": {
 
636
  },
637
  "node_modules/@vitest/spy": {
638
  "version": "4.1.5",
 
 
639
  "dev": true,
640
  "license": "MIT",
641
  "funding": {
 
644
  },
645
  "node_modules/@vitest/utils": {
646
  "version": "4.1.5",
 
 
647
  "dev": true,
648
  "license": "MIT",
649
  "dependencies": {
 
662
  "dev": true,
663
  "license": "BSD-2-Clause"
664
  },
665
+ "node_modules/acorn": {
666
+ "version": "8.16.0",
667
+ "dev": true,
668
+ "license": "MIT",
669
+ "bin": {
670
+ "acorn": "bin/acorn"
671
+ },
672
+ "engines": {
673
+ "node": ">=0.4.0"
674
+ }
675
+ },
676
+ "node_modules/acorn-walk": {
677
+ "version": "8.3.5",
678
+ "dev": true,
679
+ "license": "MIT",
680
+ "dependencies": {
681
+ "acorn": "^8.11.0"
682
+ },
683
+ "engines": {
684
+ "node": ">=0.4.0"
685
+ }
686
+ },
687
  "node_modules/ansi-styles": {
688
  "version": "4.3.0",
689
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
 
700
  "url": "https://github.com/chalk/ansi-styles?sponsor=1"
701
  }
702
  },
703
+ "node_modules/arg": {
704
+ "version": "4.1.3",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
  "dev": true,
706
  "license": "MIT"
707
  },
708
  "node_modules/assertion-error": {
709
  "version": "2.0.1",
 
 
710
  "dev": true,
711
  "license": "MIT",
712
  "engines": {
 
721
  },
722
  "node_modules/base64-js": {
723
  "version": "1.5.1",
 
 
724
  "funding": [
725
  {
726
  "type": "github",
 
802
  },
803
  "node_modules/chai": {
804
  "version": "6.2.2",
 
 
805
  "dev": true,
806
  "license": "MIT",
807
  "engines": {
 
855
  }
856
  },
857
  "node_modules/color-convert": {
858
+ "version": "2.0.1",
859
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
860
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
861
+ "dev": true,
862
  "license": "MIT",
863
  "dependencies": {
864
+ "color-name": "~1.1.4"
865
  },
866
  "engines": {
867
+ "node": ">=7.0.0"
868
  }
869
  },
870
  "node_modules/color-name": {
871
+ "version": "1.1.4",
872
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
873
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
874
+ "dev": true,
875
+ "license": "MIT"
876
+ },
877
+ "node_modules/color-string": {
878
+ "version": "2.1.4",
879
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
880
+ "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
881
+ "license": "MIT",
882
+ "dependencies": {
883
+ "color-name": "^2.0.0"
884
+ },
885
+ "engines": {
886
+ "node": ">=18"
887
+ }
888
+ },
889
+ "node_modules/color-string/node_modules/color-name": {
890
  "version": "2.1.0",
891
  "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
892
  "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
 
895
  "node": ">=12.20"
896
  }
897
  },
898
+ "node_modules/color/node_modules/color-convert": {
899
+ "version": "3.1.3",
900
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
901
+ "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
902
  "license": "MIT",
903
  "dependencies": {
904
  "color-name": "^2.0.0"
905
  },
906
  "engines": {
907
+ "node": ">=14.6"
908
+ }
909
+ },
910
+ "node_modules/color/node_modules/color-name": {
911
+ "version": "2.1.0",
912
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
913
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
914
+ "license": "MIT",
915
+ "engines": {
916
+ "node": ">=12.20"
917
  }
918
  },
919
  "node_modules/command-exists": {
 
933
  },
934
  "node_modules/convert-source-map": {
935
  "version": "2.0.0",
936
+ "dev": true,
937
+ "license": "MIT"
938
+ },
939
+ "node_modules/create-require": {
940
+ "version": "1.1.1",
941
  "dev": true,
942
  "license": "MIT"
943
  },
 
976
  },
977
  "node_modules/detect-libc": {
978
  "version": "2.1.2",
 
 
979
  "dev": true,
980
  "license": "Apache-2.0",
981
  "engines": {
982
+ "node": ">=8"
983
+ }
984
+ },
985
+ "node_modules/diff": {
986
+ "version": "4.0.4",
987
+ "dev": true,
988
+ "license": "BSD-3-Clause",
989
+ "engines": {
990
+ "node": ">=0.3.1"
991
  }
992
  },
993
  "node_modules/dotenv": {
994
  "version": "17.4.2",
 
 
995
  "license": "BSD-2-Clause",
996
  "engines": {
997
  "node": ">=12"
 
1043
  },
1044
  "node_modules/es-module-lexer": {
1045
  "version": "2.1.0",
 
 
1046
  "dev": true,
1047
  "license": "MIT"
1048
  },
1049
  "node_modules/es-object-atoms": {
1050
+ "version": "1.1.2",
1051
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
1052
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
1053
  "dev": true,
1054
  "license": "MIT",
1055
  "dependencies": {
 
1061
  },
1062
  "node_modules/estree-walker": {
1063
  "version": "3.0.3",
 
 
1064
  "dev": true,
1065
  "license": "MIT",
1066
  "dependencies": {
 
1069
  },
1070
  "node_modules/eventemitter3": {
1071
  "version": "4.0.7",
 
 
1072
  "license": "MIT"
1073
  },
1074
  "node_modules/eventsource-parser": {
1075
  "version": "3.0.8",
 
 
1076
  "license": "MIT",
1077
  "engines": {
1078
  "node": ">=18.0.0"
 
1080
  },
1081
  "node_modules/expect-type": {
1082
  "version": "1.3.0",
 
 
1083
  "dev": true,
1084
  "license": "Apache-2.0",
1085
  "engines": {
1086
  "node": ">=12.0.0"
1087
  }
1088
  },
1089
+ "node_modules/fast-sha256": {
1090
+ "version": "1.3.0",
1091
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
1092
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
1093
+ "license": "Unlicense"
1094
+ },
1095
  "node_modules/fdir": {
1096
  "version": "6.5.0",
 
 
1097
  "dev": true,
1098
  "license": "MIT",
1099
  "engines": {
 
1178
  "node": ">=12"
1179
  }
1180
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1181
  "node_modules/function-bind": {
1182
  "version": "1.1.2",
1183
  "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
 
1297
  }
1298
  },
1299
  "node_modules/hono": {
1300
+ "version": "4.12.23",
1301
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz",
1302
+ "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==",
1303
  "license": "MIT",
1304
  "engines": {
1305
  "node": ">=16.9.0"
 
1329
  },
1330
  "node_modules/is-network-error": {
1331
  "version": "1.3.2",
 
 
1332
  "license": "MIT",
1333
  "engines": {
1334
  "node": ">=16"
 
1394
  },
1395
  "node_modules/js-tiktoken": {
1396
  "version": "1.0.21",
 
 
1397
  "license": "MIT",
1398
  "dependencies": {
1399
  "base64-js": "^1.5.1"
 
1472
  "license": "MIT"
1473
  },
1474
  "node_modules/langchain": {
1475
+ "version": "1.4.0",
 
 
1476
  "license": "MIT",
1477
  "dependencies": {
1478
+ "@langchain/langgraph": "^1.3.0",
1479
  "@langchain/langgraph-checkpoint": "^1.0.1",
1480
  "langsmith": ">=0.5.0 <1.0.0",
1481
  "zod": "^3.25.76 || ^4"
 
1484
  "node": ">=20"
1485
  },
1486
  "peerDependencies": {
1487
+ "@langchain/core": "^1.1.44"
1488
  }
1489
  },
1490
  "node_modules/langsmith": {
1491
  "version": "0.6.2",
 
 
1492
  "license": "MIT",
1493
  "dependencies": {
1494
  "p-queue": "6.6.2"
 
1520
  },
1521
  "node_modules/lightningcss": {
1522
  "version": "1.32.0",
 
 
1523
  "dev": true,
1524
  "license": "MPL-2.0",
1525
  "dependencies": {
 
1546
  "lightningcss-win32-x64-msvc": "1.32.0"
1547
  }
1548
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1549
  "node_modules/lightningcss-linux-x64-gnu": {
1550
  "version": "1.32.0",
 
 
1551
  "cpu": [
1552
  "x64"
1553
  ],
 
1567
  },
1568
  "node_modules/lightningcss-linux-x64-musl": {
1569
  "version": "1.32.0",
 
 
1570
  "cpu": [
1571
  "x64"
1572
  ],
 
1584
  "url": "https://opencollective.com/parcel"
1585
  }
1586
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1587
  "node_modules/logform": {
1588
  "version": "2.7.0",
1589
  "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
 
1603
  },
1604
  "node_modules/magic-string": {
1605
  "version": "0.30.21",
 
 
1606
  "dev": true,
1607
  "license": "MIT",
1608
  "dependencies": {
1609
  "@jridgewell/sourcemap-codec": "^1.5.5"
1610
  }
1611
  },
1612
+ "node_modules/make-error": {
1613
+ "version": "1.3.6",
1614
+ "dev": true,
1615
+ "license": "ISC"
1616
+ },
1617
  "node_modules/math-intrinsics": {
1618
  "version": "1.1.0",
1619
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
 
1677
  },
1678
  "node_modules/mustache": {
1679
  "version": "4.2.0",
 
 
1680
  "license": "MIT",
1681
  "bin": {
1682
  "mustache": "bin/mustache"
 
1684
  },
1685
  "node_modules/nanoid": {
1686
  "version": "3.3.12",
 
 
1687
  "dev": true,
1688
  "funding": [
1689
  {
 
1711
  },
1712
  "node_modules/obug": {
1713
  "version": "2.1.1",
 
 
1714
  "dev": true,
1715
  "funding": [
1716
  "https://github.com/sponsors/sxzz",
 
1745
  }
1746
  },
1747
  "node_modules/openai": {
1748
+ "version": "6.38.0",
 
 
1749
  "license": "Apache-2.0",
1750
  "bin": {
1751
  "openai": "bin/cli"
 
1774
  },
1775
  "node_modules/p-finally": {
1776
  "version": "1.0.0",
 
 
1777
  "license": "MIT",
1778
  "engines": {
1779
  "node": ">=4"
 
1781
  },
1782
  "node_modules/p-queue": {
1783
  "version": "6.6.2",
 
 
1784
  "license": "MIT",
1785
  "dependencies": {
1786
  "eventemitter3": "^4.0.4",
 
1795
  },
1796
  "node_modules/p-retry": {
1797
  "version": "7.1.1",
 
 
1798
  "license": "MIT",
1799
  "dependencies": {
1800
  "is-network-error": "^1.1.0"
 
1808
  },
1809
  "node_modules/p-timeout": {
1810
  "version": "3.2.0",
 
 
1811
  "license": "MIT",
1812
  "dependencies": {
1813
  "p-finally": "^1.0.0"
 
1846
  "npm": ">5"
1847
  }
1848
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1849
  "node_modules/path-key": {
1850
  "version": "3.1.1",
1851
  "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
 
1858
  },
1859
  "node_modules/pathe": {
1860
  "version": "2.0.3",
 
 
1861
  "dev": true,
1862
  "license": "MIT"
1863
  },
1864
  "node_modules/picocolors": {
1865
  "version": "1.1.1",
 
 
1866
  "dev": true,
1867
  "license": "ISC"
1868
  },
1869
  "node_modules/picomatch": {
1870
  "version": "4.0.4",
 
 
1871
  "dev": true,
1872
  "license": "MIT",
1873
  "engines": {
 
1879
  },
1880
  "node_modules/postcss": {
1881
  "version": "8.5.14",
 
 
1882
  "dev": true,
1883
  "funding": [
1884
  {
 
1920
  },
1921
  "node_modules/rolldown": {
1922
  "version": "1.0.0-rc.18",
 
 
1923
  "dev": true,
1924
  "license": "MIT",
1925
  "dependencies": {
 
1980
  }
1981
  },
1982
  "node_modules/semver": {
1983
+ "version": "7.8.1",
1984
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
1985
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
1986
+ "dev": true,
1987
  "license": "ISC",
1988
  "bin": {
1989
+ "semver": "bin/semver.js"
1990
+ },
1991
+ "engines": {
1992
+ "node": ">=10"
1993
  }
1994
  },
1995
  "node_modules/set-function-length": {
 
2035
  },
2036
  "node_modules/siginfo": {
2037
  "version": "2.0.0",
 
 
2038
  "dev": true,
2039
  "license": "ISC"
2040
  },
 
2069
  "node": ">=12.0.0"
2070
  }
2071
  },
2072
+ "node_modules/solc/node_modules/semver": {
2073
+ "version": "5.7.2",
2074
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
2075
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
2076
+ "license": "ISC",
2077
+ "bin": {
2078
+ "semver": "bin/semver"
2079
+ }
2080
+ },
2081
+ "node_modules/solc/node_modules/tmp": {
2082
+ "version": "0.0.33",
2083
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
2084
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
2085
+ "license": "MIT",
2086
+ "dependencies": {
2087
+ "os-tmpdir": "~1.0.2"
2088
+ },
2089
+ "engines": {
2090
+ "node": ">=0.6.0"
2091
+ }
2092
+ },
2093
  "node_modules/source-map-js": {
2094
  "version": "1.2.1",
 
 
2095
  "dev": true,
2096
  "license": "BSD-3-Clause",
2097
  "engines": {
 
2109
  },
2110
  "node_modules/stackback": {
2111
  "version": "0.0.2",
 
 
2112
  "dev": true,
2113
  "license": "MIT"
2114
  },
2115
+ "node_modules/standardwebhooks": {
2116
+ "version": "1.0.0",
2117
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
2118
+ "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
2119
+ "license": "MIT",
2120
+ "dependencies": {
2121
+ "@stablelib/base64": "^1.0.0",
2122
+ "fast-sha256": "^1.3.0"
2123
+ }
2124
+ },
2125
  "node_modules/std-env": {
2126
  "version": "4.1.0",
 
 
2127
  "dev": true,
2128
  "license": "MIT"
2129
  },
 
2157
  },
2158
  "node_modules/tinybench": {
2159
  "version": "2.9.0",
 
 
2160
  "dev": true,
2161
  "license": "MIT"
2162
  },
2163
  "node_modules/tinyexec": {
2164
  "version": "1.1.2",
 
 
2165
  "dev": true,
2166
  "license": "MIT",
2167
  "engines": {
 
2170
  },
2171
  "node_modules/tinyglobby": {
2172
  "version": "0.2.16",
 
 
2173
  "dev": true,
2174
  "license": "MIT",
2175
  "dependencies": {
 
2185
  },
2186
  "node_modules/tinyrainbow": {
2187
  "version": "3.1.0",
 
 
2188
  "dev": true,
2189
  "license": "MIT",
2190
  "engines": {
 
2192
  }
2193
  },
2194
  "node_modules/tmp": {
2195
+ "version": "0.2.5",
2196
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
2197
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
2198
+ "dev": true,
2199
  "license": "MIT",
 
 
 
2200
  "engines": {
2201
+ "node": ">=14.14"
2202
  }
2203
  },
2204
  "node_modules/to-regex-range": {
 
2229
  "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==",
2230
  "license": "MIT"
2231
  },
2232
+ "node_modules/ts-node": {
2233
+ "version": "10.9.2",
 
 
2234
  "dev": true,
2235
+ "license": "MIT",
2236
+ "dependencies": {
2237
+ "@cspotcode/source-map-support": "^0.8.0",
2238
+ "@tsconfig/node10": "^1.0.7",
2239
+ "@tsconfig/node12": "^1.0.7",
2240
+ "@tsconfig/node14": "^1.0.0",
2241
+ "@tsconfig/node16": "^1.0.2",
2242
+ "acorn": "^8.4.1",
2243
+ "acorn-walk": "^8.1.1",
2244
+ "arg": "^4.1.0",
2245
+ "create-require": "^1.1.0",
2246
+ "diff": "^4.0.1",
2247
+ "make-error": "^1.1.1",
2248
+ "v8-compile-cache-lib": "^3.0.1",
2249
+ "yn": "3.1.1"
2250
+ },
2251
+ "bin": {
2252
+ "ts-node": "dist/bin.js",
2253
+ "ts-node-cwd": "dist/bin-cwd.js",
2254
+ "ts-node-esm": "dist/bin-esm.js",
2255
+ "ts-node-script": "dist/bin-script.js",
2256
+ "ts-node-transpile-only": "dist/bin-transpile.js",
2257
+ "ts-script": "dist/bin-script-deprecated.js"
2258
+ },
2259
+ "peerDependencies": {
2260
+ "@swc/core": ">=1.2.50",
2261
+ "@swc/wasm": ">=1.2.50",
2262
+ "@types/node": "*",
2263
+ "typescript": ">=2.7"
2264
+ },
2265
+ "peerDependenciesMeta": {
2266
+ "@swc/core": {
2267
+ "optional": true
2268
+ },
2269
+ "@swc/wasm": {
2270
+ "optional": true
2271
+ }
2272
+ }
2273
  },
2274
  "node_modules/typescript": {
2275
  "version": "6.0.3",
 
 
2276
  "dev": true,
2277
  "license": "Apache-2.0",
2278
  "bin": {
 
2284
  }
2285
  },
2286
  "node_modules/undici-types": {
2287
+ "version": "7.24.6",
 
 
2288
  "dev": true,
2289
  "license": "MIT"
2290
  },
 
2317
  "uuid": "dist/esm/bin/uuid"
2318
  }
2319
  },
2320
+ "node_modules/v8-compile-cache-lib": {
2321
+ "version": "3.0.1",
2322
+ "dev": true,
2323
+ "license": "MIT"
2324
+ },
2325
  "node_modules/vite": {
2326
  "version": "8.0.11",
 
 
2327
  "dev": true,
2328
  "license": "MIT",
2329
  "dependencies": {
 
2400
  },
2401
  "node_modules/vitest": {
2402
  "version": "4.1.5",
 
 
2403
  "dev": true,
2404
  "license": "MIT",
2405
  "dependencies": {
 
2504
  },
2505
  "node_modules/why-is-node-running": {
2506
  "version": "2.3.0",
 
 
2507
  "dev": true,
2508
  "license": "MIT",
2509
  "dependencies": {
 
2569
  "url": "https://github.com/sponsors/eemeli"
2570
  }
2571
  },
2572
+ "node_modules/yn": {
2573
+ "version": "3.1.1",
2574
+ "dev": true,
2575
+ "license": "MIT",
2576
+ "engines": {
2577
+ "node": ">=6"
2578
+ }
2579
+ },
2580
  "node_modules/zod": {
2581
  "version": "4.4.3",
 
 
2582
  "license": "MIT",
2583
  "funding": {
2584
  "url": "https://github.com/sponsors/colinhacks"
package.json CHANGED
@@ -25,8 +25,9 @@
25
  "homepage": "https://github.com/uandersonricardo/projeto-talp1#readme",
26
  "devDependencies": {
27
  "@biomejs/biome": "2.4.14",
28
- "@types/node": "^25.6.0",
29
  "patch-package": "^8.0.1",
 
30
  "typescript": "^6.0.3",
31
  "vitest": "^4.1.5"
32
  },
@@ -35,7 +36,8 @@
35
  "@langchain/anthropic": "^1.3.29",
36
  "@langchain/core": "^1.1.45",
37
  "@langchain/google-genai": "^2.1.31",
38
- "@langchain/langgraph": "^1.3.0",
 
39
  "@langchain/openrouter": "^0.2.4",
40
  "@solidity-parser/parser": "^0.20.2",
41
  "dotenv": "^17.4.2",
 
25
  "homepage": "https://github.com/uandersonricardo/projeto-talp1#readme",
26
  "devDependencies": {
27
  "@biomejs/biome": "2.4.14",
28
+ "@types/node": "^25.9.1",
29
  "patch-package": "^8.0.1",
30
+ "ts-node": "^10.9.2",
31
  "typescript": "^6.0.3",
32
  "vitest": "^4.1.5"
33
  },
 
36
  "@langchain/anthropic": "^1.3.29",
37
  "@langchain/core": "^1.1.45",
38
  "@langchain/google-genai": "^2.1.31",
39
+ "@langchain/langgraph": "^1.3.2",
40
+ "@langchain/openai": "^1.4.6",
41
  "@langchain/openrouter": "^0.2.4",
42
  "@solidity-parser/parser": "^0.20.2",
43
  "dotenv": "^17.4.2",
scripts/setup-sandbox.sh ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ SANDBOX="/tmp/poc-sandbox"
5
+
6
+ # Tenta encontrar forge no PATH se a variável não estiver definida ou falhar
7
+ if [ -z "$FORGE_BIN" ] || [ ! -f "$FORGE_BIN" ]; then
8
+ FORGE_BIN=$(which forge || echo "forge")
9
+ fi
10
+
11
+ echo "Inicializando sandbox Foundry em $SANDBOX usando $FORGE_BIN..."
12
+ rm -rf "$SANDBOX"
13
+ mkdir -p "$SANDBOX"
14
+ cd "$SANDBOX"
15
+
16
+ # Iniciar projeto forge mínimo sem git
17
+ "$FORGE_BIN" init --no-git --quiet
18
+
19
+ # Limpar arquivos padrão que causam erros de importação se deletados parcialmente
20
+ rm -rf src/*
21
+ rm -rf test/*
22
+ rm -rf script/*
23
+
24
+ # Criar foundry.toml configurado
25
+ cat > foundry.toml << 'EOF'
26
+ [profile.default]
27
+ src = "src"
28
+ test = "test"
29
+ script = "script"
30
+ out = "out"
31
+ libs = ["lib"]
32
+ solc_version = "0.8.20"
33
+ optimizer = true
34
+ optimizer_runs = 200
35
+ EOF
36
+
37
+ echo "Sandbox pronto. Testando com forge build..."
38
+ "$FORGE_BIN" build
39
+ echo "OK — sandbox funcionando em $SANDBOX"
src/agents/tester/Docs/01_ARCHITECTURE(1).md ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Agente Gerador de PoCs — Arquitetura e Fluxo de Dados
2
+
3
+ **Projeto:** TALP1 — CIn/UFPE
4
+ **Agente:** Agente Gerador de PoCs
5
+ **Responsável:** Tales Vinicius Alves da Cunha
6
+ **Stack:** TypeScript · Node.js · LangGraph · Foundry
7
+
8
+ ---
9
+
10
+ ## 1. Visão Geral
11
+
12
+ O Agente Gerador de PoCs recebe um relatório de vulnerabilidade estruturado (JSON) do Agente Auditor e produz automaticamente um exploit em Solidity verificado pelo Foundry. O agente executa um **loop ReAct**: gerar → executar → refletir → repetir, até que o exploit passe nos testes ou o limite de iterações seja atingido.
13
+
14
+ Diferente de abordagens de "mainnet fork", este agente foca em **simulação local controlada** (abordagem inspirada no PoCo — Bergman et al., KTH 2025), onde o ambiente é montado do zero para cada ataque.
15
+
16
+ > **Diferencial em relação ao PoCo:** o PoCo deixava o LLM escrever o `setUp()` do Foundry livremente, o que gerava erros frequentes de instanciação. Este agente introduz o **Oracle** como camada dedicada de preparação do ambiente, fornecendo um scaffold com deploy automático do contrato vítima — o LLM foca exclusivamente na lógica do exploit.
17
+
18
+ ---
19
+
20
+ ## 2. Posição no Sistema Multi-agente
21
+
22
+ ```
23
+ Requisitos (PDF/MD)
24
+
25
+
26
+ ┌─────────────────────┐
27
+ │ Agente Gerador │ ── Compiler, RAG
28
+ │ de Código │
29
+ └──────────┬──────────┘
30
+ │ Repositório Solidity
31
+
32
+ ┌─────────────────────┐
33
+ │ Agente Auditor │ ── Slither, AST
34
+ └──────────┬──────────┘
35
+ │ Relatório de Vulnerabilidades (JSON)
36
+
37
+ ┌─────────────────────┐
38
+ │ Agente Gerador │ ── Local Oracle, Foundry ◄─── você está aqui
39
+ │ de PoCs │
40
+ └──────────┬──────────┘
41
+ │ Exploit.t.sol (Projeto Solidity)
42
+
43
+ Projeto Final
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 3. Arquitetura Interna do Agente
49
+
50
+ ### 3.1 Fluxo Principal (grafo LangGraph)
51
+
52
+ ```
53
+ ┌─────────────────────────────────┐
54
+ │ ESTADO DO AGENTE │
55
+ │ report · oracleContext · pocCode │
56
+ │ executionLogs · lastError │
57
+ │ iterations · status │
58
+ └─────────────────────────────────┘
59
+
60
+ Auditor Report (JSON)
61
+
62
+
63
+ ┌───────────────────┐
64
+ │ oracleNode │ ← Preparação do ambiente: gera o setup inicial local
65
+ └────────┬──────────┘
66
+ │ OracleContext (scaffold Solidity com deploy local da vítima)
67
+
68
+ ┌───────────────────┐ ┌──────────────────────┐
69
+ │ generatePoCNode │ ◄───────│ reflectNode │
70
+ │ (LLM + prompts) │ │ (análise de logs) │
71
+ └────────┬──────────┘ └──────────▲────────────┘
72
+ │ Solidity code │ feedback estruturado
73
+ ▼ │ (categoria de erro + resumo)
74
+ ┌───────────────────┐ FAIL / ERROR │
75
+ │ runFoundryNode │────────────────────┘
76
+ │ (forge test -vvvv)│
77
+ └────────┬──────────┘
78
+
79
+ ┌────┴────┐
80
+ PASS FAIL (≥5 iterações ou timeout)
81
+ │ │
82
+ ▼ ▼
83
+ END END
84
+ (success) (failed)
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 4. O Oracle — O Que É e Por Que Existe
90
+
91
+ ### 4.1 Contexto
92
+
93
+ No contexto deste agente, o Oracle é um **gerador de ambiente de teste**. Ao invés de buscar dados na blockchain real, ele prepara um "sandbox" local onde o contrato vulnerável é implantado e financiado automaticamente.
94
+
95
+ ### 4.2 Problema que o Oracle resolve
96
+
97
+ O LLM muitas vezes tem dificuldade em escrever a função `setUp()` do Foundry porque não sabe como instanciar o contrato vítima ou dar saldo ao atacante. O Oracle resolve isso fornecendo um **scaffold (template)** pronto, permitindo que o LLM foque exclusivamente na lógica do exploit.
98
+
99
+ ### 4.3 Os 2 sub-tools do Oracle
100
+
101
+ ```
102
+ oracleNode
103
+
104
+ ├── 1. stateInitializer → Define saldos e condições iniciais (ex: 100 ETH para a vítima)
105
+
106
+ └── 2. scaffoldGenerator → Gera o Exploit.t.sol com o deploy do contrato e setUp() pronto
107
+ Retorna: string (código Solidity parcial)
108
+ ```
109
+
110
+ ---
111
+
112
+ ## 5. Fluxo de Dados Completo (entrada → saída)
113
+
114
+ ### 5.1 Input: VulnerabilityReport (do Agente Auditor)
115
+
116
+ ```typescript
117
+ interface VulnerabilityReport {
118
+ id: string;
119
+ severity: "critical" | "high" | "medium" | "low";
120
+ type: string;
121
+ title: string;
122
+ description: string;
123
+ affectedContract: {
124
+ name: string;
125
+ sourceCode: string; // código Solidity completo (preferencialmente flattened)
126
+ };
127
+ attackVector: string;
128
+ suggestedCheatcodes?: string[];
129
+ }
130
+ ```
131
+
132
+ ### 5.2 Output: PoCResult
133
+
134
+ ```typescript
135
+ interface PoCResult {
136
+ reportId: string;
137
+ status: "success" | "failed" | "timeout";
138
+ solidityCode: string; // conteúdo final do Exploit.t.sol
139
+ executionLogs: string[];
140
+ iterations: number;
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## 6. Estrutura de Arquivos
147
+
148
+ ```
149
+ src/agents/poc-generator/
150
+ ├── agent.ts # grafo LangGraph, nodes, roteamento
151
+ ├── state.ts # PoCStateAnnotation
152
+
153
+ ├── tools/
154
+ │ ├── scaffoldGenerator.ts # Oracle sub-tool (gera template local)
155
+ │ └── foundryRunner.ts # executa forge test via child_process
156
+
157
+ ├── prompts/
158
+ │ └── system.ts # system prompt do LLM gerador
159
+
160
+ └── utils/
161
+ ├── extractSolidity.ts # parser do output do LLM
162
+ └── logAnalyzer.ts # classifica erros do forge
163
+ ```
164
+
165
+ ---
166
+
167
+ ## 7. Variáveis de Ambiente
168
+
169
+ ```env
170
+ OPENROUTER_API_KEY=... # chave do LLM
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 8. Riscos e Mitigações
176
+
177
+ | Risco | Mitigação |
178
+ |-------|-----------|
179
+ | LLM reescreve o scaffold ao invés de completar | System prompt proíbe explicitamente modificar `setUp()`; validação pós-extração |
180
+ | Contrato vítima tem muitas dependências | Auditor deve fornecer código "flattened"; Oracle lida com imports locais no sandbox |
181
+ | LLM não gera bloco Solidity válido | `extractSolidity` lança erro; `generatePoCNode` captura e retenta |
182
+ | Timeout no Foundry | Limite de 60s por execução; análise de loops infinitos no `logAnalyzer` |
183
+
184
+ ---
185
+
186
+ ## 9. Base Acadêmica
187
+
188
+ - **PoCo** (Bergman et al., KTH 2025) — framework agêntico para geração de PoC exploits em smart contracts. Artefatos: `ASSERT-KTH/PoCo-public`
189
+ - **Proof-of-Patch** (ASSERT-KTH) — dataset de 23 vulnerabilidades reais (2022–2025) com patches correspondentes, usado como benchmark de avaliação
src/agents/tester/Docs/02_ROADMAP(1).md ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Agente Gerador de PoCs — Plano de Implementação (Roadmap)
2
+
3
+ **Projeto:** TALP1 — CIn/UFPE
4
+ **Agente:** Agente Gerador de PoCs
5
+ **Responsável:** Tales Vinicius Alves da Cunha
6
+
7
+ ---
8
+
9
+ ## Visão Geral das Fases
10
+
11
+ | Fase | Tema | Semana | Critério de Conclusão |
12
+ |------|------|--------|----------------------|
13
+ | 1 | Setup, Estado & Oracle | Semana 1 | Grafo linear roda com stubs; `oracleNode` gera scaffold que compila com `forge build` |
14
+ | 2 | LLM + Foundry + Loop ReAct | Semana 2 | Pipeline completo roda: LLM gera → Foundry executa → loop corrige ao menos 1 erro |
15
+ | 3 | Integração, Smoke Test & Avaliação | Semana 3 | PoC de reentrancy passa end-to-end; taxa de sucesso medida em ≥5 casos do benchmark |
16
+
17
+ ---
18
+
19
+ ## Semana 1 — Setup, Estado & Oracle
20
+
21
+ **Objetivo:** Ter o grafo LangGraph rodando com o Oracle funcional.
22
+
23
+ ### Tasks
24
+ - **Task 1.1** — Inicializar o projeto TypeScript (tsconfig, dependências LangGraph, Foundry local)
25
+ - **Task 1.2** — Definir o estado do agente (`PoCStateAnnotation`) e interfaces (`VulnerabilityReport`, `PoCResult`)
26
+ - **Task 1.3** — Criar nodes stub e grafo linear (sem LLM ainda)
27
+ - **Task 1.4** — Implementar `scaffoldGenerator` (gera `setUp()` com deploy local do contrato vítima)
28
+ - **Task 1.5** — Implementar `oracleNode` (integra `stateInitializer` + `scaffoldGenerator`)
29
+
30
+ **Gate:** `oracleNode` recebe um `VulnerabilityReport` fake e retorna scaffold que passa em `forge build`
31
+
32
+ ---
33
+
34
+ ## Semana 2 — LLM + Foundry + Loop ReAct
35
+
36
+ **Objetivo:** Pipeline completo rodando com loop de correção.
37
+
38
+ ### Tasks
39
+ - **Task 2.1** — Criar o system prompt (`prompts/system.ts`)
40
+ - **Task 2.2** — Implementar `extractSolidity` (parser do output do LLM)
41
+ - **Task 2.3** — Implementar `generatePoCNode` (chamada LLM + retry em caso de bloco Solidity inválido)
42
+ - **Task 2.4** — Setup do sandbox Foundry em `/tmp/poc-sandbox/`
43
+ - **Task 2.5** — Implementar `foundryRunner` (executa `forge test -vvvv` via `child_process`, retorna output estruturado)
44
+ - **Task 2.6** — Implementar `logAnalyzer` (classifica erros: compilation / assertion / timeout)
45
+ - **Task 2.7** — Implementar `reflectNode` (LLM analisa logs e produz feedback estruturado)
46
+ - **Task 2.8** — Implementar `routeAfterFoundry` (router condicional: pass → END, fail → reflect → generate)
47
+
48
+ **Gate:** Agente faz ≥2 iterações completas e melhora o código após erro de compilação
49
+
50
+ ---
51
+
52
+ ## Semana 3 — Integração, Smoke Test & Avaliação
53
+
54
+ **Objetivo:** Pipeline validado end-to-end com métricas.
55
+
56
+ ### Tasks
57
+ - **Task 3.1** — Definir interface pública (`runPoCGenerator`)
58
+ - **Task 3.2** — Smoke test com reentrancy simples (contrato vítima hardcoded)
59
+ - **Task 3.3** — Preparar `benchmark.json` com ≥5 casos do dataset Proof-of-Patch (ASSERT-KTH)
60
+ - **Task 3.4** — Implementar `evaluate.ts` (roda agente em batch, coleta status/iterations/logs)
61
+
62
+ **Gate:** PoC de reentrancy passa end-to-end; taxa de sucesso medida e documentada
63
+
64
+ ---
65
+
66
+ ## Critérios de Conclusão (GATES)
67
+
68
+ | Semana | Critério de Conclusão |
69
+ |--------|------------------------------|
70
+ | Semana 1 | `oracleNode` gera scaffold que compila sozinho com `forge build` |
71
+ | Semana 2 | Loop ReAct faz ≥2 iterações e produz correção após erro |
72
+ | Semana 3 | PoC de reentrancy passa end-to-end; benchmark com ≥5 casos executado |
src/agents/tester/Docs/03_TASKS(2).md ADDED
@@ -0,0 +1,1338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Agente Gerador de PoCs — Tasks (formato Jira)
2
+
3
+ **Projeto:** TALP1 — CIn/UFPE
4
+ **Agente:** Agente Gerador de PoCs
5
+ **Responsável:** Tales Vinicius Alves da Cunha
6
+ **Stack:** TypeScript · Node.js · LangGraph · Foundry
7
+
8
+ ---
9
+
10
+ ## SEMANA 1 — Setup, Estado & Oracle
11
+
12
+ ---
13
+
14
+ ### TALP-1.1 — Inicializar o projeto TypeScript
15
+
16
+ | Campo | Valor |
17
+ |-------|-------|
18
+ | **Tipo** | Setup |
19
+ | **Prioridade** | Crítica |
20
+ | **Estimativa** | 1h |
21
+ | **Depende de** | — |
22
+
23
+ **Descrição**
24
+ Criar a estrutura base do projeto TypeScript com todas as dependências necessárias para rodar o agente LangGraph com Foundry.
25
+
26
+ **Arquivos a criar**
27
+ ```
28
+ src/agents/poc-generator/ ← criar diretório
29
+ tsconfig.json ← criar na raiz
30
+ package.json ← atualizar
31
+ ```
32
+
33
+ **Setup**
34
+ ```bash
35
+ mkdir -p src/agents/poc-generator/tools
36
+ mkdir -p src/agents/poc-generator/prompts
37
+ mkdir -p src/agents/poc-generator/utils
38
+ mkdir -p tests/e2e
39
+ mkdir -p scripts
40
+ mkdir -p data
41
+
42
+ npm install @langchain/langgraph @langchain/openai zod
43
+ npm install -D typescript ts-node @types/node
44
+ ```
45
+
46
+ `tsconfig.json`:
47
+ ```json
48
+ {
49
+ "compilerOptions": {
50
+ "target": "ES2022",
51
+ "module": "Node16",
52
+ "moduleResolution": "node16",
53
+ "strict": true,
54
+ "outDir": "dist",
55
+ "rootDir": "src",
56
+ "esModuleInterop": true
57
+ }
58
+ }
59
+ ```
60
+
61
+ **Critérios de aceitação**
62
+ - [ ] `npx tsc --noEmit` roda sem erros em um arquivo vazio em `src/agents/poc-generator/agent.ts`
63
+ - [ ] Todas as dependências aparecem no `package.json`
64
+ - [ ] Estrutura de diretórios criada conforme acima
65
+
66
+ **Como testar**
67
+ ```bash
68
+ npx tsc --noEmit # deve sair com código 0
69
+ ls src/agents/poc-generator/tools/ # deve existir
70
+ ```
71
+
72
+ ---
73
+
74
+ ### TALP-1.2 — Definir interfaces e estado do agente
75
+
76
+ | Campo | Valor |
77
+ |-------|-------|
78
+ | **Tipo** | Implementação |
79
+ | **Prioridade** | Crítica |
80
+ | **Estimativa** | 2h |
81
+ | **Depende de** | TALP-1.1 |
82
+
83
+ **Descrição**
84
+ Criar os tipos TypeScript que definem o contrato de dados do agente: o que entra (`VulnerabilityReport`), o que sai (`PoCResult`), e o estado interno do grafo LangGraph (`PoCStateAnnotation`).
85
+
86
+ **Arquivos a criar**
87
+ ```
88
+ src/agents/poc-generator/types.ts ← interfaces de input/output
89
+ src/agents/poc-generator/state.ts ← PoCStateAnnotation (LangGraph)
90
+ ```
91
+
92
+ **Implementação — `types.ts`**
93
+ ```typescript
94
+ export interface VulnerabilityReport {
95
+ id: string;
96
+ severity: "critical" | "high" | "medium" | "low";
97
+ type: string;
98
+ title: string;
99
+ description: string;
100
+ affectedContract: {
101
+ name: string;
102
+ sourceCode: string; // Solidity completo, preferencialmente flattened
103
+ };
104
+ attackVector: string;
105
+ suggestedCheatcodes?: string[];
106
+ }
107
+
108
+ export interface OracleContext {
109
+ solidityScaffold: string; // Exploit.t.sol parcial com setUp() pronto
110
+ }
111
+
112
+ export interface PoCResult {
113
+ reportId: string;
114
+ status: "success" | "failed" | "timeout";
115
+ solidityCode: string;
116
+ executionLogs: string[];
117
+ iterations: number;
118
+ }
119
+ ```
120
+
121
+ **Implementação — `state.ts`**
122
+ ```typescript
123
+ import { Annotation } from "@langchain/langgraph";
124
+ import { VulnerabilityReport, OracleContext } from "./types";
125
+
126
+ export const PoCStateAnnotation = Annotation.Root({
127
+ report: Annotation<VulnerabilityReport>(),
128
+
129
+ oracleContext: Annotation<OracleContext | null>({
130
+ default: () => null,
131
+ reducer: (_, y) => y, // overwrite — preenchido 1x pelo oracleNode
132
+ }),
133
+
134
+ pocCode: Annotation<string>({
135
+ default: () => "",
136
+ reducer: (_, y) => y, // overwrite — sempre a versão mais recente
137
+ }),
138
+
139
+ executionLogs: Annotation<string[]>({
140
+ default: () => [],
141
+ reducer: (x, y) => x.concat(y), // append — nunca perde logs anteriores
142
+ }),
143
+
144
+ lastError: Annotation<string | null>({
145
+ default: () => null,
146
+ reducer: (_, y) => y, // overwrite — última análise de erro
147
+ }),
148
+
149
+ iterations: Annotation<number>({
150
+ default: () => 0,
151
+ reducer: (x, y) => x + y, // aditivo — incrementado em +1 por chamada
152
+ }),
153
+
154
+ status: Annotation<"running" | "success" | "failed" | "timeout">({
155
+ default: () => "running",
156
+ reducer: (_, y) => y, // overwrite
157
+ }),
158
+ });
159
+
160
+ export type PoCState = typeof PoCStateAnnotation.State;
161
+ ```
162
+
163
+ **Critérios de aceitação**
164
+ - [ ] `npx tsc --noEmit` passa sem erros
165
+ - [ ] `iterations` usa reducer aditivo (não overwrite)
166
+ - [ ] `executionLogs` usa reducer de append (nunca trunca histórico)
167
+ - [ ] `oracleContext` usa overwrite mas default é `null`
168
+ - [ ] Todos os campos têm `default` e `reducer` definidos
169
+
170
+ **Como testar**
171
+ ```bash
172
+ npx tsc --noEmit
173
+ ```
174
+ Criar arquivo de teste manual `tests/state.test.ts`:
175
+ ```typescript
176
+ import { PoCStateAnnotation } from "../src/agents/poc-generator/state";
177
+ const s = PoCStateAnnotation.spec;
178
+ console.assert(s.iterations !== undefined, "iterations deve existir");
179
+ console.log("Estado OK");
180
+ ```
181
+
182
+ ---
183
+
184
+ ### TALP-1.3 — Criar nodes stub e grafo linear
185
+
186
+ | Campo | Valor |
187
+ |-------|-------|
188
+ | **Tipo** | Implementação |
189
+ | **Prioridade** | Crítica |
190
+ | **Estimativa** | 2h |
191
+ | **Depende de** | TALP-1.2 |
192
+
193
+ **Descrição**
194
+ Criar o grafo LangGraph com quatro nodes stub (sem lógica real ainda) conectados linearmente. Objetivo: validar que o grafo compila, executa e passa o estado corretamente entre os nodes.
195
+
196
+ **Arquivos a criar/modificar**
197
+ ```
198
+ src/agents/poc-generator/agent.ts ← criar
199
+ ```
200
+
201
+ **Implementação**
202
+ ```typescript
203
+ import { StateGraph, END, START } from "@langchain/langgraph";
204
+ import { PoCStateAnnotation, PoCState } from "./state";
205
+
206
+ async function oracleNode(state: PoCState): Promise<Partial<PoCState>> {
207
+ console.log("[oracleNode] stub — report recebido:", state.report.id);
208
+ return {};
209
+ }
210
+
211
+ async function generatePoCNode(state: PoCState): Promise<Partial<PoCState>> {
212
+ console.log("[generatePoCNode] stub — iteração:", state.iterations);
213
+ return { iterations: 1 };
214
+ }
215
+
216
+ async function runFoundryNode(state: PoCState): Promise<Partial<PoCState>> {
217
+ console.log("[runFoundryNode] stub");
218
+ return { status: "success" };
219
+ }
220
+
221
+ async function reflectNode(state: PoCState): Promise<Partial<PoCState>> {
222
+ console.log("[reflectNode] stub");
223
+ return {};
224
+ }
225
+
226
+ const graph = new StateGraph(PoCStateAnnotation)
227
+ .addNode("oracleNode", oracleNode)
228
+ .addNode("generatePoCNode", generatePoCNode)
229
+ .addNode("runFoundryNode", runFoundryNode)
230
+ .addNode("reflectNode", reflectNode)
231
+ .addEdge(START, "oracleNode")
232
+ .addEdge("oracleNode", "generatePoCNode")
233
+ .addEdge("generatePoCNode", "runFoundryNode")
234
+ .addEdge("runFoundryNode", END);
235
+
236
+ export const pocGeneratorAgent = graph.compile();
237
+ ```
238
+
239
+ **Critérios de aceitação**
240
+ - [ ] `pocGeneratorAgent.invoke({ report: mockReport })` executa sem erros
241
+ - [ ] Console exibe os 4 nomes de nodes em ordem correta
242
+ - [ ] Estado final tem `status: "success"` e `iterations: 1`
243
+ - [ ] `npx tsc --noEmit` passa
244
+
245
+ **Como testar**
246
+ ```typescript
247
+ // tests/stub-run.ts
248
+ import { pocGeneratorAgent } from "../src/agents/poc-generator/agent";
249
+ const mockReport = {
250
+ id: "test-stub", severity: "high" as const, type: "reentrancy",
251
+ title: "Test", description: "Test", attackVector: "Test",
252
+ affectedContract: { name: "Test", sourceCode: "pragma solidity ^0.8.0;" }
253
+ };
254
+ const result = await pocGeneratorAgent.invoke({ report: mockReport });
255
+ console.assert(result.status === "success", "status deve ser success");
256
+ console.assert(result.iterations === 1, "iterations deve ser 1");
257
+ console.log("Grafo stub OK:", result.status);
258
+ ```
259
+ ```bash
260
+ npx ts-node tests/stub-run.ts
261
+ ```
262
+
263
+ ---
264
+
265
+ ### TALP-1.4 — Implementar `scaffoldGenerator`
266
+
267
+ | Campo | Valor |
268
+ |-------|-------|
269
+ | **Tipo** | Implementação |
270
+ | **Prioridade** | Crítica |
271
+ | **Estimativa** | 3h |
272
+ | **Depende de** | TALP-1.2 |
273
+
274
+ **Descrição**
275
+ Implementar a função que gera o scaffold Solidity com `setUp()` pronto. O LLM receberá este arquivo parcial e precisará completar apenas a função `test_Exploit()`. Isso elimina o erro mais comum do PoCo: o LLM instanciar o contrato vítima de forma incorreta.
276
+
277
+ **Arquivos a criar**
278
+ ```
279
+ src/agents/poc-generator/tools/scaffoldGenerator.ts
280
+ ```
281
+
282
+ **Implementação**
283
+ ```typescript
284
+ import { VulnerabilityReport } from "../types";
285
+
286
+ export function generateLocalScaffold(report: VulnerabilityReport): string {
287
+ const cheatcodes = report.suggestedCheatcodes?.join(", ") ?? "vm.deal, vm.prank, vm.warp";
288
+
289
+ return `// SPDX-License-Identifier: UNLICENSED
290
+ pragma solidity ^0.8.20;
291
+
292
+ import "forge-std/Test.sol";
293
+ import "forge-std/console.sol";
294
+
295
+ // ── Código-fonte do contrato vulnerável ──────────────────────────────────────
296
+ ${report.affectedContract.sourceCode}
297
+ // ─────────────────────────────────────────────────────────────────────────────
298
+
299
+ contract ExploitTest is Test {
300
+ ${report.affectedContract.name} target;
301
+ address constant ATTACKER = address(0xBEEF);
302
+
303
+ // setUp() gerado automaticamente pelo Oracle — NÃO MODIFICAR
304
+ function setUp() public {
305
+ target = new ${report.affectedContract.name}();
306
+ vm.deal(address(target), 100 ether);
307
+ vm.deal(ATTACKER, 10 ether);
308
+ vm.label(address(target), "TARGET");
309
+ vm.label(ATTACKER, "ATTACKER");
310
+ }
311
+
312
+ // Vulnerabilidade: ${report.title}
313
+ // Tipo: ${report.type}
314
+ // Vetor: ${report.attackVector}
315
+ // Cheatcodes sugeridos: ${cheatcodes}
316
+ //
317
+ // COMPLETE APENAS ESTA FUNÇÃO — não altere setUp() nem os campos acima
318
+ function test_Exploit() public {
319
+ vm.startPrank(ATTACKER);
320
+ // TODO: implementar exploit aqui
321
+ vm.stopPrank();
322
+ }
323
+ }`.trim();
324
+ }
325
+ ```
326
+
327
+ **Critérios de aceitação**
328
+ - [ ] Output é Solidity sintaticamente válido (passa `forge build`)
329
+ - [ ] `setUp()` inclui `vm.deal` para target (100 ETH) e ATTACKER (10 ETH)
330
+ - [ ] Comentários indicam claramente o que o LLM deve completar
331
+ - [ ] `suggestedCheatcodes` aparece no scaffold quando presentes no report
332
+ - [ ] Contrato vítima é incluído inline (sem imports externos)
333
+
334
+ **Como testar**
335
+ ```bash
336
+ # 1. Gerar o scaffold manualmente
337
+ npx ts-node -e "
338
+ import { generateLocalScaffold } from './src/agents/poc-generator/tools/scaffoldGenerator';
339
+ const scaffold = generateLocalScaffold({
340
+ id:'t1', severity:'high', type:'reentrancy', title:'Reentrancy em withdraw()',
341
+ description:'...', attackVector:'callback malicioso',
342
+ affectedContract: { name: 'VulnerableBank', sourceCode: \`
343
+ pragma solidity ^0.8.20;
344
+ contract VulnerableBank {
345
+ mapping(address=>uint) public balances;
346
+ function deposit() external payable { balances[msg.sender] += msg.value; }
347
+ function withdraw() external {
348
+ uint a = balances[msg.sender];
349
+ (bool ok,) = msg.sender.call{value:a}('');
350
+ require(ok); balances[msg.sender] = 0;
351
+ }
352
+ }\`}
353
+ });
354
+ console.log(scaffold);
355
+ " > /tmp/poc-sandbox/test/Exploit.t.sol
356
+
357
+ # 2. Verificar compilação
358
+ cd /tmp/poc-sandbox && forge build
359
+ ```
360
+
361
+ ---
362
+
363
+ ### TALP-1.5 — Implementar `oracleNode`
364
+
365
+ | Campo | Valor |
366
+ |-------|-------|
367
+ | **Tipo** | Implementação |
368
+ | **Prioridade** | Crítica |
369
+ | **Estimativa** | 1h |
370
+ | **Depende de** | TALP-1.3, TALP-1.4 |
371
+
372
+ **Descrição**
373
+ Substituir o stub do `oracleNode` pela implementação real que chama o `scaffoldGenerator` e persiste o resultado no estado.
374
+
375
+ **Arquivos a modificar**
376
+ ```
377
+ src/agents/poc-generator/agent.ts ← substituir stub do oracleNode
378
+ ```
379
+
380
+ **Implementação**
381
+ ```typescript
382
+ import { generateLocalScaffold } from "./tools/scaffoldGenerator";
383
+
384
+ async function oracleNode(state: PoCState): Promise<Partial<PoCState>> {
385
+ console.log("[oracleNode] gerando scaffold para:", state.report.title);
386
+
387
+ const solidityScaffold = generateLocalScaffold(state.report);
388
+ const oracleContext: OracleContext = { solidityScaffold };
389
+
390
+ console.log("[oracleNode] scaffold gerado, tamanho:", solidityScaffold.length, "chars");
391
+ return { oracleContext };
392
+ }
393
+ ```
394
+
395
+ **Critérios de aceitação**
396
+ - [ ] `oracleContext` não é mais `null` após a execução do node
397
+ - [ ] Scaffold gerado passa `forge build` sem erros de compilação
398
+ - [ ] Não há chamadas de rede, RPC ou I/O externo neste node
399
+ - [ ] Log mostra o título do report e o tamanho do scaffold
400
+
401
+ **Gate da Semana 1:** Rodar o grafo stub com um `VulnerabilityReport` fake e verificar que `oracleContext.solidityScaffold` compila com `forge build`.
402
+
403
+ ```bash
404
+ # Teste do gate
405
+ npx ts-node tests/stub-run.ts
406
+ # Copiar o scaffold para o sandbox e compilar
407
+ cd /tmp/poc-sandbox && forge build
408
+ ```
409
+
410
+ ---
411
+
412
+ ## SEMANA 2 — LLM + Foundry + Loop ReAct
413
+
414
+ ---
415
+
416
+ ### TALP-2.1 — Criar o system prompt
417
+
418
+ | Campo | Valor |
419
+ |-------|-------|
420
+ | **Tipo** | Implementação |
421
+ | **Prioridade** | Alta |
422
+ | **Estimativa** | 2h |
423
+ | **Depende de** | TALP-1.4 |
424
+
425
+ **Descrição**
426
+ Criar o system prompt que instrui o LLM a agir como pesquisador de segurança Solidity. O prompt precisa garantir: (1) output é apenas Solidity em bloco, (2) o LLM não reescreve o `setUp()`, (3) toda linha não-óbvia tem comentário.
427
+
428
+ **Arquivos a criar**
429
+ ```
430
+ src/agents/poc-generator/prompts/system.ts
431
+ ```
432
+
433
+ **Implementação**
434
+ ```typescript
435
+ export const SYSTEM_PROMPT = `Você é um Pesquisador de Segurança Solidity especializado em escrever exploits Proof of Concept (PoC) para Foundry.
436
+
437
+ ## TAREFA
438
+ Você receberá:
439
+ 1. Um relatório de vulnerabilidade descrevendo uma falha de segurança em Solidity.
440
+ 2. Um scaffold Foundry parcialmente completo com setUp() já implementado.
441
+
442
+ Sua missão: completar APENAS a função test_Exploit() — e, se necessário, adicionar contratos auxiliares (ex: atacante com fallback()) ANTES do contrato ExploitTest.
443
+
444
+ ## RESTRIÇÕES ABSOLUTAS
445
+ - NÃO modifique setUp(), imports, constants ou qualquer campo marcado com "NÃO MODIFICAR".
446
+ - NÃO adicione novos imports além dos já presentes.
447
+ - Output APENAS um bloco \`\`\`solidity ... \`\`\` com o arquivo completo. Sem texto fora do bloco.
448
+
449
+ ## REGRAS DE QUALIDADE
450
+ - Use cheatcodes Foundry quando necessário: vm.warp(), vm.roll(), vm.prank(), vm.deal(), vm.expectRevert().
451
+ - A assertion final DEVE usar assertTrue(), assertGt() ou assertEq() para provar que o exploit teve sucesso.
452
+ - Cada linha não-óbvia DEVE ter um comentário inline explicando por que existe.
453
+ - Se precisar de flash loan, implemente o callback do provider já configurado no setUp().
454
+ - Se não conseguir completar o exploit, implemente o máximo possível e adicione comentários // TODO: explicando o que falta.
455
+
456
+ ## FORMATO DE OUTPUT
457
+ \`\`\`solidity
458
+ // arquivo completo aqui
459
+ \`\`\`
460
+ `.trim();
461
+ ```
462
+
463
+ **Critérios de aceitação**
464
+ - [ ] LLM sempre produz um bloco ` ```solidity``` ` no output (validar em ≥5 chamadas manuais)
465
+ - [ ] LLM nunca reescreve `setUp()` (testar com prompt de retry)
466
+ - [ ] LLM sempre inclui pelo menos uma assertion no `test_Exploit()`
467
+ - [ ] Prompt cabe em menos de 500 tokens (verificar com `tiktoken`)
468
+
469
+ **Como testar**
470
+ ```typescript
471
+ // Teste manual: chamar o LLM diretamente com o system prompt
472
+ import { ChatOpenAI } from "@langchain/openai";
473
+ import { SYSTEM_PROMPT } from "./src/agents/poc-generator/prompts/system";
474
+ const llm = new ChatOpenAI({ modelName: "gpt-4o", openAIApiKey: process.env.OPENROUTER_API_KEY });
475
+ const resp = await llm.invoke([
476
+ { role: "system", content: SYSTEM_PROMPT },
477
+ { role: "user", content: "Scaffold: ...\nVulnerabilidade: reentrancy simples" }
478
+ ]);
479
+ console.log(resp.content);
480
+ // Verificar manualmente: contém ```solidity```? Não modificou setUp()?
481
+ ```
482
+
483
+ ---
484
+
485
+ ### TALP-2.2 — Implementar `extractSolidity`
486
+
487
+ | Campo | Valor |
488
+ |-------|-------|
489
+ | **Tipo** | Implementação |
490
+ | **Prioridade** | Alta |
491
+ | **Estimativa** | 1h |
492
+ | **Depende de** | TALP-2.1 |
493
+
494
+ **Descrição**
495
+ Parser robusto que extrai o bloco Solidity do output do LLM, com fallbacks para casos onde o modelo omite os backticks.
496
+
497
+ **Arquivos a criar**
498
+ ```
499
+ src/agents/poc-generator/utils/extractSolidity.ts
500
+ ```
501
+
502
+ **Implementação**
503
+ ```typescript
504
+ export function extractSolidity(llmOutput: string): string {
505
+ // Caso 1: bloco ```solidity ... ``` padrão
506
+ const match = llmOutput.match(/```solidity\s*([\s\S]*?)```/);
507
+ if (match) return match[1].trim();
508
+
509
+ // Caso 2: LLM omitiu backticks mas começa com pragma/SPDX
510
+ const trimmed = llmOutput.trim();
511
+ if (trimmed.startsWith("// SPDX") || trimmed.startsWith("pragma")) {
512
+ return trimmed;
513
+ }
514
+
515
+ // Caso 3: output inválido — lançar erro descritivo
516
+ throw new Error(
517
+ `LLM output não contém bloco Solidity válido. Preview: "${llmOutput.slice(0, 200)}"`
518
+ );
519
+ }
520
+ ```
521
+
522
+ **Critérios de aceitação**
523
+ - [ ] Extrai corretamente de bloco ` ```solidity``` ` padrão
524
+ - [ ] Usa fallback quando LLM omite backticks mas começa com `pragma` ou `// SPDX`
525
+ - [ ] Lança `Error` descritivo quando output é texto puro sem Solidity
526
+ - [ ] Resultado nunca contém os backticks do bloco
527
+
528
+ **Como testar**
529
+ ```typescript
530
+ // tests/unit/extractSolidity.test.ts
531
+ import { extractSolidity } from "../../src/agents/poc-generator/utils/extractSolidity";
532
+
533
+ // Caso 1: bloco padrão
534
+ const r1 = extractSolidity("Aqui está:\n```solidity\npragma solidity ^0.8.0;\n```");
535
+ console.assert(r1 === "pragma solidity ^0.8.0;", "Caso 1 falhou");
536
+
537
+ // Caso 2: sem backticks
538
+ const r2 = extractSolidity("pragma solidity ^0.8.0;\ncontract A {}");
539
+ console.assert(r2.startsWith("pragma"), "Caso 2 falhou");
540
+
541
+ // Caso 3: inválido — deve lançar
542
+ try {
543
+ extractSolidity("Desculpe, não consigo gerar isso.");
544
+ console.error("Caso 3 deveria ter lançado erro!");
545
+ } catch (e) {
546
+ console.log("Caso 3 OK — erro lançado:", (e as Error).message.slice(0, 50));
547
+ }
548
+
549
+ console.log("Todos os testes de extractSolidity passaram");
550
+ ```
551
+
552
+ ---
553
+
554
+ ### TALP-2.3 — Implementar `generatePoCNode`
555
+
556
+ | Campo | Valor |
557
+ |-------|-------|
558
+ | **Tipo** | Implementação |
559
+ | **Prioridade** | Crítica |
560
+ | **Estimativa** | 3h |
561
+ | **Depende de** | TALP-2.1, TALP-2.2 |
562
+
563
+ **Descrição**
564
+ Substituir o stub por um node real que chama o LLM. O prompt do usuário muda dependendo se é a primeira tentativa (passa o scaffold) ou um retry (passa o código com erro anterior).
565
+
566
+ **Arquivos a modificar**
567
+ ```
568
+ src/agents/poc-generator/agent.ts ← substituir stub do generatePoCNode
569
+ ```
570
+
571
+ **Implementação**
572
+ ```typescript
573
+ import { ChatOpenAI } from "@langchain/openai";
574
+ import { SYSTEM_PROMPT } from "./prompts/system";
575
+ import { extractSolidity } from "./utils/extractSolidity";
576
+
577
+ const llm = new ChatOpenAI({
578
+ modelName: "gpt-4o",
579
+ temperature: 0.2,
580
+ openAIApiKey: process.env.OPENROUTER_API_KEY,
581
+ configuration: { baseURL: "https://openrouter.ai/api/v1" },
582
+ });
583
+
584
+ async function generatePoCNode(state: PoCState): Promise<Partial<PoCState>> {
585
+ const { report, oracleContext, executionLogs, pocCode, iterations, lastError } = state;
586
+ const isRetry = iterations > 0;
587
+
588
+ const userMessage = isRetry
589
+ ? `O seguinte exploit FALHOU no Foundry.
590
+
591
+ Código anterior:
592
+ \`\`\`solidity
593
+ ${pocCode}
594
+ \`\`\`
595
+
596
+ Output do Forge (última execução):
597
+ ${executionLogs[executionLogs.length - 1]?.slice(0, 3000) ?? "sem logs"}
598
+
599
+ Análise do erro: ${lastError ?? "desconhecido"}
600
+
601
+ Corrija o código. Retorne o arquivo Solidity completo corrigido.`
602
+ : `Relatório de Vulnerabilidade:
603
+ - Título: ${report.title}
604
+ - Tipo: ${report.type}
605
+ - Descrição: ${report.description}
606
+ - Vetor de Ataque: ${report.attackVector}
607
+
608
+ Scaffold (complete APENAS test_Exploit):
609
+ \`\`\`solidity
610
+ ${oracleContext!.solidityScaffold}
611
+ \`\`\``;
612
+
613
+ console.log(`[generatePoCNode] iteração ${iterations + 1}, isRetry=${isRetry}`);
614
+
615
+ try {
616
+ const response = await llm.invoke([
617
+ { role: "system", content: SYSTEM_PROMPT },
618
+ { role: "user", content: userMessage },
619
+ ]);
620
+ const solidityCode = extractSolidity(response.content as string);
621
+ console.log("[generatePoCNode] Solidity extraído, tamanho:", solidityCode.length);
622
+ return { pocCode: solidityCode, iterations: 1 };
623
+ } catch (err) {
624
+ console.error("[generatePoCNode] falha na extração:", (err as Error).message);
625
+ return { iterations: 1, lastError: `Falha ao extrair Solidity: ${(err as Error).message}` };
626
+ }
627
+ }
628
+ ```
629
+
630
+ **Critérios de aceitação**
631
+ - [ ] Na primeira iteração: passa scaffold completo + descrição da vulnerabilidade
632
+ - [ ] No retry: passa código anterior + logs do forge + análise do erro
633
+ - [ ] `iterations` incrementa em +1 a cada chamada (via reducer aditivo)
634
+ - [ ] Erro de extração não trava o grafo — registra `lastError` e continua
635
+ - [ ] Logs do forge são truncados a 3000 chars (evitar ultrapassar context window)
636
+
637
+ ---
638
+
639
+ ### TALP-2.4 — Setup do sandbox Foundry
640
+
641
+ | Campo | Valor |
642
+ |-------|-------|
643
+ | **Tipo** | Setup/Infra |
644
+ | **Prioridade** | Crítica |
645
+ | **Estimativa** | 1h |
646
+ | **Depende de** | TALP-1.1 |
647
+
648
+ **Descrição**
649
+ Criar script de inicialização do sandbox Foundry local em `/tmp/poc-sandbox/`. O agente escreve o arquivo `Exploit.t.sol` aqui e executa `forge test`.
650
+
651
+ **Arquivos a criar**
652
+ ```
653
+ scripts/setup-sandbox.sh
654
+ foundry.toml ← copiado para o sandbox
655
+ ```
656
+
657
+ **Implementação — `setup-sandbox.sh`**
658
+ ```bash
659
+ #!/bin/bash
660
+ set -e
661
+
662
+ SANDBOX="/tmp/poc-sandbox"
663
+
664
+ echo "Inicializando sandbox Foundry em $SANDBOX..."
665
+ rm -rf "$SANDBOX"
666
+ mkdir -p "$SANDBOX"
667
+ cd "$SANDBOX"
668
+
669
+ forge init --no-git --quiet
670
+ forge install foundry-rs/forge-std --no-git --quiet
671
+
672
+ cat > foundry.toml << 'EOF'
673
+ [profile.default]
674
+ src = "src"
675
+ test = "test"
676
+ out = "out"
677
+ libs = ["lib"]
678
+ solc-version = "0.8.20"
679
+ EOF
680
+
681
+ # Remover o contrato e teste de exemplo do forge init
682
+ rm -f src/Counter.sol test/Counter.t.sol
683
+
684
+ echo "Sandbox pronto. Testando com forge build..."
685
+ forge build
686
+ echo "OK — sandbox funcionando em $SANDBOX"
687
+ ```
688
+
689
+ **Critérios de aceitação**
690
+ - [ ] Script roda sem erros em máquina com Foundry instalado (`forge --version`)
691
+ - [ ] `forge build` dentro de `/tmp/poc-sandbox` tem sucesso após o script
692
+ - [ ] Diretório `test/` existe e está vazio (pronto para receber `Exploit.t.sol`)
693
+ - [ ] `forge-std` instalado corretamente (import `"forge-std/Test.sol"` funciona)
694
+
695
+ **Como testar**
696
+ ```bash
697
+ chmod +x scripts/setup-sandbox.sh
698
+ ./scripts/setup-sandbox.sh
699
+ echo "// SPDX-License-Identifier: UNLICENSED
700
+ pragma solidity ^0.8.20;
701
+ import 'forge-std/Test.sol';
702
+ contract SmokeTest is Test {
703
+ function test_ok() public { assertTrue(true); }
704
+ }" > /tmp/poc-sandbox/test/Smoke.t.sol
705
+ cd /tmp/poc-sandbox && forge test
706
+ ```
707
+
708
+ ---
709
+
710
+ ### TALP-2.5 — Implementar `foundryRunner`
711
+
712
+ | Campo | Valor |
713
+ |-------|-------|
714
+ | **Tipo** | Implementação |
715
+ | **Prioridade** | Crítica |
716
+ | **Estimativa** | 2h |
717
+ | **Depende de** | TALP-2.4 |
718
+
719
+ **Descrição**
720
+ Módulo que escreve o código Solidity no sandbox, executa `forge test` via `child_process` e retorna o resultado estruturado. Nunca lança erro — sempre retorna `FoundryResult`.
721
+
722
+ **Arquivos a criar**
723
+ ```
724
+ src/agents/poc-generator/tools/foundryRunner.ts
725
+ ```
726
+
727
+ **Implementação**
728
+ ```typescript
729
+ import { exec } from "child_process";
730
+ import { promisify } from "util";
731
+ import { writeFile } from "fs/promises";
732
+
733
+ const execAsync = promisify(exec);
734
+ const SANDBOX = "/tmp/poc-sandbox";
735
+ const TIMEOUT_MS = 60_000;
736
+
737
+ export interface FoundryResult {
738
+ exitCode: number;
739
+ stdout: string;
740
+ stderr: string;
741
+ combined: string;
742
+ timedOut: boolean;
743
+ }
744
+
745
+ export async function runFoundry(solidityCode: string): Promise<FoundryResult> {
746
+ // Escrever o arquivo no sandbox
747
+ await writeFile(`${SANDBOX}/test/Exploit.t.sol`, solidityCode, "utf-8");
748
+
749
+ try {
750
+ const { stdout, stderr } = await execAsync(
751
+ "forge test --match-contract ExploitTest -vvvv",
752
+ { cwd: SANDBOX, timeout: TIMEOUT_MS, env: { ...process.env } }
753
+ );
754
+ return {
755
+ exitCode: 0,
756
+ stdout,
757
+ stderr,
758
+ combined: `STDOUT:\n${stdout}\nSTDERR:\n${stderr}`,
759
+ timedOut: false,
760
+ };
761
+ } catch (err: any) {
762
+ if (err.killed || err.signal === "SIGTERM") {
763
+ return {
764
+ exitCode: -1, stdout: "", stderr: "Forge timed out",
765
+ combined: `TIMEOUT após ${TIMEOUT_MS / 1000}s`,
766
+ timedOut: true,
767
+ };
768
+ }
769
+ return {
770
+ exitCode: err.code ?? 1,
771
+ stdout: err.stdout ?? "",
772
+ stderr: err.stderr ?? "",
773
+ combined: `STDOUT:\n${err.stdout ?? ""}\nSTDERR:\n${err.stderr ?? ""}`,
774
+ timedOut: false,
775
+ };
776
+ }
777
+ }
778
+ ```
779
+
780
+ **Critérios de aceitação**
781
+ - [ ] Detecta test pass: `exitCode === 0` + stdout contém `"ok"`
782
+ - [ ] Detecta compiler error: `exitCode !== 0` + stderr contém `"Compiler run failed"`
783
+ - [ ] Detecta timeout: `timedOut === true`, processo morto após 60s
784
+ - [ ] Nunca lança exceção — sempre retorna `FoundryResult`
785
+ - [ ] `combined` contém stdout e stderr separados por label
786
+
787
+ **Como testar**
788
+ ```typescript
789
+ // tests/unit/foundryRunner.test.ts
790
+ import { runFoundry } from "../../src/agents/poc-generator/tools/foundryRunner";
791
+
792
+ // Caso 1: código válido que passa
793
+ const validCode = `// SPDX-License-Identifier: UNLICENSED
794
+ pragma solidity ^0.8.20;
795
+ import "forge-std/Test.sol";
796
+ contract ExploitTest is Test {
797
+ function setUp() public {}
798
+ function test_Exploit() public { assertTrue(true); }
799
+ }`;
800
+ const r1 = await runFoundry(validCode);
801
+ console.assert(r1.exitCode === 0, "Deveria passar");
802
+ console.assert(r1.stdout.includes("ok"), "Deveria ter 'ok' no stdout");
803
+
804
+ // Caso 2: código com erro de compilação
805
+ const invalidCode = `pragma solidity ^0.8.20; contract Bad { function foo( }`;
806
+ const r2 = await runFoundry(invalidCode);
807
+ console.assert(r2.exitCode !== 0, "Deveria falhar");
808
+ console.assert(r2.stderr.includes("Error") || r2.combined.includes("Error"), "Deveria ter erro");
809
+
810
+ console.log("foundryRunner OK");
811
+ ```
812
+
813
+ ---
814
+
815
+ ### TALP-2.6 — Implementar `logAnalyzer`
816
+
817
+ | Campo | Valor |
818
+ |-------|-------|
819
+ | **Tipo** | Implementação |
820
+ | **Prioridade** | Alta |
821
+ | **Estimativa** | 2h |
822
+ | **Depende de** | TALP-2.5 |
823
+
824
+ **Descrição**
825
+ Módulo que lê o output bruto do `forge test` e produz um resumo legível em linguagem natural para o LLM. Classifica o erro em uma de 5 categorias.
826
+
827
+ **Arquivos a criar**
828
+ ```
829
+ src/agents/poc-generator/utils/logAnalyzer.ts
830
+ ```
831
+
832
+ **Implementação**
833
+ ```typescript
834
+ import { FoundryResult } from "../tools/foundryRunner";
835
+
836
+ export type ErrorCategory =
837
+ | "compiler_error"
838
+ | "revert_no_message"
839
+ | "revert_with_message"
840
+ | "assertion_failed"
841
+ | "timeout"
842
+ | "unknown";
843
+
844
+ export interface LogAnalysis {
845
+ category: ErrorCategory;
846
+ summary: string; // 1-2 frases em linguagem natural para o LLM
847
+ relevantLines: string[]; // máx 10 linhas do log original
848
+ }
849
+
850
+ export function analyzeFoundryLog(result: FoundryResult): LogAnalysis {
851
+ if (result.timedOut) return {
852
+ category: "timeout",
853
+ summary: "Forge excedeu 60s. O exploit pode ter entrado em loop infinito ou a lógica está bloqueante.",
854
+ relevantLines: [],
855
+ };
856
+
857
+ if (result.combined.includes("Compiler run failed")) {
858
+ const lines = result.combined.split("\n")
859
+ .filter(l => l.includes("Error") || l.includes("error") || l.includes("-->"))
860
+ .slice(0, 10);
861
+ return {
862
+ category: "compiler_error",
863
+ summary: "Erro de compilação Solidity. Verifique: interfaces faltando, assinaturas incorretas, tipos incompatíveis.",
864
+ relevantLines: lines,
865
+ };
866
+ }
867
+
868
+ if (result.combined.includes("FAIL")) {
869
+ const revertReason = result.combined.match(/revert: (.+)/)?.[1];
870
+ const assertionFail = result.combined.includes("Assertion Failed") || result.combined.includes("assertion failed");
871
+
872
+ if (assertionFail) return {
873
+ category: "assertion_failed",
874
+ summary: "O exploit executou mas a assertion final falhou — o atacante não obteve o resultado esperado.",
875
+ relevantLines: result.combined.split("\n")
876
+ .filter(l => l.includes("assertion") || l.includes("FAIL")).slice(0, 10),
877
+ };
878
+
879
+ if (revertReason) return {
880
+ category: "revert_with_message",
881
+ summary: `Transação reverteu com: "${revertReason}". O contrato rejeitou a operação.`,
882
+ relevantLines: [revertReason],
883
+ };
884
+
885
+ return {
886
+ category: "revert_no_message",
887
+ summary: "Transação reverteu sem mensagem. Verifique a ordem das chamadas, permissões e estado do contrato.",
888
+ relevantLines: result.combined.split("\n")
889
+ .filter(l => l.includes("revert") || l.includes("FAIL")).slice(0, 5),
890
+ };
891
+ }
892
+
893
+ return {
894
+ category: "unknown",
895
+ summary: "Erro desconhecido. Revisar output completo do forge.",
896
+ relevantLines: result.combined.split("\n").slice(0, 10),
897
+ };
898
+ }
899
+ ```
900
+
901
+ **Critérios de aceitação**
902
+ - [ ] Classifica `compiler_error` quando stderr contém `"Compiler run failed"`
903
+ - [ ] Classifica `assertion_failed` quando stdout contém `"FAIL"` + `"Assertion Failed"`
904
+ - [ ] Classifica `revert_with_message` quando há `revert: <mensagem>`
905
+ - [ ] `relevantLines` nunca tem mais de 10 linhas
906
+ - [ ] `summary` é sempre linguagem natural (não reproduz stack trace bruto)
907
+
908
+ **Como testar**
909
+ ```typescript
910
+ // tests/unit/logAnalyzer.test.ts
911
+ import { analyzeFoundryLog } from "../../src/agents/poc-generator/utils/logAnalyzer";
912
+
913
+ const compilerError = { exitCode: 1, timedOut: false, stdout: "", stderr: "Compiler run failed\nError: ...\n--> src/A.sol:10:5", combined: "STDOUT:\n\nSTDERR:\nCompiler run failed\nError: ...\n--> src/A.sol:10:5" };
914
+ const r1 = analyzeFoundryLog(compilerError as any);
915
+ console.assert(r1.category === "compiler_error", "Caso 1 falhou");
916
+ console.assert(r1.relevantLines.length <= 10, "Muitas linhas");
917
+
918
+ const timeout = { exitCode: -1, timedOut: true, stdout: "", stderr: "", combined: "TIMEOUT" };
919
+ const r2 = analyzeFoundryLog(timeout as any);
920
+ console.assert(r2.category === "timeout", "Caso timeout falhou");
921
+
922
+ console.log("logAnalyzer OK");
923
+ ```
924
+
925
+ ---
926
+
927
+ ### TALP-2.7 — Implementar `reflectNode`
928
+
929
+ | Campo | Valor |
930
+ |-------|-------|
931
+ | **Tipo** | Implementação |
932
+ | **Prioridade** | Alta |
933
+ | **Estimativa** | 2h |
934
+ | **Depende de** | TALP-2.6 |
935
+
936
+ **Descrição**
937
+ Node que usa o `logAnalyzer` para produzir um `lastError` estruturado e legível. Este valor é passado para o `generatePoCNode` no retry, orientando o LLM sobre o que corrigir.
938
+
939
+ **Arquivos a modificar**
940
+ ```
941
+ src/agents/poc-generator/agent.ts ← substituir stub do reflectNode
942
+ ```
943
+
944
+ **Implementação**
945
+ ```typescript
946
+ import { analyzeFoundryLog } from "./utils/logAnalyzer";
947
+
948
+ async function reflectNode(state: PoCState): Promise<Partial<PoCState>> {
949
+ // Pegar o último log de execução
950
+ const lastLog = state.executionLogs[state.executionLogs.length - 1];
951
+ if (!lastLog) {
952
+ return { lastError: "Sem logs disponíveis para análise." };
953
+ }
954
+
955
+ // Reconstruir FoundryResult mínimo a partir do log combinado
956
+ const mockResult = {
957
+ exitCode: 1, timedOut: lastLog.includes("TIMEOUT"),
958
+ stdout: "", stderr: "", combined: lastLog,
959
+ };
960
+
961
+ const analysis = analyzeFoundryLog(mockResult as any);
962
+
963
+ console.log(`[reflectNode] categoria: ${analysis.category}`);
964
+ console.log(`[reflectNode] resumo: ${analysis.summary}`);
965
+
966
+ return {
967
+ lastError: `[${analysis.category.toUpperCase()}] ${analysis.summary}\n\nLinhas relevantes:\n${analysis.relevantLines.join("\n")}`,
968
+ };
969
+ }
970
+ ```
971
+
972
+ **Critérios de aceitação**
973
+ - [ ] `lastError` sempre é uma string não-vazia após o node
974
+ - [ ] `lastError` inclui a categoria do erro entre colchetes
975
+ - [ ] `lastError` inclui as linhas relevantes do log (não o log inteiro)
976
+ - [ ] Node não trava se `executionLogs` estiver vazio
977
+
978
+ ---
979
+
980
+ ### TALP-2.8 — Implementar router condicional e fechar o loop
981
+
982
+ | Campo | Valor |
983
+ |-------|-------|
984
+ | **Tipo** | Implementação |
985
+ | **Prioridade** | Crítica |
986
+ | **Estimativa** | 2h |
987
+ | **Depende de** | TALP-2.3, TALP-2.5, TALP-2.7 |
988
+
989
+ **Descrição**
990
+ Substituir as edges fixas do grafo por edges condicionais que implementam o loop ReAct. Atualizar o `runFoundryNode` real e conectar tudo.
991
+
992
+ **Arquivos a modificar**
993
+ ```
994
+ src/agents/poc-generator/agent.ts ← refatorar grafo completo
995
+ ```
996
+
997
+ **Implementação**
998
+ ```typescript
999
+ const MAX_ITERATIONS = 5;
1000
+
1001
+ function routeAfterFoundry(state: PoCState): "reflectNode" | "__end__" {
1002
+ if (state.status === "success") return "__end__";
1003
+ if (state.status === "timeout") return "__end__";
1004
+ if (state.iterations >= MAX_ITERATIONS) return "__end__";
1005
+ return "reflectNode";
1006
+ }
1007
+
1008
+ async function runFoundryNode(state: PoCState): Promise<Partial<PoCState>> {
1009
+ const result = await runFoundry(state.pocCode);
1010
+ const analysis = analyzeFoundryLog(result);
1011
+ const passed = result.exitCode === 0 && result.stdout.includes("ok");
1012
+
1013
+ console.log(`[runFoundryNode] exitCode=${result.exitCode}, passed=${passed}`);
1014
+
1015
+ return {
1016
+ executionLogs: [result.combined], // reducer append
1017
+ lastError: analysis.summary,
1018
+ status: passed ? "success"
1019
+ : result.timedOut ? "timeout"
1020
+ : "running",
1021
+ };
1022
+ }
1023
+
1024
+ // Grafo final com loop ReAct
1025
+ const graph = new StateGraph(PoCStateAnnotation)
1026
+ .addNode("oracleNode", oracleNode)
1027
+ .addNode("generatePoCNode", generatePoCNode)
1028
+ .addNode("runFoundryNode", runFoundryNode)
1029
+ .addNode("reflectNode", reflectNode)
1030
+ .addEdge(START, "oracleNode")
1031
+ .addEdge("oracleNode", "generatePoCNode")
1032
+ .addEdge("generatePoCNode", "runFoundryNode")
1033
+ .addConditionalEdges("runFoundryNode", routeAfterFoundry, {
1034
+ reflectNode: "reflectNode",
1035
+ __end__: END,
1036
+ })
1037
+ .addEdge("reflectNode", "generatePoCNode"); // fecha o loop
1038
+
1039
+ export const pocGeneratorAgent = graph.compile();
1040
+ ```
1041
+
1042
+ **Critérios de aceitação**
1043
+ - [ ] Loop executa ≥2 iterações quando a primeira tentativa falha
1044
+ - [ ] Para em `END` quando `status === "success"`
1045
+ - [ ] Para em `END` quando `iterations >= 5` (mesmo sem sucesso)
1046
+ - [ ] Para em `END` quando `status === "timeout"`
1047
+ - [ ] `executionLogs` tem uma entrada por iteração ao final
1048
+
1049
+ **Gate da Semana 2:** Rodar o agente com o `VulnerableBank` e confirmar que o loop executa ≥2 iterações e melhora o código após erro de compilação.
1050
+
1051
+ ---
1052
+
1053
+ ## SEMANA 3 — Integração, Smoke Test & Avaliação
1054
+
1055
+ ---
1056
+
1057
+ ### TALP-3.1 — Interface pública do agente
1058
+
1059
+ | Campo | Valor |
1060
+ |-------|-------|
1061
+ | **Tipo** | Implementação |
1062
+ | **Prioridade** | Alta |
1063
+ | **Estimativa** | 1h |
1064
+ | **Depende de** | TALP-2.8 |
1065
+
1066
+ **Descrição**
1067
+ Criar o entry point público que o restante do sistema (Agente Auditor) usará para invocar o Agente de PoCs.
1068
+
1069
+ **Arquivos a criar**
1070
+ ```
1071
+ src/agents/poc-generator/index.ts
1072
+ ```
1073
+
1074
+ **Implementação**
1075
+ ```typescript
1076
+ import { pocGeneratorAgent } from "./agent";
1077
+ import { VulnerabilityReport, PoCResult } from "./types";
1078
+
1079
+ export async function runPoCGenerator(report: VulnerabilityReport): Promise<PoCResult> {
1080
+ console.log(`[runPoCGenerator] iniciando para: ${report.id} — ${report.title}`);
1081
+
1082
+ const finalState = await pocGeneratorAgent.invoke({ report });
1083
+
1084
+ const result: PoCResult = {
1085
+ reportId: report.id,
1086
+ status: finalState.status === "running" ? "failed" : finalState.status,
1087
+ solidityCode: finalState.pocCode,
1088
+ executionLogs: finalState.executionLogs,
1089
+ iterations: finalState.iterations,
1090
+ };
1091
+
1092
+ console.log(`[runPoCGenerator] concluído — status=${result.status}, iterações=${result.iterations}`);
1093
+ return result;
1094
+ }
1095
+
1096
+ export type { VulnerabilityReport, PoCResult };
1097
+ ```
1098
+
1099
+ **Critérios de aceitação**
1100
+ - [ ] Nunca lança exceção — retorna `PoCResult` em qualquer cenário
1101
+ - [ ] `status` nunca é `"running"` no resultado final (mapeia para `"failed"`)
1102
+ - [ ] Tipos exportados batem com o contrato esperado pelo Agente Auditor
1103
+
1104
+ ---
1105
+
1106
+ ### TALP-3.2 — Smoke test end-to-end com reentrancy
1107
+
1108
+ | Campo | Valor |
1109
+ |-------|-------|
1110
+ | **Tipo** | Teste |
1111
+ | **Prioridade** | Crítica |
1112
+ | **Estimativa** | 3h |
1113
+ | **Depende de** | TALP-3.1 |
1114
+
1115
+ **Descrição**
1116
+ Validar o pipeline completo com um contrato vulnerável simples de reentrancy escrito manualmente. Este teste não depende de dataset externo.
1117
+
1118
+ **Arquivos a criar**
1119
+ ```
1120
+ tests/e2e/poc-generator.test.ts
1121
+ ```
1122
+
1123
+ **Implementação**
1124
+ ```typescript
1125
+ import { runPoCGenerator } from "../../src/agents/poc-generator";
1126
+ import { VulnerabilityReport } from "../../src/agents/poc-generator/types";
1127
+
1128
+ const VULNERABLE_BANK = `
1129
+ pragma solidity ^0.8.20;
1130
+ contract VulnerableBank {
1131
+ mapping(address => uint) public balances;
1132
+ function deposit() external payable { balances[msg.sender] += msg.value; }
1133
+ function withdraw() external {
1134
+ uint amount = balances[msg.sender];
1135
+ (bool ok,) = msg.sender.call{value: amount}("");
1136
+ require(ok);
1137
+ balances[msg.sender] = 0; // atualiza DEPOIS — reentrancy
1138
+ }
1139
+ receive() external payable {}
1140
+ }`.trim();
1141
+
1142
+ const mockReport: VulnerabilityReport = {
1143
+ id: "e2e-reentrancy-001",
1144
+ severity: "critical",
1145
+ type: "reentrancy",
1146
+ title: "Reentrancy em withdraw()",
1147
+ description: "withdraw() envia ETH antes de zerar o saldo, permitindo re-entrada.",
1148
+ affectedContract: { name: "VulnerableBank", sourceCode: VULNERABLE_BANK },
1149
+ attackVector: "Contrato atacante com fallback() que chama withdraw() novamente antes do saldo ser zerado.",
1150
+ suggestedCheatcodes: ["vm.deal", "vm.startPrank", "vm.stopPrank"],
1151
+ };
1152
+
1153
+ async function runE2ETest() {
1154
+ console.log("Iniciando smoke test end-to-end...");
1155
+ const result = await runPoCGenerator(mockReport);
1156
+
1157
+ console.log(`Status: ${result.status}`);
1158
+ console.log(`Iterações: ${result.iterations}`);
1159
+ console.log(`Logs: ${result.executionLogs.length} entrada(s)`);
1160
+
1161
+ console.assert(result.status === "success", `FALHOU: status esperado 'success', recebido '${result.status}'`);
1162
+ console.assert(result.iterations <= 5, `FALHOU: muitas iterações (${result.iterations})`);
1163
+ console.assert(result.solidityCode.includes("test_Exploit"), "FALHOU: código não contém test_Exploit");
1164
+
1165
+ console.log("Smoke test PASSOU");
1166
+ return result;
1167
+ }
1168
+
1169
+ runE2ETest().catch(console.error);
1170
+ ```
1171
+
1172
+ **Critérios de aceitação**
1173
+ - [ ] `result.status === "success"`
1174
+ - [ ] `result.iterations <= 5`
1175
+ - [ ] `result.solidityCode` contém `test_Exploit`
1176
+ - [ ] Teste completo roda em menos de 3 minutos
1177
+ - [ ] Não requer variável `MAINNET_RPC_URL` (contrato é local)
1178
+
1179
+ **Como executar**
1180
+ ```bash
1181
+ OPENROUTER_API_KEY=sk-... npx ts-node tests/e2e/poc-generator.test.ts
1182
+ ```
1183
+
1184
+ ---
1185
+
1186
+ ### TALP-3.3 — Preparar dataset de benchmark
1187
+
1188
+ | Campo | Valor |
1189
+ |-------|-------|
1190
+ | **Tipo** | Dados |
1191
+ | **Prioridade** | Alta |
1192
+ | **Estimativa** | 3h |
1193
+ | **Depende de** | — |
1194
+
1195
+ **Descrição**
1196
+ Selecionar ≥5 casos reais do dataset **Proof-of-Patch** (ASSERT-KTH) e montar o `benchmark.json`. Priorizar: reentrancy, access control bypass, integer overflow.
1197
+
1198
+ **Arquivos a criar**
1199
+ ```
1200
+ data/benchmark.json
1201
+ ```
1202
+
1203
+ **Formato**
1204
+ ```json
1205
+ [
1206
+ {
1207
+ "id": "bench-001",
1208
+ "vulnerability": "Reentrancy em withdraw()",
1209
+ "type": "reentrancy",
1210
+ "severity": "critical",
1211
+ "contractName": "VulnerableBank",
1212
+ "sourceCode": "pragma solidity ^0.8.20; ...",
1213
+ "attackVector": "Contrato atacante com fallback reentrant",
1214
+ "source": "Proof-of-Patch / ASSERT-KTH",
1215
+ "referencePoC": "disponível no repositório ASSERT-KTH/Proof-of-Patch"
1216
+ }
1217
+ ]
1218
+ ```
1219
+
1220
+ **Critérios de aceitação**
1221
+ - [ ] ≥5 entradas com `sourceCode` completo e compilável
1222
+ - [ ] Cobre pelo menos 3 tipos de vulnerabilidade diferentes
1223
+ - [ ] Cada entrada tem `attackVector` descrito
1224
+ - [ ] Todos os contratos compilam com `forge build` (verificar antes de incluir)
1225
+
1226
+ ---
1227
+
1228
+ ### TALP-3.4 — Script de avaliação em batch
1229
+
1230
+ | Campo | Valor |
1231
+ |-------|-------|
1232
+ | **Tipo** | Avaliação |
1233
+ | **Prioridade** | Alta |
1234
+ | **Estimativa** | 2h |
1235
+ | **Depende de** | TALP-3.1, TALP-3.3 |
1236
+
1237
+ **Descrição**
1238
+ Script que roda o agente sobre todos os casos do benchmark e reporta a taxa de sucesso. Um caso que falha não interrompe o batch.
1239
+
1240
+ **Arquivos a criar**
1241
+ ```
1242
+ scripts/evaluate.ts
1243
+ data/eval-results.json ← gerado pelo script
1244
+ ```
1245
+
1246
+ **Implementação**
1247
+ ```typescript
1248
+ import { readFileSync, writeFileSync } from "fs";
1249
+ import { runPoCGenerator } from "../src/agents/poc-generator";
1250
+
1251
+ interface BenchmarkCase {
1252
+ id: string; vulnerability: string; type: string; severity: string;
1253
+ contractName: string; sourceCode: string; attackVector: string;
1254
+ }
1255
+
1256
+ interface EvalResult {
1257
+ id: string; status: string; iterations: number;
1258
+ passed: boolean; durationMs: number;
1259
+ }
1260
+
1261
+ async function main() {
1262
+ const dataset: BenchmarkCase[] = JSON.parse(readFileSync("data/benchmark.json", "utf-8"));
1263
+ const results: EvalResult[] = [];
1264
+
1265
+ console.log(`Iniciando avaliação — ${dataset.length} caso(s)\n`);
1266
+
1267
+ for (const item of dataset) {
1268
+ const start = Date.now();
1269
+ console.log(`[${item.id}] Rodando: ${item.vulnerability}...`);
1270
+
1271
+ try {
1272
+ const result = await runPoCGenerator({
1273
+ id: item.id, severity: item.severity as any, type: item.type,
1274
+ title: item.vulnerability, description: item.vulnerability,
1275
+ affectedContract: { name: item.contractName, sourceCode: item.sourceCode },
1276
+ attackVector: item.attackVector,
1277
+ });
1278
+ const dur = Date.now() - start;
1279
+ results.push({ id: item.id, status: result.status, iterations: result.iterations, passed: result.status === "success", durationMs: dur });
1280
+ console.log(` → ${result.status} em ${result.iterations} iter(s), ${(dur/1000).toFixed(1)}s`);
1281
+ } catch (err) {
1282
+ const dur = Date.now() - start;
1283
+ results.push({ id: item.id, status: "error", iterations: 0, passed: false, durationMs: dur });
1284
+ console.error(` → ERRO: ${(err as Error).message}`);
1285
+ }
1286
+ }
1287
+
1288
+ const passed = results.filter(r => r.passed).length;
1289
+ const total = results.length;
1290
+ const successRate = ((passed / total) * 100).toFixed(1);
1291
+ const avgIter = (results.reduce((s, r) => s + r.iterations, 0) / total).toFixed(1);
1292
+
1293
+ console.log(`\n${"=".repeat(40)}`);
1294
+ console.log(`Taxa de sucesso: ${successRate}% (${passed}/${total})`);
1295
+ console.log(`Média de iterações: ${avgIter}`);
1296
+ console.log(`${"=".repeat(40)}`);
1297
+
1298
+ writeFileSync("data/eval-results.json", JSON.stringify({ summary: { successRate: parseFloat(successRate), passed, total, avgIterations: parseFloat(avgIter) }, results }, null, 2));
1299
+ console.log("\nResultados salvos em data/eval-results.json");
1300
+ }
1301
+
1302
+ main().catch(console.error);
1303
+ ```
1304
+
1305
+ **Critérios de aceitação**
1306
+ - [ ] Roda todos os casos sem travar (erro individual registrado e continua)
1307
+ - [ ] Gera `data/eval-results.json` com resultados por caso + sumário
1308
+ - [ ] Reporta taxa de sucesso, total de casos e média de iterações
1309
+ - [ ] **Taxa alvo:** ≥50% de sucesso nos casos do benchmark
1310
+
1311
+ **Como executar**
1312
+ ```bash
1313
+ OPENROUTER_API_KEY=sk-... npx ts-node scripts/evaluate.ts
1314
+ ```
1315
+
1316
+ ---
1317
+
1318
+ ## Resumo de Arquivos por Task
1319
+
1320
+ | Task | Arquivo | Ação |
1321
+ |------|---------|------|
1322
+ | TALP-1.1 | `tsconfig.json`, `package.json` | criar/atualizar |
1323
+ | TALP-1.2 | `src/.../types.ts`, `src/.../state.ts` | criar |
1324
+ | TALP-1.3 | `src/.../agent.ts` | criar (stubs) |
1325
+ | TALP-1.4 | `src/.../tools/scaffoldGenerator.ts` | criar |
1326
+ | TALP-1.5 | `src/.../agent.ts` | modificar (oracleNode real) |
1327
+ | TALP-2.1 | `src/.../prompts/system.ts` | criar |
1328
+ | TALP-2.2 | `src/.../utils/extractSolidity.ts` | criar |
1329
+ | TALP-2.3 | `src/.../agent.ts` | modificar (generatePoCNode real) |
1330
+ | TALP-2.4 | `scripts/setup-sandbox.sh`, `foundry.toml` | criar |
1331
+ | TALP-2.5 | `src/.../tools/foundryRunner.ts` | criar |
1332
+ | TALP-2.6 | `src/.../utils/logAnalyzer.ts` | criar |
1333
+ | TALP-2.7 | `src/.../agent.ts` | modificar (reflectNode real) |
1334
+ | TALP-2.8 | `src/.../agent.ts` | modificar (grafo final com loop) |
1335
+ | TALP-3.1 | `src/.../index.ts` | criar |
1336
+ | TALP-3.2 | `tests/e2e/poc-generator.test.ts` | criar |
1337
+ | TALP-3.3 | `data/benchmark.json` | criar |
1338
+ | TALP-3.4 | `scripts/evaluate.ts` | criar |
src/agents/tester/README.md ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Agente Gerador de PoCs (Tester)
2
+
3
+ Este agente é responsável por validar vulnerabilidades identificadas pelo **Agente Auditor** através da geração automática de exploits em Solidity (*Proof of Concepts* - PoCs) e execução em um ambiente sandbox utilizando **Foundry**.
4
+
5
+ ## 1. Visão Geral
6
+
7
+ O agente implementa um **loop ReAct** (Gerar → Executar → Refletir) orquestrado via **LangGraph**. Diferente de abordagens tradicionais, ele utiliza um componente **Oracle** para preparar o scaffold do teste, permitindo que o LLM foque exclusivamente na lógica do exploit.
8
+
9
+ ### Fluxo Multi-agente
10
+ ```mermaid
11
+ graph LR
12
+ Coder[Agente Gerador] -- "Código Fonte" --> Auditor
13
+ Auditor[Agente Auditor] -- "Findings (JSON)" --> Tester
14
+ Tester[Agente de PoCs] -- "PoCResult (Verificado)" --> Final[Projeto Validado]
15
+ ```
16
+
17
+ ### Principais Funcionalidades:
18
+ - **Sandbox Autônomo:** O agente detecta e inicializa o ambiente Foundry (`/tmp/poc-sandbox`) automaticamente no primeiro uso.
19
+ - **Scaffold Automático:** Gera o arquivo `Exploit.t.sol` com o contrato vítima já instanciado e financiado.
20
+ - **Loop de Auto-correção:** Se o exploit falhar, o agente analisa os logs e tenta corrigir o código por até 5 iterações.
21
+ - **Integração com DeepSeek:** Utiliza o modelo `deepseek-v4-pro` via OpenRouter.
22
+
23
+ ## 2. Arquitetura
24
+
25
+ O fluxo de execução segue o grafo definido em `agent.ts`:
26
+
27
+ 1. **Oracle Node:** Recebe o relatório de vulnerabilidade e gera o scaffold Solidity inicial.
28
+ 2. **Generate PoC Node:** O LLM completa a função `test_Exploit()` com base no scaffold e na descrição da falha.
29
+ 3. **Run Foundry Node:** Escreve o código no sandbox e executa `forge test`.
30
+ 4. **Reflect Node:** Em caso de falha, analisa o output do Forge, classifica o erro e fornece feedback para o próximo ciclo de geração.
31
+
32
+ ## 3. Estrutura de Arquivos
33
+
34
+ ```
35
+ src/agents/tester/
36
+ ├── agent.ts # Definição do grafo LangGraph e lógica dos nodes
37
+ ├── state.ts # Estado interno do agente (PoCStateAnnotation)
38
+ ├── types.ts # Interfaces de entrada (Finding) e saída (PoCResult)
39
+ ├── index.ts # Entry point público (runPoCGenerator)
40
+
41
+ ├── tools/
42
+ │ ├── scaffoldGenerator.ts # Gerador de boilerplate Foundry
43
+ │ └── foundryRunner.ts # Executor de comandos shell (forge)
44
+
45
+ ├── prompts/
46
+ │ └── system.ts # Instruções especializadas para o LLM
47
+
48
+ └── utils/
49
+ ├── extractSolidity.ts # Parser de blocos de código
50
+ └── logAnalyzer.ts # Classificador de erros de execução
51
+ ```
52
+
53
+ ## 4. Integração e Uso
54
+
55
+ ### Fluxo de Dados (Input/Output)
56
+
57
+ O agente recebe um objeto `VulnerabilityReport`. Como o **Agente Auditor** gera objetos do tipo `Finding`, é necessário realizar um mapeamento (veja `src/index.ts` para o adapter).
58
+
59
+ #### Estrutura de Entrada (`VulnerabilityReport`)
60
+ ```typescript
61
+ interface VulnerabilityReport {
62
+ id: string; // Identificador único do report
63
+ severity: string; // "high", "medium", "low"
64
+ title: string; // Título curto da falha
65
+ description: string; // Descrição técnica detalhada
66
+ affectedContract: {
67
+ name: string; // Nome da classe do contrato
68
+ sourceCode: string; // Código-fonte completo (Solidity)
69
+ };
70
+ attackVector: string; // Descrição do caminho de ataque
71
+ exploitablePaths?: string[]; // (Opcional) Passos detalhados
72
+ }
73
+ ```
74
+
75
+ #### Estrutura de Saída (`PoCResult`)
76
+ ```typescript
77
+ interface PoCResult {
78
+ reportId: string;
79
+ status: "success" | "failed" | "timeout";
80
+ solidityCode: string; // Conteúdo final do Exploit.t.sol
81
+ executionLogs: string[]; // Logs brutos de todas as iterações
82
+ iterations: number; // Total de tentativas realizadas
83
+ }
84
+ ```
85
+
86
+ ### Exemplo de Integração
87
+ ```typescript
88
+ import { runPoCGenerator } from "./src/agents/tester";
89
+
90
+ // O orquestrador mapeia o Finding + Código Fonte para o Report
91
+ const result = await runPoCGenerator(report);
92
+ ```
93
+
94
+ ### Pré-requisitos
95
+ - **Foundry:** `forge` deve estar instalado e acessível. O agente busca em `~/.foundry/bin` e no PATH padrão.
96
+ - **API Key:** `OPENROUTER_API_KEY` deve estar configurada no arquivo `.env`.
97
+
98
+ ## 5. Avaliação de Resultados
99
+
100
+ O `PoCResult` retorna um status que indica a validade da vulnerabilidade ou a eficácia de um patch:
101
+
102
+ | Status | Significado | Ação Recomendada |
103
+ | :--- | :--- | :--- |
104
+ | **`success`** | Exploit executou e passou na assertion. | Vulnerabilidade confirmada. |
105
+ | **`failed`** | Exploit falhou após 5 tentativas. | Verificar `executionLogs` para erro de lógica ou compilação. |
106
+ | **`timeout`** | Forge excedeu 60 segundos. | Possível loop infinito no contrato ou exploit. |
107
+
108
+ ## 6. Base Acadêmica
109
+ A implementação deste agente foi inspirada no framework **PoCo** (Bergman et al., KTH 2025), adaptada para execução local determinística e suporte multi-agente.
src/agents/tester/agent.ts CHANGED
@@ -1,13 +1,168 @@
1
- import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
 
 
 
 
 
 
 
 
 
2
 
3
- import { TesterState } from "./state.ts";
4
 
5
- const runTests: GraphNode<typeof TesterState> = async (_state) => {
6
- return { results: [] };
7
- };
8
 
9
- export const testerAgent = new StateGraph(TesterState)
10
- .addNode("runTests", runTests)
11
- .addEdge(START, "runTests")
12
- .addEdge("runTests", END)
13
- .compile();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import "dotenv/config";
2
+ import { StateGraph, END, START } from "@langchain/langgraph";
3
+ import { PoCStateAnnotation, PoCState } from "./state.js";
4
+ import { generateLocalScaffold } from "./tools/scaffoldGenerator.js";
5
+ import { OracleContext } from "./types.js";
6
+ import { createLLM } from "../../config/llm.ts";
7
+ import { SYSTEM_PROMPT } from "./prompts/system.js";
8
+ import { extractSolidity } from "./utils/extractSolidity.js";
9
+ import { runFoundry } from "./tools/foundryRunner.js";
10
+ import { analyzeFoundryLog } from "./utils/logAnalyzer.js";
11
 
12
+ const MAX_ITERATIONS = 5;
13
 
14
+ const llm = createLLM();
 
 
15
 
16
+ async function oracleNode(state: PoCState): Promise<Partial<PoCState>> {
17
+ console.log("[oracleNode] gerando scaffold para:", state.report.title);
18
+
19
+ const solidityScaffold = generateLocalScaffold(state.report);
20
+ const oracleContext: OracleContext = { solidityScaffold };
21
+
22
+ console.log("[oracleNode] scaffold gerado, tamanho:", solidityScaffold.length, "chars");
23
+ return { oracleContext };
24
+ }
25
+
26
+ async function generatePoCNode(state: PoCState): Promise<Partial<PoCState>> {
27
+ const { report, oracleContext, executionLogs, pocCode, iterations, lastError } = state;
28
+ const isRetry = iterations > 0;
29
+
30
+ const userMessage = isRetry
31
+ ? `O seguinte exploit FALHOU no Foundry.
32
+
33
+ Código anterior:
34
+ \`\`\`solidity
35
+ ${pocCode}
36
+ \`\`\`
37
+
38
+ Output do Forge (última execução):
39
+ ${executionLogs[executionLogs.length - 1]?.slice(0, 3000) ?? "sem logs"}
40
+
41
+ Análise do erro: ${lastError ?? "desconhecido"}
42
+
43
+ Corrija o código. Retorne o arquivo Solidity completo corrigido.`
44
+ : `Relatório de Vulnerabilidade:
45
+ - Título: ${report.title}
46
+ - Tipo: ${report.type}
47
+ - Descrição: ${report.description}
48
+ - Vetor de Ataque: ${report.attackVector}
49
+ ${report.exploitablePaths ? `- Caminhos de Exploração:\n * ${report.exploitablePaths.join("\n * ")}` : ""}
50
+
51
+ Scaffold (complete APENAS test_Exploit):
52
+ \`\`\`solidity
53
+ ${oracleContext!.solidityScaffold}
54
+ \`\`\``;
55
+
56
+ console.log(`[testerAgent] generatePoCNode iteração ${iterations + 1}, isRetry=${isRetry}`);
57
+
58
+ try {
59
+ const response = await llm.invoke([
60
+ { role: "system", content: SYSTEM_PROMPT },
61
+ { role: "user", content: userMessage },
62
+ ]);
63
+ const solidityCode = extractSolidity(response.content as string);
64
+ console.log("[testerAgent] Solidity extraído, tamanho:", solidityCode.length);
65
+ return { pocCode: solidityCode, iterations: 1 };
66
+ } catch (err) {
67
+ console.error("[testerAgent] falha na geração:", (err as Error).message);
68
+ return { iterations: 1, lastError: `Erro na geração/extração: ${(err as Error).message}` };
69
+ }
70
+ }
71
+
72
+ async function runFoundryNode(state: PoCState): Promise<Partial<PoCState>> {
73
+ console.log("[testerAgent] Executando runFoundryNode...");
74
+
75
+ const trimmedCode = state.pocCode.trim();
76
+ const isMissingCode = trimmedCode.length === 0;
77
+ const isMissingContract = !trimmedCode.includes("contract ExploitTest");
78
+ const isMissingTest = !trimmedCode.includes("function test_Exploit()");
79
+ const isPlaceholder = trimmedCode.includes("TODO: implementar exploit");
80
+ if (isMissingCode || isMissingContract || isMissingTest || isPlaceholder) {
81
+ const summary = state.lastError ?? (isMissingCode
82
+ ? "Código Solidity ausente. O LLM não retornou o arquivo do exploit."
83
+ : isMissingContract
84
+ ? "Contrato ExploitTest não encontrado no arquivo."
85
+ : isMissingTest
86
+ ? "Função test_Exploit() não encontrada no arquivo."
87
+ : "Exploit não implementado (placeholder TODO ainda presente)."
88
+ );
89
+ const status = state.iterations >= MAX_ITERATIONS ? "failed" : "running";
90
+ return {
91
+ executionLogs: [summary],
92
+ lastError: summary,
93
+ status,
94
+ };
95
+ }
96
+
97
+ const result = await runFoundry(state.pocCode);
98
+ const analysis = analyzeFoundryLog(result);
99
+ const noTestsFound = result.combined.includes("No tests found");
100
+ const summary = noTestsFound
101
+ ? "Forge não encontrou nenhum teste. Verifique se o contrato se chama ExploitTest e se existe test_Exploit()."
102
+ : analysis.summary;
103
+ const passed = result.exitCode === 0 && result.stdout.includes("ok") && !noTestsFound;
104
+ const isLastAttempt = state.iterations >= MAX_ITERATIONS;
105
+
106
+ const status = passed
107
+ ? "success"
108
+ : result.timedOut
109
+ ? "timeout"
110
+ : isLastAttempt
111
+ ? "failed"
112
+ : "running";
113
+
114
+ console.log(`[testerAgent] Resultado Foundry: exitCode=${result.exitCode}, passed=${passed}`);
115
+ if (!passed) {
116
+ console.log(`[testerAgent] Falha detectada: ${analysis.summary}`);
117
+ }
118
+
119
+ return {
120
+ executionLogs: [result.combined], // reducer append
121
+ lastError: summary,
122
+ status,
123
+ };
124
+ }
125
+
126
+ async function reflectNode(state: PoCState): Promise<Partial<PoCState>> {
127
+ const lastLog = state.executionLogs[state.executionLogs.length - 1];
128
+ if (!lastLog) {
129
+ return { lastError: "Sem logs disponíveis para análise." };
130
+ }
131
+
132
+ const mockResult = {
133
+ exitCode: 1, timedOut: lastLog.includes("TIMEOUT"),
134
+ stdout: "", stderr: "", combined: lastLog,
135
+ };
136
+
137
+ const analysis = analyzeFoundryLog(mockResult as any);
138
+
139
+ console.log(`[reflectNode] categoria: ${analysis.category}`);
140
+ console.log(`[reflectNode] resumo: ${analysis.summary}`);
141
+
142
+ return {
143
+ lastError: `[${analysis.category.toUpperCase()}] ${analysis.summary}\n\nLinhas relevantes:\n${analysis.relevantLines.join("\n")}`,
144
+ };
145
+ }
146
+
147
+ function routeAfterFoundry(state: PoCState): "reflectNode" | typeof END {
148
+ if (state.status === "success") return END;
149
+ if (state.status === "timeout") return END;
150
+ if (state.iterations >= MAX_ITERATIONS) return END;
151
+ return "reflectNode";
152
+ }
153
+
154
+ const graph = new StateGraph(PoCStateAnnotation)
155
+ .addNode("oracleNode", oracleNode)
156
+ .addNode("generatePoCNode", generatePoCNode)
157
+ .addNode("runFoundryNode", runFoundryNode)
158
+ .addNode("reflectNode", reflectNode)
159
+ .addEdge(START, "oracleNode")
160
+ .addEdge("oracleNode", "generatePoCNode")
161
+ .addEdge("generatePoCNode", "runFoundryNode")
162
+ .addConditionalEdges("runFoundryNode", routeAfterFoundry, {
163
+ reflectNode: "reflectNode",
164
+ [END]: END,
165
+ })
166
+ .addEdge("reflectNode", "generatePoCNode");
167
+
168
+ export const testerAgent = graph.compile();
src/agents/tester/data/ExploitTest.t.sol ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import {Test} from "forge-std/Test.sol";
5
+ import {console} from "forge-std/console.sol";
6
+ import {Size} from "@src/Size.sol";
7
+ import {DepositParams} from "@src/libraries/actions/Deposit.sol";
8
+ import {WithdrawParams} from "@src/libraries/actions/Withdraw.sol";
9
+ import {RepayParams} from "@src/libraries/actions/Repay.sol";
10
+ import {BuyCreditMarketParams} from "@src/libraries/actions/BuyCreditMarket.sol";
11
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
12
+ import {RESERVED_ID} from "@src/libraries/LoanLibrary.sol";
13
+
14
+ /**
15
+ * @title MulticallInvariantBypassPoC
16
+ * @notice Demonstrates how the multicall invariant check can be bypassed
17
+ *
18
+ * VULNERABILITY: The multicall function validates that borrowAToken increase <= debtToken decrease
19
+ * only at the END of all operations, checking NET changes. This allows attackers to:
20
+ * 1. Deposit massive amounts (exceeding cap)
21
+ * 2. Perform operations with excess liquidity
22
+ * 3. Withdraw excess before final validation
23
+ *
24
+ * The invariant passes because net changes appear compliant, but intermediate states
25
+ * violate the cap restrictions.
26
+ */
27
+ contract MulticallInvariantBypassPoC is Test {
28
+ Size public size;
29
+
30
+ address public attacker;
31
+ address public victim;
32
+ address public lender;
33
+
34
+ IERC20 public borrowToken;
35
+ IERC20 public collateralToken;
36
+
37
+ uint256 public constant INITIAL_BORROW_SUPPLY = 9_990_000e6; // 9.99M (10k below cap)
38
+ uint256 public constant BORROW_CAP = 10_000_000e6; // 10M cap
39
+ uint256 public constant ATTACKER_DEBT = 100_000e6; // 100k debt
40
+ uint256 public constant EXPLOIT_DEPOSIT = 5_000_000e6; // 5M deposit (far exceeds cap)
41
+ uint256 public constant EXPLOIT_WITHDRAW = 4_900_000e6; // 4.9M withdraw
42
+
43
+ function setUp() public {
44
+ // Setup test accounts
45
+ attacker = makeAddr("attacker");
46
+ victim = makeAddr("victim");
47
+ lender = makeAddr("lender");
48
+
49
+ // Deploy Size contract
50
+ // Note: In a real test, you would need to properly initialize Size with all dependencies
51
+ // For this PoC, we'll use a mock setup that demonstrates the vulnerability
52
+
53
+ vm.label(attacker, "Attacker");
54
+ vm.label(victim, "Victim");
55
+ vm.label(lender, "Lender");
56
+ }
57
+
58
+ /**
59
+ * @notice Demonstrates the cap bypass exploit
60
+ *
61
+ * ATTACK FLOW:
62
+ * 1. Deposit 5M USDC → borrowAToken supply jumps to 14.99M (4.99M over cap!)
63
+ * 2. Repay 100k debt → debtToken decreases by 100k
64
+ * 3. Withdraw 4.9M USDC → borrowAToken supply drops to 10M
65
+ *
66
+ * RESULT:
67
+ * - Net borrowAToken increase: 10k
68
+ * - Net debtToken decrease: 100k
69
+ * - Invariant check: 10k <= 100k ✓ PASSES
70
+ * - But attacker temporarily held 4.99M excess borrowAToken!
71
+ */
72
+ function testMulticallCapBypass() public {
73
+ // This test demonstrates the vulnerability conceptually
74
+ // In a real scenario, you would:
75
+ // 1. Deploy and initialize Size with proper configuration
76
+ // 2. Setup initial state with borrowAToken supply near cap
77
+ // 3. Create a debt position for the attacker
78
+ // 4. Execute the multicall exploit
79
+
80
+ console.log("=== MULTICALL CAP BYPASS VULNERABILITY ===");
81
+ console.log("");
82
+ console.log("INITIAL STATE:");
83
+ console.log("- BorrowAToken Supply: %s", INITIAL_BORROW_SUPPLY);
84
+ console.log("- BorrowAToken Cap: %s", BORROW_CAP);
85
+ console.log("- Space below cap: %s", BORROW_CAP - INITIAL_BORROW_SUPPLY);
86
+ console.log("- Attacker's debt: %s", ATTACKER_DEBT);
87
+ console.log("");
88
+
89
+ // Simulate the exploit flow
90
+ uint256 borrowSupplyBefore = INITIAL_BORROW_SUPPLY;
91
+ uint256 debtSupplyBefore = ATTACKER_DEBT;
92
+
93
+ console.log("EXPLOIT EXECUTION:");
94
+ console.log("");
95
+
96
+ // Step 1: Deposit 5M (exceeds cap by 4.99M)
97
+ console.log("Step 1: Deposit %s USDC", EXPLOIT_DEPOSIT);
98
+ uint256 borrowSupplyAfterDeposit = borrowSupplyBefore + EXPLOIT_DEPOSIT;
99
+ console.log(" -> BorrowAToken supply: %s", borrowSupplyAfterDeposit);
100
+ console.log(" -> EXCEEDS CAP BY: %s", borrowSupplyAfterDeposit - BORROW_CAP);
101
+ console.log("");
102
+
103
+ // Step 2: Repay 100k debt
104
+ console.log("Step 2: Repay %s debt", ATTACKER_DEBT);
105
+ uint256 debtSupplyAfterRepay = debtSupplyBefore - ATTACKER_DEBT;
106
+ uint256 borrowSupplyAfterRepay = borrowSupplyAfterDeposit - ATTACKER_DEBT;
107
+ console.log(" -> DebtToken supply: %s", debtSupplyAfterRepay);
108
+ console.log(" -> BorrowAToken supply: %s", borrowSupplyAfterRepay);
109
+ console.log("");
110
+
111
+ // Step 3: Withdraw 4.9M
112
+ console.log("Step 3: Withdraw %s USDC", EXPLOIT_WITHDRAW);
113
+ uint256 borrowSupplyAfter = borrowSupplyAfterRepay - EXPLOIT_WITHDRAW;
114
+ console.log(" -> BorrowAToken supply: %s", borrowSupplyAfter);
115
+ console.log("");
116
+
117
+ // Calculate net changes
118
+ uint256 netBorrowIncrease = borrowSupplyAfter - borrowSupplyBefore;
119
+ uint256 netDebtDecrease = debtSupplyBefore - 0; // All debt repaid
120
+
121
+ console.log("FINAL STATE:");
122
+ console.log("- Net borrowAToken increase: %s", netBorrowIncrease);
123
+ console.log("- Net debtToken decrease: %s", netDebtDecrease);
124
+ console.log("- Invariant check: %s <= %s", netBorrowIncrease, netDebtDecrease);
125
+ console.log("- Invariant status: %s", netBorrowIncrease <= netDebtDecrease ? "PASS" : "FAIL");
126
+ console.log("");
127
+
128
+ // Verify the invariant passes
129
+ assertLe(netBorrowIncrease, netDebtDecrease, "Invariant should pass");
130
+
131
+ console.log("=== VULNERABILITY CONFIRMED ===");
132
+ console.log("During execution, borrowAToken supply reached: %s", borrowSupplyAfterDeposit);
133
+ console.log("This EXCEEDED the cap of %s by: %s", BORROW_CAP, borrowSupplyAfterDeposit - BORROW_CAP);
134
+ console.log("");
135
+ console.log("The attacker temporarily held %s excess borrowAToken", EXPLOIT_DEPOSIT - ATTACKER_DEBT);
136
+ console.log("This excess could be used for:");
137
+ console.log(" - Market manipulation");
138
+ console.log(" - Arbitrage opportunities");
139
+ console.log(" - Flash-loan-like attacks");
140
+ console.log(" - Bypassing risk parameters");
141
+ console.log("");
142
+ console.log("Yet the invariant check PASSED because it only validates NET changes!");
143
+ }
144
+
145
+ /**
146
+ * @notice Demonstrates using excess liquidity for market manipulation
147
+ *
148
+ * This shows how the temporarily available excess borrowAToken can be weaponized
149
+ * during the multicall execution to manipulate markets or perform other attacks.
150
+ */
151
+ function testMulticallMarketManipulation() public {
152
+ console.log("=== MARKET MANIPULATION EXPLOIT ===");
153
+ console.log("");
154
+ console.log("ATTACK SCENARIO:");
155
+ console.log("1. Deposit %s USDC (exceeds cap)", EXPLOIT_DEPOSIT);
156
+ console.log("2. Use %s borrowAToken for market operations", EXPLOIT_WITHDRAW);
157
+ console.log("3. Repay %s debt", ATTACKER_DEBT);
158
+ console.log("4. Withdraw remaining excess");
159
+ console.log("");
160
+
161
+ uint256 excessLiquidity = EXPLOIT_DEPOSIT - ATTACKER_DEBT;
162
+
163
+ console.log("IMPACT:");
164
+ console.log("- Attacker gains temporary access to %s excess liquidity", excessLiquidity);
165
+ console.log("- This can be used to:");
166
+ console.log(" * Buy large credit positions (distorting market prices)");
167
+ console.log(" * Manipulate interest rates");
168
+ console.log(" * Front-run other users");
169
+ console.log(" * Extract value from the protocol");
170
+ console.log("");
171
+ console.log("- All while the invariant check passes!");
172
+ console.log("- The cap is meant to prevent exactly this kind of exposure");
173
+
174
+ // Verify the exploit provides significant excess liquidity
175
+ assertGt(excessLiquidity, 1_000_000e6, "Exploit should provide >1M excess liquidity");
176
+ }
177
+
178
+ /**
179
+ * @notice Demonstrates the root cause of the vulnerability
180
+ *
181
+ * The issue is that the invariant validation happens AFTER all multicall operations,
182
+ * checking only NET changes rather than intermediate states.
183
+ */
184
+ function testRootCauseAnalysis() public {
185
+ console.log("=== ROOT CAUSE ANALYSIS ===");
186
+ console.log("");
187
+ console.log("VULNERABLE CODE PATTERN:");
188
+ console.log("1. Multicall executes all operations sequentially");
189
+ console.log("2. During execution, deposit() skips cap validation:");
190
+ console.log(" if (!state.data.isMulticall) {");
191
+ console.log(" state.validateBorrowATokenCap();");
192
+ console.log(" }");
193
+ console.log("");
194
+ console.log("3. After all operations, invariant is checked:");
195
+ console.log(" validateBorrowATokenIncreaseLteDebtTokenDecrease()");
196
+ console.log("");
197
+ console.log("4. Invariant only compares NET changes:");
198
+ console.log(" borrowATokenSupplyIncrease = supplyAfter - supplyBefore");
199
+ console.log(" debtTokenSupplyDecrease = debtBefore - debtAfter");
200
+ console.log(" require(increase <= decrease)");
201
+ console.log("");
202
+ console.log("PROBLEM:");
203
+ console.log("- Intermediate states are NEVER validated");
204
+ console.log("- Attacker can deposit huge amounts, use them, then withdraw");
205
+ console.log("- As long as net changes satisfy the invariant, exploit succeeds");
206
+ console.log("");
207
+ console.log("CORRECT APPROACH:");
208
+ console.log("- Validate cap on EVERY deposit, even in multicall");
209
+ console.log("- OR track maximum supply reached during multicall");
210
+ console.log("- OR validate intermediate states, not just final state");
211
+ }
212
+
213
+ /**
214
+ * @notice Shows the mathematical proof of the bypass
215
+ */
216
+ function testMathematicalProof() public {
217
+ console.log("=== MATHEMATICAL PROOF ===");
218
+ console.log("");
219
+
220
+ uint256 S0 = INITIAL_BORROW_SUPPLY; // Initial supply
221
+ uint256 C = BORROW_CAP; // Cap
222
+ uint256 D = ATTACKER_DEBT; // Debt to repay
223
+ uint256 X = EXPLOIT_DEPOSIT; // Exploit deposit amount
224
+
225
+ console.log("Given:");
226
+ console.log(" S0 = %s (initial supply)", S0);
227
+ console.log(" C = %s (cap)", C);
228
+ console.log(" D = %s (debt)", D);
229
+ console.log(" X = %s (deposit amount)", X);
230
+ console.log("");
231
+
232
+ console.log("Execution:");
233
+ uint256 S1 = S0 + X;
234
+ console.log(" After deposit: S1 = S0 + X = %s", S1);
235
+ console.log(" Cap violation: S1 - C = %s", S1 - C);
236
+ console.log("");
237
+
238
+ uint256 S2 = S1 - D;
239
+ console.log(" After repay: S2 = S1 - D = %s", S2);
240
+ console.log("");
241
+
242
+ uint256 W = X - D;
243
+ uint256 S3 = S2 - W;
244
+ console.log(" After withdraw W = X - D = %s: S3 = %s", W, S3);
245
+ console.log("");
246
+
247
+ console.log("Invariant check:");
248
+ uint256 netIncrease = S3 - S0;
249
+ uint256 netDecrease = D;
250
+ console.log(" Net increase: S3 - S0 = %s", netIncrease);
251
+ console.log(" Net decrease: D = %s", netDecrease);
252
+ console.log(" Check: %s <= %s ? %s", netIncrease, netDecrease, netIncrease <= netDecrease);
253
+ console.log("");
254
+
255
+ console.log("Conclusion:");
256
+ console.log(" Invariant PASSES, but S1 = %s exceeded cap C = %s", S1, C);
257
+ console.log(" Excess exposure: %s", S1 - C);
258
+
259
+ assertTrue(netIncrease <= netDecrease, "Invariant passes");
260
+ assertTrue(S1 > C, "But cap was violated during execution");
261
+ }
262
+ }
src/agents/tester/data/input.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "title": "Unrestricted Reward String in burn() Enables Log Poisoning and Off-Chain Manipulation",
3
+ "description": "The burn() function accepts an arbitrary user-supplied string as the 'recompensa' parameter and emits it directly in the RewardRedeemed event without any validation, allowlist check, or length restriction. Any caller can pass malicious, misleading, or excessively long strings into the on-chain event log. Off-chain systems (loyalty backends, indexers, dashboards) that consume this event and trust the 'recompensa' field are vulnerable to: (1) log poisoning / event spoofing — a user can emit 'recompensa' values like 'Admin Grant: 1000 free coffees' that were never authorized; (2) denial-of-service on indexers via extremely large strings; (3) injection attacks if the string is rendered in a web UI without sanitization.",
4
+ "recommendation": "Replace the free-form string parameter with an enumerated reward identifier (e.g. uint8 rewardId) and maintain an owner-controlled mapping of valid reward IDs to descriptions. This constrains what can appear in event logs to only administrator-approved values. Example refactor:\n\nsolidity\n// Owner-managed reward catalogue\nmapping(uint8 => string) public rewardCatalogue;\n\nfunction setReward(uint8 id, string calldata description) external onlyOwner {\n rewardCatalogue[id] = description;\n}\n\nfunction burn(uint256 amount, uint8 rewardId) public {\n require(amount > 0, \"CafeToken: quantidade invalida\");\n require(bytes(rewardCatalogue[rewardId]).length > 0, \"CafeToken: recompensa invalida\");\n require(balanceOf(msg.sender) >= amount, \"CafeToken: saldo insuficiente\");\n _burn(msg.sender, amount);\n emit RewardRedeemed(msg.sender, amount, rewardId);\n}\n\nThis ensures only legitimate, pre-approved rewards are ever recorded on-chain.",
5
+ "severity": "medium",
6
+ "codeSnippet": "function burn(uint256 amount, string memory recompensa) public {\n require(amount > 0, \"CafeToken: a quantidade a queimar deve ser maior que zero\");\n require(balanceOf(msg.sender) >= amount, \"CafeToken: saldo insuficiente para queimar tokens\");\n _burn(msg.sender, amount);\n emit RewardRedeemed(msg.sender, amount, recompensa); // <-- arbitrary user input emitted as event data\n}",
7
+ "location": "L71-L81",
8
+ "path": "contracts/CafeToken.sol",
9
+ "judgeReview": {
10
+ "review": "Confirmed valid finding. The vulnerability is real and the attack surface is well-defined. The burn() function places zero constraints on the 'recompensa' string before broadcasting it as an authoritative event. The severity is appropriately rated medium rather than high because: (a) no funds are directly at risk from the contract itself — a caller can only burn their own tokens; (b) the primary damage surface is off-chain systems and UX layers that consume events, not the on-chain state. However, in a loyalty program context where the event log IS the business record, the ability for any token holder to forge arbitrary reward redemption records is a meaningful integrity risk. The exploit is trivially reproducible with zero prerequisites beyond holding at least 1 CAFE token. The recommendation to use an enumerated reward catalogue with owner-gated registration is sound and idiomatic for this pattern.",
11
+ "confidence": 0.91,
12
+ "exploitablePaths": [
13
+ "Attacker holds ≥1 CAFE token → calls burn(1, 'Gold Member Upgrade: 500 free coffees') → forged RewardRedeemed event is emitted and indexed by the loyalty backend as a legitimate redemption record",
14
+ "Attacker calls burn(1, <64KB string>) repeatedly → bloats event logs and causes out-of-memory or timeout failures in off-chain indexers processing the RewardRedeemed event stream",
15
+ "Web dashboard renders recompensa field as raw HTML → attacker passes '<script>...</script>' as recompensa → stored XSS executes in the admin panel of any operator that displays redemption history without sanitization"
16
+ ]
17
+ }
18
+ }
src/agents/tester/data/input_centrifuge.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "title": "Escrow mismatch in LiquidityPool due to price changes during epoch execution",
3
+ "description": "The LiquidityPool contract relies on an external InvestmentManager to process deposits and mints. When an investor requests a deposit, their assets are locked. During the epoch execution, if the tranche token price changes significantly, the amount of shares to be minted (TokenShares) may exceed the available balance in the Escrow contract, causing subsequent collection transactions (mint/deposit) to revert for some users while others succeed.",
4
+ "recommendation": "Ensure the Escrow contract is always sufficiently funded by validating price impacts before final execution or implement a more robust collection mechanism that handles partial fills or explicit failure states when Escrow is empty.",
5
+ "severity": "high",
6
+ "codeSnippet": "function deposit(uint256 assets, address receiver) public withApproval(receiver) returns (uint256 shares) {\n shares = investmentManager.processDeposit(receiver, assets);\n emit Deposit(address(this), receiver, assets, shares);\n}",
7
+ "location": "L148-L151",
8
+ "path": "contracts/LiquidityPool.sol",
9
+ "judgeReview": {
10
+ "review": "Confirmed valid finding. The vulnerability occurs when multiple investors deposit at different prices within the same logic flow. If the price in the second epoch is higher/lower than expected, the calculation of total shares needed in Escrow might be incorrect, leading to a denial of service (revert) for users trying to collect their shares after the price update.",
11
+ "confidence": 0.95,
12
+ "exploitablePaths": [
13
+ "User A deposits 100 assets at price 1.25 -> User B deposits 100 assets at price 2.0 -> Price updates -> User A collects successfully -> User B tries to collect but Escrow is empty -> Transaction reverts."
14
+ ]
15
+ }
16
+ }
src/agents/tester/index.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { testerAgent } from "./agent.js";
2
+ import { VulnerabilityReport, PoCResult } from "./types.js";
3
+
4
+ /**
5
+ * Entry point para o Agente Gerador de PoCs.
6
+ * @param report O relatório de vulnerabilidade (mapeado a partir do Finding do Auditor).
7
+ * @returns PoCResult contendo o código do exploit e o status da execução.
8
+ */
9
+ export async function runPoCGenerator(report: VulnerabilityReport): Promise<PoCResult> {
10
+ console.log(`[runPoCGenerator] Iniciando para: ${report.id} — ${report.title}`);
11
+
12
+ const finalState = await testerAgent.invoke({ report });
13
+
14
+ const result: PoCResult = {
15
+ reportId: report.id,
16
+ status: finalState.status === "running" ? "failed" : finalState.status,
17
+ solidityCode: finalState.pocCode,
18
+ executionLogs: finalState.executionLogs,
19
+ iterations: finalState.iterations,
20
+ };
21
+
22
+ console.log(`[runPoCGenerator] Concluído — status=${result.status}, iterações=${result.iterations}`);
23
+ return result;
24
+ }
25
+
26
+ export type { VulnerabilityReport, PoCResult, Finding, OracleContext } from "./types.js";
27
+ export { testerAgent } from "./agent.js";
src/agents/tester/prompts/system.ts ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const SYSTEM_PROMPT = `You are an expert smart contract security testing specialist. Your mission is to generate executable Proof-of-Concept (PoC) exploits demonstrating vulnerabilities using Foundry.
2
+
3
+ ## PoC Explainability
4
+ Write exploits as executable demonstrations that clearly prove the vulnerability. Include detailed comments documenting each attack step, the vulnerability being exploited, and why the exploit succeeds. The PoC must be self-explanatory to security auditors.
5
+
6
+ ## Vulnerability Analysis
7
+ Parse the vulnerability description provided and analyze the vulnerability type, affected code sections, and potential impact. Analyze the contract logic to understand the root cause before developing exploits.
8
+
9
+ ## Testing Framework Guidelines
10
+ Use Foundry exclusively for testing. Utilize Foundry cheatcodes for test control: "vm.prank()" for identity switching, "vm.deal()" for ETH funding, "vm.warp()" for time manipulation, "vm.expectRevert()" for failure testing.
11
+
12
+ ## Scaffold Strict Compliance
13
+ - The target contract's full source code is ALREADY included at the top of the scaffold. You can and MUST call its functions directly (e.g., \`target.deposit()\`). Do NOT create fake interfaces or use low-level \`.call(abi.encodeWithSignature(...))\`.
14
+ - YOU MUST RETURN THE ENTIRE FILE PROVIDED IN THE SCAFFOLD. Do not omit the \`setUp()\` function or the original contract source code. Your output will overwrite the file directly.
15
+ - DO NOT rename \`test_Exploit()\`. You MUST implement your exploit inside \`function test_Exploit() public\`.
16
+ - DO NOT use characters with accents (like ã, ç, é, etc.) in string literals (e.g., inside \`assertEq\` or \`require\`). Use ONLY plain ASCII, or prefix with \`unicode"..."\` to avoid Solc compiler errors.
17
+
18
+ ## PoC Executability
19
+ Ensure all generated code compiles successfully. Write ONLY the test file code (helper contracts + ExploitTest). Resolve all compilation errors and logic reverts while preserving original contract logic.
20
+
21
+ ## Iterative Refinement
22
+ Debug compilation errors and test failures systematically using Forge output. If stuck on the same issue for >3 attempts, shift to a minimal working demonstration—proving the vulnerability exists matters more than setup complexity.
23
+
24
+ ## Exploit Soundness
25
+ The assertion in your test MUST prove the vulnerability. For example, if funds are stolen, assert that the vault balance decreased and the attacker balance increased.
26
+
27
+ ## Output Format
28
+ Return ONLY a code block with the full ExploitTest contract and any helper attacker contracts. Do not include markdown outside the code block.
29
+
30
+ ## Examples (Few-Shot)
31
+
32
+ **Input Example:**
33
+ Vulnerability: Reentrancy in withdraw() allows draining the contract.
34
+ Scaffold:
35
+ \`\`\`solidity
36
+ // SPDX-License-Identifier: UNLICENSED
37
+ pragma solidity ^0.8.20;
38
+ import "forge-std/Test.sol";
39
+ contract Target { function withdraw(uint256) public {} } // Source code
40
+ contract ExploitTest is Test {
41
+ Target target;
42
+ function setUp() public { target = new Target(); }
43
+ function test_Exploit() public {
44
+ // TODO: implementar exploit aqui
45
+ }
46
+ }
47
+ \`\`\`
48
+
49
+ **Expected Output:**
50
+ \`\`\`solidity
51
+ // SPDX-License-Identifier: UNLICENSED
52
+ pragma solidity ^0.8.20;
53
+ import "forge-std/Test.sol";
54
+ contract Target { function withdraw(uint256) public {} } // Source code
55
+
56
+ // We can define helper contracts outside the main test contract
57
+ contract Attacker {
58
+ Target target;
59
+ constructor(address _target) {
60
+ target = Target(_target);
61
+ }
62
+ fallback() external payable {
63
+ if (address(target).balance >= 1 ether) {
64
+ target.withdraw(1 ether);
65
+ }
66
+ }
67
+ function attack() external {
68
+ target.withdraw(1 ether);
69
+ }
70
+ }
71
+
72
+ contract ExploitTest is Test {
73
+ Target target;
74
+
75
+ // IMPORTANT: We include the EXACT setUp() provided in the scaffold.
76
+ function setUp() public {
77
+ target = new Target();
78
+ }
79
+
80
+ function test_Exploit() public {
81
+ vm.startPrank(address(0xBEEF));
82
+
83
+ // 1. Deploy malicious contract
84
+ Attacker attacker = new Attacker(address(target));
85
+
86
+ // 2. Exploit the vulnerability using direct function calls
87
+ attacker.attack();
88
+
89
+ // 3. Verify the exploit succeeded (no special characters in assertion strings)
90
+ assertEq(address(target).balance, 0, "Target contract should be drained");
91
+
92
+ vm.stopPrank();
93
+ }
94
+ }
95
+ \`\`\`
96
+ `.trim();
src/agents/tester/state.ts CHANGED
@@ -1,8 +1,38 @@
1
- import { StateSchema } from "@langchain/langgraph";
2
- import { z } from "zod";
3
 
4
- export const TesterState = new StateSchema({
5
- solidityFiles: z.array(z.string()).default([]),
6
- vulnerability: z.record(z.string(), z.any()).default({}),
7
- results: z.array(z.any()).default([]),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  });
 
 
 
1
+ import { Annotation } from "@langchain/langgraph";
2
+ import { VulnerabilityReport, OracleContext } from "./types.js";
3
 
4
+ export const PoCStateAnnotation = Annotation.Root({
5
+ report: Annotation<VulnerabilityReport>(),
6
+
7
+ oracleContext: Annotation<OracleContext | null>({
8
+ default: () => null,
9
+ reducer: (_, y) => y, // overwrite — filled once by oracleNode
10
+ }),
11
+
12
+ pocCode: Annotation<string>({
13
+ default: () => "",
14
+ reducer: (_, y) => y, // overwrite — always latest version
15
+ }),
16
+
17
+ executionLogs: Annotation<string[]>({
18
+ default: () => [],
19
+ reducer: (x, y) => x.concat(y), // append — never lose previous logs
20
+ }),
21
+
22
+ lastError: Annotation<string | null>({
23
+ default: () => null,
24
+ reducer: (_, y) => y, // overwrite — last error analysis
25
+ }),
26
+
27
+ iterations: Annotation<number>({
28
+ default: () => 0,
29
+ reducer: (x, y) => x + y, // additive — incremented by +1 per call
30
+ }),
31
+
32
+ status: Annotation<"running" | "success" | "failed" | "timeout">({
33
+ default: () => "running",
34
+ reducer: (_, y) => y, // overwrite
35
+ }),
36
  });
37
+
38
+ export type PoCState = typeof PoCStateAnnotation.State;
src/agents/tester/tools/foundryRunner.ts ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ import { writeFile, access } from "fs/promises";
4
+ import { join } from "path";
5
+
6
+ const execAsync = promisify(exec);
7
+ const SANDBOX = process.env.SANDBOX_DIR || "/tmp/poc-sandbox";
8
+ const TIMEOUT_MS = 60_000;
9
+
10
+ export interface FoundryResult {
11
+ exitCode: number;
12
+ stdout: string;
13
+ stderr: string;
14
+ combined: string;
15
+ timedOut: boolean;
16
+ }
17
+
18
+ /**
19
+ * Garante que o sandbox Foundry existe e está inicializado.
20
+ */
21
+ async function ensureSandbox() {
22
+ try {
23
+ await access(join(SANDBOX, "foundry.toml"));
24
+ } catch {
25
+ console.log("[foundryRunner] Sandbox não encontrado. Inicializando...");
26
+ // Caminho absoluto para o script de setup (assume execução da raiz do projeto)
27
+ await execAsync("./scripts/setup-sandbox.sh");
28
+ }
29
+ }
30
+
31
+ export async function runFoundry(solidityCode: string): Promise<FoundryResult> {
32
+ await ensureSandbox();
33
+
34
+ // Escrever o arquivo no sandbox
35
+ await writeFile(`${SANDBOX}/test/Exploit.t.sol`, solidityCode, "utf-8");
36
+
37
+ try {
38
+ const { stdout, stderr } = await execAsync(
39
+ "forge test --match-contract ExploitTest -vvvv",
40
+ {
41
+ cwd: SANDBOX,
42
+ timeout: TIMEOUT_MS,
43
+ env: { ...process.env, PATH: `${process.env.HOME}/.foundry/bin:${process.env.PATH}` }
44
+ }
45
+ );
46
+ return {
47
+ exitCode: 0,
48
+ stdout,
49
+ stderr,
50
+ combined: `STDOUT:\n${stdout}\nSTDERR:\n${stderr}`,
51
+ timedOut: false,
52
+ };
53
+ } catch (err: any) {
54
+ if (err.killed || err.signal === "SIGTERM") {
55
+ return {
56
+ exitCode: -1, stdout: "", stderr: "Forge timed out",
57
+ combined: `TIMEOUT após ${TIMEOUT_MS / 1000}s`,
58
+ timedOut: true,
59
+ };
60
+ }
61
+ return {
62
+ exitCode: err.code ?? 1,
63
+ stdout: err.stdout ?? "",
64
+ stderr: err.stderr ?? "",
65
+ combined: `STDOUT:\n${err.stdout ?? ""}\nSTDERR:\n${err.stderr ?? ""}`,
66
+ timedOut: false,
67
+ };
68
+ }
69
+ }
src/agents/tester/tools/scaffoldGenerator.ts ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { VulnerabilityReport } from "../types.js";
2
+
3
+ export function generateLocalScaffold(report: VulnerabilityReport): string {
4
+ const cheatcodes = report.suggestedCheatcodes?.join(", ") ?? "vm.deal, vm.prank, vm.warp";
5
+
6
+ return `// SPDX-License-Identifier: UNLICENSED
7
+ pragma solidity ^0.8.20;
8
+
9
+ import "forge-std/Test.sol";
10
+ import "forge-std/console.sol";
11
+
12
+ // ── Código-fonte do contrato vulnerável ──────────────────────────────────────
13
+ ${report.affectedContract.sourceCode}
14
+ // ─────────────────────────────────────────────────────────────────────────────
15
+
16
+ contract ExploitTest is Test {
17
+ ${report.affectedContract.name} target;
18
+ address constant ATTACKER = address(0xBEEF);
19
+
20
+ // setUp() gerado automaticamente pelo Oracle — NÃO MODIFICAR
21
+ function setUp() public {
22
+ target = new ${report.affectedContract.name}();
23
+ vm.deal(address(target), 100 ether);
24
+ vm.deal(ATTACKER, 10 ether);
25
+ vm.label(address(target), "TARGET");
26
+ vm.label(ATTACKER, "ATTACKER");
27
+ }
28
+
29
+ // Vulnerabilidade: ${report.title}
30
+ // Tipo: ${report.type}
31
+ // Vetor: ${report.attackVector}
32
+ ${report.exploitablePaths ? `// Caminhos de Exploração:\n // - ${report.exploitablePaths.join("\n // - ")}` : ""}
33
+ // Cheatcodes sugeridos: ${cheatcodes}
34
+ //
35
+ // COMPLETE APENAS ESTA FUNÇÃO — não altere setUp() nem os campos acima
36
+ function test_Exploit() public {
37
+ vm.startPrank(ATTACKER);
38
+ // TODO: implementar exploit aqui
39
+ vm.stopPrank();
40
+ }
41
+ }`.trim();
42
+ }
src/agents/tester/types.ts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface Finding {
2
+ title: string;
3
+ description: string;
4
+ recommendation: string;
5
+ severity: "high" | "medium" | "low";
6
+ codeSnippet: string;
7
+ location: string;
8
+ path: string;
9
+ judgeReview: {
10
+ review: string;
11
+ confidence: number;
12
+ exploitablePaths: string[];
13
+ };
14
+ }
15
+
16
+ export interface VulnerabilityReport {
17
+ id: string;
18
+ severity: "critical" | "high" | "medium" | "low";
19
+ type: string;
20
+ title: string;
21
+ description: string;
22
+ affectedContract: {
23
+ name: string;
24
+ sourceCode: string; // Solidity completo, preferencialmente flattened
25
+ };
26
+ attackVector: string;
27
+ suggestedCheatcodes?: string[];
28
+ codeSnippet?: string;
29
+ location?: string;
30
+ exploitablePaths?: string[];
31
+ }
32
+
33
+ export interface OracleContext {
34
+ solidityScaffold: string; // Exploit.t.sol parcial com setUp() pronto
35
+ }
36
+
37
+ export interface PoCResult {
38
+ reportId: string;
39
+ status: "success" | "failed" | "timeout";
40
+ solidityCode: string;
41
+ executionLogs: string[];
42
+ iterations: number;
43
+ }
src/agents/tester/utils/extractSolidity.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function extractSolidity(llmOutput: string): string {
2
+ // Caso 1: bloco ```solidity ... ``` padrão
3
+ const match = llmOutput.match(/```solidity\s*([\s\S]*?)```/);
4
+ if (match) return match[1].trim();
5
+
6
+ // Caso 2: LLM omitiu backticks mas começa com pragma/SPDX
7
+ const trimmed = llmOutput.trim();
8
+ if (trimmed.startsWith("// SPDX") || trimmed.startsWith("pragma")) {
9
+ return trimmed;
10
+ }
11
+
12
+ // Caso 3: output inválido — lançar erro descritivo
13
+ throw new Error(
14
+ `LLM output não contém bloco Solidity válido. Preview: "${llmOutput.slice(0, 200)}"`
15
+ );
16
+ }
src/agents/tester/utils/logAnalyzer.ts ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { FoundryResult } from "../tools/foundryRunner.js";
2
+
3
+ export type ErrorCategory =
4
+ | "compiler_error"
5
+ | "revert_no_message"
6
+ | "revert_with_message"
7
+ | "assertion_failed"
8
+ | "timeout"
9
+ | "unknown";
10
+
11
+ export interface LogAnalysis {
12
+ category: ErrorCategory;
13
+ summary: string; // 1-2 frases em linguagem natural para o LLM
14
+ relevantLines: string[]; // máx 10 linhas do log original
15
+ }
16
+
17
+ export function analyzeFoundryLog(result: FoundryResult): LogAnalysis {
18
+ if (result.timedOut) return {
19
+ category: "timeout",
20
+ summary: "Forge excedeu 60s. O exploit pode ter entrado em loop infinito ou a lógica está bloqueante.",
21
+ relevantLines: [],
22
+ };
23
+
24
+ if (result.combined.includes("Compiler run failed")) {
25
+ const lines = result.combined.split("\n")
26
+ .filter(l => l.includes("Error") || l.includes("error") || l.includes("-->"))
27
+ .slice(0, 10);
28
+ return {
29
+ category: "compiler_error",
30
+ summary: "Erro de compilação Solidity. Verifique: interfaces faltando, assinaturas incorretas, tipos incompatíveis.",
31
+ relevantLines: lines,
32
+ };
33
+ }
34
+
35
+ if (result.combined.includes("FAIL")) {
36
+ const revertReason = result.combined.match(/revert: (.+)/)?.[1];
37
+ const assertionFail = result.combined.includes("Assertion Failed") || result.combined.includes("assertion failed");
38
+
39
+ if (assertionFail) return {
40
+ category: "assertion_failed",
41
+ summary: "O exploit executou mas a assertion final falhou — o atacante não obteve o resultado esperado.",
42
+ relevantLines: result.combined.split("\n")
43
+ .filter(l => l.includes("assertion") || l.includes("FAIL")).slice(0, 10),
44
+ };
45
+
46
+ if (revertReason) return {
47
+ category: "revert_with_message",
48
+ summary: `Transação reverteu com: "${revertReason}". O contrato rejeitou a operação.`,
49
+ relevantLines: [revertReason],
50
+ };
51
+
52
+ return {
53
+ category: "revert_no_message",
54
+ summary: "Transação reverteu sem mensagem. Verifique a ordem das chamadas, permissões e estado do contrato.",
55
+ relevantLines: result.combined.split("\n")
56
+ .filter(l => l.includes("revert") || l.includes("FAIL")).slice(0, 5),
57
+ };
58
+ }
59
+
60
+ return {
61
+ category: "unknown",
62
+ summary: "Erro desconhecido. Revisar output completo do forge.",
63
+ relevantLines: result.combined.split("\n").slice(0, 10),
64
+ };
65
+ }
src/config/llm.ts CHANGED
@@ -1,24 +1,28 @@
1
  import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
2
  import { ChatAnthropic } from "@langchain/anthropic";
 
3
  import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
4
 
5
  export type LLMProvider = "google" | "openrouter" | "anthropic";
6
 
7
  export function createLLM(overrideProvider?: LLMProvider): BaseChatModel {
8
- const provider = overrideProvider || (process.env.LLM_PROVIDER as LLMProvider) || "google";
9
 
10
  switch (provider) {
11
- case "openrouter": {
12
- const { ChatOpenRouter } = require("@langchain/openrouter");
13
  return new ChatOpenRouter({
14
- model: process.env.OPENROUTER_MODEL || "google/gemini-2.5-flash",
15
  temperature: 0.2,
16
- }) as BaseChatModel;
17
- }
 
 
 
18
  case "anthropic":
19
  return new ChatAnthropic({
20
  model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
21
  temperature: 0.2,
 
22
  });
23
  case "google":
24
  default:
@@ -26,6 +30,7 @@ export function createLLM(overrideProvider?: LLMProvider): BaseChatModel {
26
  apiKey: process.env.GOOGLE_API_KEY || "",
27
  model: process.env.MODEL_NAME || "gemini-2.5-flash",
28
  temperature: 0.2,
 
29
  });
30
  }
31
  }
 
1
  import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
2
  import { ChatAnthropic } from "@langchain/anthropic";
3
+ import { ChatOpenRouter } from "@langchain/openrouter";
4
  import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
5
 
6
  export type LLMProvider = "google" | "openrouter" | "anthropic";
7
 
8
  export function createLLM(overrideProvider?: LLMProvider): BaseChatModel {
9
+ const provider = overrideProvider || (process.env.LLM_PROVIDER as LLMProvider) || "openrouter";
10
 
11
  switch (provider) {
12
+ case "openrouter":
 
13
  return new ChatOpenRouter({
14
+ model: process.env.OPENROUTER_MODEL || "google/gemini-3.1-flash-lite",
15
  temperature: 0.2,
16
+ apiKey: process.env.OPENROUTER_API_KEY,
17
+ maxTokens: 4096,
18
+ });
19
+
20
+
21
  case "anthropic":
22
  return new ChatAnthropic({
23
  model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
24
  temperature: 0.2,
25
+ maxTokens: 4096,
26
  });
27
  case "google":
28
  default:
 
30
  apiKey: process.env.GOOGLE_API_KEY || "",
31
  model: process.env.MODEL_NAME || "gemini-2.5-flash",
32
  temperature: 0.2,
33
+ maxOutputTokens: 4096,
34
  });
35
  }
36
  }
src/index.ts CHANGED
@@ -4,10 +4,12 @@ import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
4
  import { resolve, dirname } from "node:path";
5
  import { fileURLToPath } from "node:url";
6
 
7
- import { auditorAgent } from "./agents/auditor/agent.ts";
8
- import { coderAgent } from "./agents/coder/agent.ts";
9
- import { testerAgent } from "./agents/tester/agent.ts";
10
- import { logger } from "./logger.ts";
 
 
11
 
12
  const inputPath = process.argv[2];
13
 
@@ -22,7 +24,6 @@ console.log("Requisitos carregados de:", inputPath);
22
 
23
  const coderResult = await coderAgent.invoke({ requirements: [requirementsText] });
24
  console.log("======= Coder =======");
25
- console.log(coderResult.contract);
26
 
27
  const __dirname = dirname(fileURLToPath(import.meta.url));
28
  const outputDir = resolve(__dirname, "agents/coder/outputs");
@@ -42,10 +43,16 @@ for (const f of auditorResult.findings) {
42
  logger.info(` [${f.severity.toUpperCase()}] ${f.title} — ${f.location}`);
43
  }
44
 
45
- const testerResult = await testerAgent.invoke({
46
- solidityFiles: [coderResult.contract],
47
- vulnerability: auditorResult.findings[0] ?? {},
48
- });
49
 
50
- console.log("\n======= Tester =======");
51
- console.log(testerResult.results);
 
 
 
 
 
 
 
 
4
  import { resolve, dirname } from "node:path";
5
  import { fileURLToPath } from "node:url";
6
 
7
+ import { auditorAgent } from "./agents/auditor/agent.js";
8
+ import { coderAgent } from "./agents/coder/agent.js";
9
+ import { testerAgent } from "./agents/tester/agent.js";
10
+ import { logger } from "./logger.js";
11
+ import type { VulnerabilityReport, Finding } from "./agents/tester/types.js";
12
+ import { mapFindingToReport } from "./utils/mapFinding.js";
13
 
14
  const inputPath = process.argv[2];
15
 
 
24
 
25
  const coderResult = await coderAgent.invoke({ requirements: [requirementsText] });
26
  console.log("======= Coder =======");
 
27
 
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
  const outputDir = resolve(__dirname, "agents/coder/outputs");
 
43
  logger.info(` [${f.severity.toUpperCase()}] ${f.title} — ${f.location}`);
44
  }
45
 
46
+ if (auditorResult.findings.length > 0) {
47
+ const finding = auditorResult.findings[0];
48
+ const report = mapFindingToReport(finding, coderResult.contract);
 
49
 
50
+ console.log("\n======= Tester =======");
51
+ const testerResult = await testerAgent.invoke({ report });
52
+
53
+ console.log("Status:", testerResult.status);
54
+ console.log("Iterations:", testerResult.iterations);
55
+ } else {
56
+ console.log("\n======= Tester =======");
57
+ console.log("Nenhuma vulnerabilidade encontrada pelo Auditor.");
58
+ }
src/server.ts CHANGED
@@ -12,6 +12,7 @@ import { serveStatic } from "@hono/node-server/serve-static";
12
  import { coderAgent } from "./agents/coder/agent.ts";
13
  import { auditorAgent } from "./agents/auditor/agent.ts";
14
  import { testerAgent } from "./agents/tester/agent.ts";
 
15
 
16
  const app = new Hono();
17
 
@@ -77,18 +78,27 @@ app.post("/api/run", (c) => {
77
 
78
  // === TESTER ===
79
  await send("log", "[Tester] Gerando testes de prova de conceito...");
80
- const testerResult = await testerAgent.invoke({
81
- solidityFiles: [coderResult.contract],
82
- vulnerability: auditorResult.findings[0] ?? {},
83
- });
84
- await send("log", `[Tester] ${testerResult.results.length} resultado(s) de teste.`);
85
 
86
- await send(
87
- "tester",
88
- JSON.stringify({
89
- results: testerResult.results,
90
- }),
91
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  await send("log", "Pipeline concluído.");
94
  await send("done", "ok");
 
12
  import { coderAgent } from "./agents/coder/agent.ts";
13
  import { auditorAgent } from "./agents/auditor/agent.ts";
14
  import { testerAgent } from "./agents/tester/agent.ts";
15
+ import { mapFindingToReport } from "./utils/mapFinding.js";
16
 
17
  const app = new Hono();
18
 
 
78
 
79
  // === TESTER ===
80
  await send("log", "[Tester] Gerando testes de prova de conceito...");
 
 
 
 
 
81
 
82
+ if (auditorResult.findings.length > 0) {
83
+ const report = mapFindingToReport(auditorResult.findings[0], coderResult.contract);
84
+ const testerResult = await testerAgent.invoke({ report });
85
+
86
+ await send("log", `[Tester] Execução concluída com status: ${testerResult.status}`);
87
+
88
+ // Garante que o objeto enviado tem exatamente o que o front espera
89
+ await send(
90
+ "tester",
91
+ JSON.stringify({
92
+ status: testerResult.status,
93
+ pocCode: testerResult.pocCode,
94
+ executionLogs: testerResult.executionLogs,
95
+ iterations: testerResult.iterations,
96
+ }),
97
+ );
98
+ } else {
99
+ await send("log", "[Tester] Nenhuma vulnerabilidade para testar.");
100
+ await send("tester", JSON.stringify({ status: "skipped", iterations: 0 }));
101
+ }
102
 
103
  await send("log", "Pipeline concluído.");
104
  await send("done", "ok");
src/utils/mapFinding.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Finding, VulnerabilityReport } from "../agents/tester/types.js";
2
+
3
+ /**
4
+ * Mapeia um achado (Finding) do Auditor para um relatório de vulnerabilidade (VulnerabilityReport)
5
+ * compatível com o Gerador de PoCs (Tester).
6
+ */
7
+ export function mapFindingToReport(finding: any, sourceCode: string): VulnerabilityReport {
8
+ const title = finding.title || finding.type || "Unknown vulnerability";
9
+ const description = finding.description || "No description provided by auditor.";
10
+
11
+ const nameMatch = finding.path?.match(/([^\/]+)\.sol$/);
12
+ const contractName = nameMatch ? nameMatch[1] : "TargetContract";
13
+
14
+ const exploitablePaths = finding.judgeReview?.exploitablePaths || [];
15
+
16
+ return {
17
+ id: title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50),
18
+ severity: (finding.severity === "high" || finding.severity === "medium" || finding.severity === "low")
19
+ ? finding.severity : "low",
20
+ type: finding.type || "custom",
21
+ title,
22
+ description,
23
+ affectedContract: {
24
+ name: contractName,
25
+ sourceCode,
26
+ },
27
+ attackVector: exploitablePaths[0] ?? "Unknown vector",
28
+ exploitablePaths,
29
+ codeSnippet: finding.codeSnippet,
30
+ location: finding.location
31
+ };
32
+ }
tests/centrifuge_flat.sol ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ pragma solidity 0.8.21;
3
+
4
+ interface IERC20 {
5
+ function totalSupply() external view returns (uint256);
6
+ function balanceOf(address account) external view returns (uint256);
7
+ function transfer(address recipient, uint256 amount) external returns (bool);
8
+ function allowance(address owner, address spender) external view returns (uint256);
9
+ function approve(address spender, uint256 amount) external returns (bool);
10
+ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
11
+ }
12
+
13
+ interface IERC4626 is IERC20 {
14
+ function asset() external view returns (address);
15
+ }
16
+
17
+ interface InvestmentManagerLike {
18
+ function processDeposit(address receiver, uint256 assets) external returns (uint256);
19
+ function processMint(address receiver, uint256 shares) external returns (uint256);
20
+ function maxDeposit(address user, address _tranche) external view returns (uint256);
21
+ function maxMint(address user, address _tranche) external view returns (uint256);
22
+ function requestDeposit(uint256 assets, address receiver) external;
23
+ }
24
+
25
+ contract Auth {
26
+ mapping (address => uint) public wards;
27
+ function rely(address usr) external auth { wards[usr] = 1; }
28
+ function deny(address usr) external auth { wards[usr] = 0; }
29
+ modifier auth {
30
+ require(wards[msg.sender] == 1, "not-authorized");
31
+ _;
32
+ }
33
+ }
34
+
35
+ contract LiquidityPool is Auth {
36
+ uint64 public poolId;
37
+ bytes16 public trancheId;
38
+ address public immutable asset;
39
+ address public immutable share;
40
+ InvestmentManagerLike public investmentManager;
41
+
42
+ constructor(uint64 poolId_, bytes16 trancheId_, address asset_, address share_, address investmentManager_) {
43
+ poolId = poolId_;
44
+ trancheId = trancheId_;
45
+ asset = asset_;
46
+ share = share_;
47
+ investmentManager = InvestmentManagerLike(investmentManager_);
48
+ wards[msg.sender] = 1;
49
+ }
50
+
51
+ modifier withApproval(address owner) {
52
+ require(msg.sender == owner, "LiquidityPool/no-approval");
53
+ _;
54
+ }
55
+
56
+ function deposit(uint256 assets, address receiver) public withApproval(receiver) returns (uint256 shares) {
57
+ shares = investmentManager.processDeposit(receiver, assets);
58
+ }
59
+
60
+ function mint(uint256 shares, address receiver) public withApproval(receiver) returns (uint256 assets) {
61
+ assets = investmentManager.processMint(receiver, shares);
62
+ }
63
+
64
+ function maxDeposit(address receiver) public view returns (uint256) {
65
+ return investmentManager.maxDeposit(receiver, address(this));
66
+ }
67
+
68
+ function maxMint(address receiver) external view returns (uint256 maxShares) {
69
+ return investmentManager.maxMint(receiver, address(this));
70
+ }
71
+ }
tests/e2e/poc-generator.test.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { runPoCGenerator } from "../../src/agents/tester/index.js";
4
+ import { VulnerabilityReport } from "../../src/agents/tester/types.js";
5
+
6
+ const VULNERABLE_BANK = `
7
+ pragma solidity ^0.8.20;
8
+ contract VulnerableBank {
9
+ mapping(address => uint) public balances;
10
+ function deposit() external payable { balances[msg.sender] += msg.value; }
11
+ function withdraw() external {
12
+ uint amount = balances[msg.sender];
13
+ (bool ok,) = msg.sender.call{value: amount}("");
14
+ require(ok);
15
+ balances[msg.sender] = 0; // atualiza DEPOIS — reentrancy
16
+ }
17
+ receive() external payable {}
18
+ }`.trim();
19
+
20
+ const mockReport: VulnerabilityReport = {
21
+ id: "e2e-reentrancy-001",
22
+ severity: "high",
23
+ type: "reentrancy",
24
+ title: "Reentrancy em withdraw()",
25
+ description: "withdraw() envia ETH antes de zerar o saldo, permitindo re-entrada.",
26
+ affectedContract: { name: "VulnerableBank", sourceCode: VULNERABLE_BANK },
27
+ attackVector: "Contrato atacante com fallback() que chama withdraw() novamente antes do saldo ser zerado.",
28
+ suggestedCheatcodes: ["vm.deal", "vm.startPrank", "vm.stopPrank"],
29
+ };
30
+
31
+ describe("PoC generator (e2e)", () => {
32
+ it("generates a PoC from a vulnerability report", async () => {
33
+ const result = await runPoCGenerator(mockReport);
34
+
35
+ expect(result.status).toBe("success");
36
+ expect(result.solidityCode).toContain("test_Exploit");
37
+ expect(result.executionLogs.length).toBeGreaterThan(0);
38
+ }, 120000);
39
+ });
tests/run-centrifuge-test.ts ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { testerAgent } from "../src/agents/tester/agent.js";
2
+ import { Finding, VulnerabilityReport } from "../src/agents/tester/types.js";
3
+ import { readFileSync } from "fs";
4
+
5
+ function mapFindingToReport(finding: Finding, sourceCode: string): VulnerabilityReport {
6
+ const nameMatch = finding.path.match(/([^\/]+)\.sol$/);
7
+ const contractName = nameMatch ? nameMatch[1] : "TargetContract";
8
+
9
+ return {
10
+ id: finding.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50),
11
+ severity: finding.severity === "high" ? "high" : finding.severity === "medium" ? "medium" : "low",
12
+ type: "custom",
13
+ title: finding.title,
14
+ description: finding.description,
15
+ affectedContract: {
16
+ name: contractName,
17
+ sourceCode: sourceCode,
18
+ },
19
+ attackVector: finding.judgeReview.exploitablePaths[0] || "Unknown vector",
20
+ exploitablePaths: finding.judgeReview.exploitablePaths,
21
+ codeSnippet: finding.codeSnippet,
22
+ location: finding.location
23
+ };
24
+ }
25
+
26
+ async function main() {
27
+ const input = JSON.parse(readFileSync("src/agents/tester/data/input_centrifuge.json", "utf-8"));
28
+ const sourceCode = readFileSync("tests/centrifuge_flat.sol", "utf-8");
29
+
30
+ const report = mapFindingToReport(input, sourceCode);
31
+
32
+ console.log("Iniciando execução do Agente Tester com Centrifuge Trajectory 008...");
33
+ const result = await testerAgent.invoke({ report });
34
+
35
+ console.log("\n======= Resultado =======");
36
+ console.log("Status Final:", result.status);
37
+ console.log("Iterações:", result.iterations);
38
+ if (result.lastError) console.log("Último Erro:", result.lastError);
39
+
40
+ console.log("\n======= Código Gerado =======");
41
+ console.log(result.pocCode);
42
+ }
43
+
44
+ main().catch(console.error);
tests/run-input-test.ts ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { testerAgent } from "../src/agents/tester/agent.js";
2
+ import { Finding, VulnerabilityReport } from "../src/agents/tester/types.js";
3
+ import { readFileSync } from "fs";
4
+
5
+ function mapFindingToReport(finding: Finding, sourceCode: string): VulnerabilityReport {
6
+ const nameMatch = finding.path.match(/([^\/]+)\.sol$/);
7
+ const contractName = nameMatch ? nameMatch[1] : "TargetContract";
8
+
9
+ return {
10
+ id: finding.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50),
11
+ severity: finding.severity === "high" ? "high" : finding.severity === "medium" ? "medium" : "low",
12
+ type: "custom",
13
+ title: finding.title,
14
+ description: finding.description,
15
+ affectedContract: {
16
+ name: contractName,
17
+ sourceCode: sourceCode,
18
+ },
19
+ attackVector: finding.judgeReview.exploitablePaths[0] || "Unknown vector",
20
+ exploitablePaths: finding.judgeReview.exploitablePaths,
21
+ codeSnippet: finding.codeSnippet,
22
+ location: finding.location
23
+ };
24
+ }
25
+
26
+ async function main() {
27
+ const input = JSON.parse(readFileSync("src/agents/tester/data/input.json", "utf-8"));
28
+
29
+ // O Finding do auditor já tem o 'codeSnippet', mas para o Oracle precisamos do 'sourceCode' completo.
30
+ // Como não temos o repositório do coder aqui, vamos usar o codeSnippet envolto em um contrato mínimo
31
+ // ou assumir que o codeSnippet é representativo para o teste.
32
+ // Na vida real, o index.ts passa o coderResult.contract.
33
+
34
+ // Vamos criar um sourceCode fake que contém o snippet para testar o fluxo.
35
+ const fakeSourceCode = `
36
+ pragma solidity ^0.8.20;
37
+ contract CafeToken {
38
+ mapping(address => uint256) public balances;
39
+ event RewardRedeemed(address indexed user, uint256 amount, string recompensa);
40
+ function _burn(address account, uint256 amount) internal {
41
+ balances[account] -= amount;
42
+ }
43
+ function balanceOf(address account) public view returns (uint256) {
44
+ return balances[account];
45
+ }
46
+ function mint(address account, uint256 amount) public {
47
+ balances[account] += amount;
48
+ }
49
+ ${input.codeSnippet}
50
+ }
51
+ `;
52
+
53
+ const report = mapFindingToReport(input, fakeSourceCode);
54
+
55
+ console.log("Iniciando execução do Agente Tester com input.json...");
56
+ const result = await testerAgent.invoke({ report });
57
+
58
+ console.log("\n======= Resultado =======");
59
+ console.log("Status Final:", result.status);
60
+ console.log("Iterações:", result.iterations);
61
+ if (result.lastError) console.log("Último Erro:", result.lastError);
62
+
63
+ console.log("\n======= Código Gerado =======");
64
+ console.log(result.pocCode);
65
+ }
66
+
67
+ main().catch(console.error);
tests/scaffold.test.ts ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { generateLocalScaffold } from "../src/agents/tester/tools/scaffoldGenerator.js";
4
+
5
+ describe("generateLocalScaffold", () => {
6
+ it("creates a basic exploit scaffold", () => {
7
+ const mockReport = {
8
+ id: "t1",
9
+ severity: "high" as const,
10
+ type: "reentrancy",
11
+ title: "Reentrancy in withdraw()",
12
+ description: "withdraw() sends ETH before zeroing balance",
13
+ attackVector: "Malicious callback",
14
+ affectedContract: {
15
+ name: "VulnerableBank",
16
+ sourceCode: `
17
+ pragma solidity ^0.8.20;
18
+ contract VulnerableBank {
19
+ mapping(address=>uint) public balances;
20
+ function withdraw() external {
21
+ uint a = balances[msg.sender];
22
+ (bool ok,) = msg.sender.call{value:a}("");
23
+ require(ok); balances[msg.sender] = 0;
24
+ }
25
+ }`,
26
+ },
27
+ };
28
+
29
+ const scaffold = generateLocalScaffold(mockReport);
30
+
31
+ expect(scaffold).toContain("contract ExploitTest is Test");
32
+ expect(scaffold).toContain("VulnerableBank target");
33
+ expect(scaffold).toContain("function setUp()");
34
+ expect(scaffold).toContain("function test_Exploit()");
35
+ });
36
+ });
tests/state.test.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { PoCStateAnnotation } from "../src/agents/tester/state.js";
4
+
5
+ describe("PoCStateAnnotation", () => {
6
+ it("defines the iterations field", () => {
7
+ const spec = (PoCStateAnnotation as any).spec;
8
+ expect(spec.iterations).toBeDefined();
9
+ });
10
+
11
+ it("uses additive reducer for iterations", () => {
12
+ const spec = (PoCStateAnnotation as any).spec;
13
+ const reducer = spec.iterations.reducer ?? ((x: number, y: number) => x + y);
14
+ expect(reducer(0, 1)).toBe(1);
15
+ });
16
+ });
tests/stub-run.ts ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { testerAgent } from "../src/agents/tester/agent.js";
2
+
3
+ const mockReport = {
4
+ id: "test-stub",
5
+ severity: "high" as const,
6
+ type: "reentrancy",
7
+ title: "Test",
8
+ description: "Test",
9
+ attackVector: "Test",
10
+ affectedContract: { name: "Test", sourceCode: "pragma solidity ^0.8.0;" }
11
+ };
12
+
13
+ const result = await testerAgent.invoke({ report: mockReport });
14
+
15
+ console.assert(result.status === "success", `status deve ser success, mas foi ${result.status}`);
16
+ console.assert(result.iterations === 1, `iterations deve ser 1, mas foi ${result.iterations}`);
17
+
18
+ console.log("Grafo stub OK:", result.status);
19
+ console.log("Iterations:", result.iterations);
tsconfig.json CHANGED
@@ -8,7 +8,7 @@
8
  "outDir": "./dist",
9
  "moduleResolution": "nodenext",
10
  "module": "nodenext",
11
- "target": "esnext",
12
  "types": [
13
  "node"
14
  ],
 
8
  "outDir": "./dist",
9
  "moduleResolution": "nodenext",
10
  "module": "nodenext",
11
+ "target": "ES2022",
12
  "types": [
13
  "node"
14
  ],