@console-agent/agent 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -0
- package/dist/index.cjs +858 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +850 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var google = require('@ai-sdk/google');
|
|
4
|
+
var ai = require('ai');
|
|
5
|
+
var chalk = require('chalk');
|
|
6
|
+
var ora = require('ora');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
11
|
+
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
12
|
+
|
|
13
|
+
// src/personas/debugger.ts
|
|
14
|
+
var debuggerPersona = {
|
|
15
|
+
name: "debugger",
|
|
16
|
+
icon: "\u{1F41B}",
|
|
17
|
+
label: "Debugging",
|
|
18
|
+
systemPrompt: `You are a senior debugging expert and performance engineer.
|
|
19
|
+
|
|
20
|
+
Your role:
|
|
21
|
+
- Analyze errors, stack traces, exceptions, and performance issues
|
|
22
|
+
- Identify root causes with high confidence
|
|
23
|
+
- Provide concrete fixes with code examples
|
|
24
|
+
- Suggest preventive measures
|
|
25
|
+
|
|
26
|
+
Output format:
|
|
27
|
+
- Start with a one-line summary of the issue
|
|
28
|
+
- Explain the root cause clearly
|
|
29
|
+
- Provide a concrete fix (with code if applicable)
|
|
30
|
+
- Rate severity: LOW / MEDIUM / HIGH / CRITICAL
|
|
31
|
+
- Include confidence score (0-1)
|
|
32
|
+
|
|
33
|
+
Always be concise, technical, and actionable. No fluff.`,
|
|
34
|
+
defaultTools: ["code_execution", "google_search"],
|
|
35
|
+
keywords: [
|
|
36
|
+
"slow",
|
|
37
|
+
"perf",
|
|
38
|
+
"performance",
|
|
39
|
+
"optimize",
|
|
40
|
+
"optimization",
|
|
41
|
+
"debug",
|
|
42
|
+
"error",
|
|
43
|
+
"bug",
|
|
44
|
+
"crash",
|
|
45
|
+
"exception",
|
|
46
|
+
"stack",
|
|
47
|
+
"trace",
|
|
48
|
+
"memory",
|
|
49
|
+
"leak",
|
|
50
|
+
"timeout",
|
|
51
|
+
"latency",
|
|
52
|
+
"bottleneck",
|
|
53
|
+
"hang",
|
|
54
|
+
"freeze",
|
|
55
|
+
"deadlock",
|
|
56
|
+
"race condition"
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/personas/security.ts
|
|
61
|
+
var securityPersona = {
|
|
62
|
+
name: "security",
|
|
63
|
+
icon: "\u{1F6E1}\uFE0F",
|
|
64
|
+
label: "Security audit",
|
|
65
|
+
systemPrompt: `You are an OWASP security expert and penetration testing specialist.
|
|
66
|
+
|
|
67
|
+
Your role:
|
|
68
|
+
- Audit code and inputs for vulnerabilities (SQL injection, XSS, CSRF, SSRF, etc.)
|
|
69
|
+
- Flag security risks immediately with severity ratings
|
|
70
|
+
- Check for known CVEs in dependencies
|
|
71
|
+
- Recommend secure coding practices
|
|
72
|
+
|
|
73
|
+
Output format:
|
|
74
|
+
- Start with overall risk level: SAFE / LOW RISK / MEDIUM RISK / HIGH RISK / CRITICAL
|
|
75
|
+
- List each vulnerability found with:
|
|
76
|
+
- Type (e.g., SQL Injection, XSS)
|
|
77
|
+
- Location (where in the code/input)
|
|
78
|
+
- Impact (what an attacker could do)
|
|
79
|
+
- Fix (concrete remediation)
|
|
80
|
+
- Include confidence score (0-1)
|
|
81
|
+
|
|
82
|
+
Be thorough, explicit about risks, and always err on the side of caution.`,
|
|
83
|
+
defaultTools: ["google_search"],
|
|
84
|
+
keywords: [
|
|
85
|
+
"security",
|
|
86
|
+
"vuln",
|
|
87
|
+
"vulnerability",
|
|
88
|
+
"exploit",
|
|
89
|
+
"injection",
|
|
90
|
+
"xss",
|
|
91
|
+
"csrf",
|
|
92
|
+
"ssrf",
|
|
93
|
+
"sql injection",
|
|
94
|
+
"auth",
|
|
95
|
+
"authentication",
|
|
96
|
+
"authorization",
|
|
97
|
+
"permission",
|
|
98
|
+
"privilege",
|
|
99
|
+
"escalation",
|
|
100
|
+
"sanitize",
|
|
101
|
+
"escape",
|
|
102
|
+
"encrypt",
|
|
103
|
+
"decrypt",
|
|
104
|
+
"hash",
|
|
105
|
+
"token",
|
|
106
|
+
"secret",
|
|
107
|
+
"api key",
|
|
108
|
+
"password",
|
|
109
|
+
"credential",
|
|
110
|
+
"owasp",
|
|
111
|
+
"cve"
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/personas/architect.ts
|
|
116
|
+
var architectPersona = {
|
|
117
|
+
name: "architect",
|
|
118
|
+
icon: "\u{1F3D7}\uFE0F",
|
|
119
|
+
label: "Architecture review",
|
|
120
|
+
systemPrompt: `You are a principal software engineer and system architect.
|
|
121
|
+
|
|
122
|
+
Your role:
|
|
123
|
+
- Review system design, API design, and code architecture
|
|
124
|
+
- Evaluate scalability, maintainability, and performance characteristics
|
|
125
|
+
- Identify design pattern opportunities and anti-patterns
|
|
126
|
+
- Suggest architectural improvements with trade-off analysis
|
|
127
|
+
|
|
128
|
+
Output format:
|
|
129
|
+
- Start with an overall assessment: SOLID / NEEDS IMPROVEMENT / SIGNIFICANT CONCERNS
|
|
130
|
+
- List strengths of the current design
|
|
131
|
+
- List concerns with severity and impact
|
|
132
|
+
- Provide concrete recommendations with:
|
|
133
|
+
- What to change
|
|
134
|
+
- Why (trade-offs)
|
|
135
|
+
- How (implementation guidance)
|
|
136
|
+
- Include confidence score (0-1)
|
|
137
|
+
|
|
138
|
+
Think like a senior architect reviewing a design doc. Be constructive, not pedantic.`,
|
|
139
|
+
defaultTools: ["google_search", "file_analysis"],
|
|
140
|
+
keywords: [
|
|
141
|
+
"design",
|
|
142
|
+
"architecture",
|
|
143
|
+
"architect",
|
|
144
|
+
"pattern",
|
|
145
|
+
"scalab",
|
|
146
|
+
"microservice",
|
|
147
|
+
"monolith",
|
|
148
|
+
"api design",
|
|
149
|
+
"schema",
|
|
150
|
+
"database",
|
|
151
|
+
"system design",
|
|
152
|
+
"infrastructure",
|
|
153
|
+
"deploy",
|
|
154
|
+
"ci/cd",
|
|
155
|
+
"pipeline",
|
|
156
|
+
"refactor",
|
|
157
|
+
"modular",
|
|
158
|
+
"coupling",
|
|
159
|
+
"cohesion",
|
|
160
|
+
"solid",
|
|
161
|
+
"clean architecture",
|
|
162
|
+
"domain driven",
|
|
163
|
+
"event driven"
|
|
164
|
+
]
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/personas/general.ts
|
|
168
|
+
var generalPersona = {
|
|
169
|
+
name: "general",
|
|
170
|
+
icon: "\u{1F50D}",
|
|
171
|
+
label: "Analyzing",
|
|
172
|
+
systemPrompt: `You are a helpful senior full-stack engineer with broad expertise.
|
|
173
|
+
|
|
174
|
+
Your role:
|
|
175
|
+
- Provide actionable advice on any technical topic
|
|
176
|
+
- Analyze code, data, configurations, and systems
|
|
177
|
+
- Validate inputs, schemas, and data integrity
|
|
178
|
+
- Answer questions with practical, real-world guidance
|
|
179
|
+
|
|
180
|
+
Output format:
|
|
181
|
+
- Start with a clear, one-line answer or summary
|
|
182
|
+
- Provide supporting details and reasoning
|
|
183
|
+
- Include code examples when relevant
|
|
184
|
+
- List any caveats or edge cases
|
|
185
|
+
- Include confidence score (0-1)
|
|
186
|
+
|
|
187
|
+
Be balanced, practical, and concise. Prioritize actionable insights over theory.`,
|
|
188
|
+
defaultTools: ["code_execution", "google_search", "file_analysis"],
|
|
189
|
+
keywords: []
|
|
190
|
+
// General catches everything not matched by specific personas
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/personas/index.ts
|
|
194
|
+
var personas = {
|
|
195
|
+
debugger: debuggerPersona,
|
|
196
|
+
security: securityPersona,
|
|
197
|
+
architect: architectPersona,
|
|
198
|
+
general: generalPersona
|
|
199
|
+
};
|
|
200
|
+
function detectPersona(prompt, defaultPersona) {
|
|
201
|
+
const lower = prompt.toLowerCase();
|
|
202
|
+
for (const name of ["security", "debugger", "architect"]) {
|
|
203
|
+
const persona = personas[name];
|
|
204
|
+
if (persona.keywords.some((kw) => lower.includes(kw))) {
|
|
205
|
+
return persona;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return personas[defaultPersona];
|
|
209
|
+
}
|
|
210
|
+
function getPersona(name) {
|
|
211
|
+
return personas[name];
|
|
212
|
+
}
|
|
213
|
+
var currentLogLevel = "info";
|
|
214
|
+
function setLogLevel(level) {
|
|
215
|
+
currentLogLevel = level;
|
|
216
|
+
}
|
|
217
|
+
function shouldLog(level) {
|
|
218
|
+
const levels = ["silent", "errors", "info", "debug"];
|
|
219
|
+
return levels.indexOf(currentLogLevel) >= levels.indexOf(level);
|
|
220
|
+
}
|
|
221
|
+
function startSpinner(persona, prompt) {
|
|
222
|
+
if (!shouldLog("info")) return null;
|
|
223
|
+
const truncated = prompt.length > 60 ? prompt.substring(0, 57) + "..." : prompt;
|
|
224
|
+
const spinner = ora__default.default({
|
|
225
|
+
text: chalk__default.default.cyan(`${persona.icon} ${persona.label}... `) + chalk__default.default.dim(truncated),
|
|
226
|
+
prefixText: chalk__default.default.gray("[AGENT]")
|
|
227
|
+
}).start();
|
|
228
|
+
return spinner;
|
|
229
|
+
}
|
|
230
|
+
function stopSpinner(spinner, success) {
|
|
231
|
+
if (!spinner) return;
|
|
232
|
+
if (success) {
|
|
233
|
+
spinner.succeed();
|
|
234
|
+
} else {
|
|
235
|
+
spinner.fail();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function formatResult(result, persona) {
|
|
239
|
+
if (!shouldLog("info")) return;
|
|
240
|
+
const prefix = chalk__default.default.gray("[AGENT]");
|
|
241
|
+
const confidenceColor = result.confidence >= 0.8 ? chalk__default.default.green : result.confidence >= 0.5 ? chalk__default.default.yellow : chalk__default.default.red;
|
|
242
|
+
const statusIcon = result.success ? chalk__default.default.green("\u2713") : chalk__default.default.red("\u2717");
|
|
243
|
+
console.log("");
|
|
244
|
+
console.log(`${prefix} ${persona.icon} ${chalk__default.default.bold(persona.label)} Complete`);
|
|
245
|
+
console.log(`${prefix} \u251C\u2500 ${statusIcon} ${chalk__default.default.white(result.summary)}`);
|
|
246
|
+
if (result.actions.length > 0) {
|
|
247
|
+
for (let i = 0; i < result.actions.length; i++) {
|
|
248
|
+
const connector = i < result.actions.length - 1 ? "\u251C\u2500" : "\u251C\u2500";
|
|
249
|
+
console.log(`${prefix} ${connector} ${chalk__default.default.dim("Tool:")} ${chalk__default.default.cyan(result.actions[i])}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const dataEntries = Object.entries(result.data);
|
|
253
|
+
if (dataEntries.length > 0) {
|
|
254
|
+
for (const [key, value] of dataEntries) {
|
|
255
|
+
const displayValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
256
|
+
console.log(`${prefix} \u251C\u2500 ${chalk__default.default.dim(key + ":")} ${chalk__default.default.white(displayValue)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (result.reasoning) {
|
|
260
|
+
const reasoningLines = result.reasoning.split("\n").slice(0, 3);
|
|
261
|
+
console.log(`${prefix} \u251C\u2500 ${chalk__default.default.dim("Reasoning:")}`);
|
|
262
|
+
for (const line of reasoningLines) {
|
|
263
|
+
console.log(`${prefix} \u2502 ${chalk__default.default.dim(line.trim())}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const confidence = confidenceColor(`confidence: ${result.confidence.toFixed(2)}`);
|
|
267
|
+
const latency = chalk__default.default.dim(`${result.metadata.latencyMs}ms`);
|
|
268
|
+
const tokens = chalk__default.default.dim(`${result.metadata.tokensUsed} tokens`);
|
|
269
|
+
const cached = result.metadata.cached ? chalk__default.default.green(" (cached)") : "";
|
|
270
|
+
console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens}${cached}`);
|
|
271
|
+
console.log("");
|
|
272
|
+
}
|
|
273
|
+
function formatError(error, persona) {
|
|
274
|
+
if (!shouldLog("errors")) return;
|
|
275
|
+
const prefix = chalk__default.default.gray("[AGENT]");
|
|
276
|
+
console.log("");
|
|
277
|
+
console.log(`${prefix} ${persona.icon} ${chalk__default.default.red("Error:")} ${error.message}`);
|
|
278
|
+
if (shouldLog("debug") && error.stack) {
|
|
279
|
+
console.log(`${prefix} ${chalk__default.default.dim(error.stack)}`);
|
|
280
|
+
}
|
|
281
|
+
console.log("");
|
|
282
|
+
}
|
|
283
|
+
function formatBudgetWarning(reason) {
|
|
284
|
+
if (!shouldLog("errors")) return;
|
|
285
|
+
const prefix = chalk__default.default.gray("[AGENT]");
|
|
286
|
+
console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Budget limit:")} ${reason}`);
|
|
287
|
+
}
|
|
288
|
+
function formatRateLimitWarning() {
|
|
289
|
+
if (!shouldLog("errors")) return;
|
|
290
|
+
const prefix = chalk__default.default.gray("[AGENT]");
|
|
291
|
+
console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Rate limited:")} Too many calls. Try again later.`);
|
|
292
|
+
}
|
|
293
|
+
function formatDryRun(prompt, persona, context) {
|
|
294
|
+
if (!shouldLog("info")) return;
|
|
295
|
+
const prefix = chalk__default.default.gray("[AGENT]");
|
|
296
|
+
console.log("");
|
|
297
|
+
console.log(`${prefix} ${chalk__default.default.magenta("DRY RUN")} ${persona.icon} ${persona.label}`);
|
|
298
|
+
console.log(`${prefix} \u251C\u2500 ${chalk__default.default.dim("Persona:")} ${persona.name}`);
|
|
299
|
+
console.log(`${prefix} \u251C\u2500 ${chalk__default.default.dim("Prompt:")} ${prompt}`);
|
|
300
|
+
if (context !== void 0) {
|
|
301
|
+
const contextStr = typeof context === "string" ? context : JSON.stringify(context, null, 2);
|
|
302
|
+
const lines = contextStr.split("\n").slice(0, 5);
|
|
303
|
+
console.log(`${prefix} \u251C\u2500 ${chalk__default.default.dim("Context:")}`);
|
|
304
|
+
for (const line of lines) {
|
|
305
|
+
console.log(`${prefix} \u2502 ${chalk__default.default.dim(line)}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
console.log(`${prefix} \u2514\u2500 ${chalk__default.default.dim("(No API call made)")}`);
|
|
309
|
+
console.log("");
|
|
310
|
+
}
|
|
311
|
+
function logDebug(message) {
|
|
312
|
+
if (!shouldLog("debug")) return;
|
|
313
|
+
console.log(`${chalk__default.default.gray("[AGENT DEBUG]")} ${chalk__default.default.dim(message)}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/providers/google.ts
|
|
317
|
+
var agentOutputSchema = ai.jsonSchema({
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
success: { type: "boolean", description: "Whether the task was completed successfully" },
|
|
321
|
+
summary: { type: "string", description: "One-line human-readable conclusion" },
|
|
322
|
+
reasoning: { type: "string", description: "Your thought process" },
|
|
323
|
+
data: {
|
|
324
|
+
type: "object",
|
|
325
|
+
description: "Structured findings as key-value pairs",
|
|
326
|
+
properties: {
|
|
327
|
+
result: { type: "string", description: "Primary result or finding" }
|
|
328
|
+
},
|
|
329
|
+
additionalProperties: true
|
|
330
|
+
},
|
|
331
|
+
actions: { type: "array", items: { type: "string" }, description: "List of tools/steps you used" },
|
|
332
|
+
confidence: { type: "number", minimum: 0, maximum: 1, description: "0-1 confidence score" }
|
|
333
|
+
},
|
|
334
|
+
required: ["success", "summary", "data", "actions", "confidence"],
|
|
335
|
+
additionalProperties: false
|
|
336
|
+
});
|
|
337
|
+
async function callGoogle(prompt, context, persona, config2, options) {
|
|
338
|
+
const startTime = Date.now();
|
|
339
|
+
const modelName = options?.model ?? config2.model;
|
|
340
|
+
logDebug(`Using model: ${modelName}`);
|
|
341
|
+
logDebug(`Persona: ${persona.name}`);
|
|
342
|
+
const google$1 = google.createGoogleGenerativeAI({
|
|
343
|
+
apiKey: config2.apiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY
|
|
344
|
+
});
|
|
345
|
+
const providerOptions = {};
|
|
346
|
+
const googleOpts = {};
|
|
347
|
+
if (options?.thinking) {
|
|
348
|
+
const thinking = options.thinking;
|
|
349
|
+
if (thinking.budget !== void 0) {
|
|
350
|
+
googleOpts["thinkingConfig"] = { thinkingBudget: thinking.budget };
|
|
351
|
+
} else if (thinking.level) {
|
|
352
|
+
googleOpts["thinkingConfig"] = { thinkingLevel: thinking.level };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (Object.keys(googleOpts).length > 0) {
|
|
356
|
+
providerOptions["google"] = googleOpts;
|
|
357
|
+
}
|
|
358
|
+
if (!config2.localOnly) {
|
|
359
|
+
const toolNames = options?.tools ?? persona.defaultTools;
|
|
360
|
+
logDebug(`Persona tools (informational): ${toolNames.join(", ")}`);
|
|
361
|
+
}
|
|
362
|
+
const userMessage = context ? `${prompt}
|
|
363
|
+
|
|
364
|
+
--- Context ---
|
|
365
|
+
${context}` : prompt;
|
|
366
|
+
const collectedToolCalls = [];
|
|
367
|
+
const useCustomSchema = !!(options?.schema || options?.responseFormat);
|
|
368
|
+
let outputConfig;
|
|
369
|
+
if (options?.schema) {
|
|
370
|
+
if (options.responseFormat) {
|
|
371
|
+
logDebug("Both schema (Zod) and responseFormat provided \u2014 using schema (Zod)");
|
|
372
|
+
}
|
|
373
|
+
logDebug("Using custom Zod schema for structured output");
|
|
374
|
+
outputConfig = ai.Output.object({ schema: options.schema });
|
|
375
|
+
} else if (options?.responseFormat) {
|
|
376
|
+
logDebug("Using custom JSON Schema (responseFormat) for structured output");
|
|
377
|
+
outputConfig = ai.Output.object({ schema: ai.jsonSchema(options.responseFormat.schema) });
|
|
378
|
+
} else {
|
|
379
|
+
outputConfig = ai.Output.object({ schema: agentOutputSchema });
|
|
380
|
+
}
|
|
381
|
+
const agent = new ai.ToolLoopAgent({
|
|
382
|
+
model: google$1(modelName),
|
|
383
|
+
instructions: useCustomSchema ? `${persona.systemPrompt}
|
|
384
|
+
|
|
385
|
+
IMPORTANT: You must respond with structured data matching the requested output schema. Do not include AgentResult wrapper fields \u2014 just return the data matching the schema.` : persona.systemPrompt,
|
|
386
|
+
maxOutputTokens: config2.budget.maxTokensPerCall,
|
|
387
|
+
output: outputConfig,
|
|
388
|
+
providerOptions: Object.keys(providerOptions).length > 0 ? providerOptions : void 0,
|
|
389
|
+
onStepFinish: (step) => {
|
|
390
|
+
if (step.toolCalls) {
|
|
391
|
+
for (const tc of step.toolCalls) {
|
|
392
|
+
collectedToolCalls.push({
|
|
393
|
+
name: tc.toolName,
|
|
394
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
395
|
+
args: tc.args ?? {},
|
|
396
|
+
result: tc.toolName
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
logDebug(`Step finished: ${step.finishReason}`);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
const result = await agent.generate({
|
|
404
|
+
prompt: userMessage,
|
|
405
|
+
timeout: config2.timeout
|
|
406
|
+
});
|
|
407
|
+
const latencyMs = Date.now() - startTime;
|
|
408
|
+
const tokensUsed = result.usage?.totalTokens ?? 0;
|
|
409
|
+
logDebug(`Response received: ${latencyMs}ms, ${tokensUsed} tokens`);
|
|
410
|
+
if (useCustomSchema && result.output) {
|
|
411
|
+
const customData = result.output;
|
|
412
|
+
logDebug("Custom schema output received, wrapping in AgentResult");
|
|
413
|
+
return {
|
|
414
|
+
success: true,
|
|
415
|
+
summary: `Structured output returned (${Object.keys(customData).length} fields)`,
|
|
416
|
+
data: customData,
|
|
417
|
+
actions: collectedToolCalls.map((tc) => tc.name),
|
|
418
|
+
confidence: 1,
|
|
419
|
+
metadata: {
|
|
420
|
+
model: modelName,
|
|
421
|
+
tokensUsed,
|
|
422
|
+
latencyMs,
|
|
423
|
+
toolCalls: collectedToolCalls,
|
|
424
|
+
cached: false
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (result.output) {
|
|
429
|
+
const output = result.output;
|
|
430
|
+
return {
|
|
431
|
+
success: output.success,
|
|
432
|
+
summary: output.summary,
|
|
433
|
+
reasoning: output.reasoning,
|
|
434
|
+
data: output.data,
|
|
435
|
+
actions: output.actions,
|
|
436
|
+
confidence: output.confidence,
|
|
437
|
+
metadata: {
|
|
438
|
+
model: modelName,
|
|
439
|
+
tokensUsed,
|
|
440
|
+
latencyMs,
|
|
441
|
+
toolCalls: collectedToolCalls,
|
|
442
|
+
cached: false
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const parsed = parseResponse(result.text);
|
|
447
|
+
return {
|
|
448
|
+
success: parsed?.success ?? true,
|
|
449
|
+
summary: parsed?.summary ?? result.text.substring(0, 200),
|
|
450
|
+
reasoning: parsed?.reasoning,
|
|
451
|
+
data: parsed?.data ?? { raw: result.text },
|
|
452
|
+
actions: parsed?.actions ?? collectedToolCalls.map((tc) => tc.name),
|
|
453
|
+
confidence: parsed?.confidence ?? 0.5,
|
|
454
|
+
metadata: {
|
|
455
|
+
model: modelName,
|
|
456
|
+
tokensUsed,
|
|
457
|
+
latencyMs,
|
|
458
|
+
toolCalls: collectedToolCalls,
|
|
459
|
+
cached: false
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function parseResponse(text) {
|
|
464
|
+
try {
|
|
465
|
+
return JSON.parse(text);
|
|
466
|
+
} catch {
|
|
467
|
+
const jsonMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
468
|
+
if (jsonMatch) {
|
|
469
|
+
try {
|
|
470
|
+
return JSON.parse(jsonMatch[1]);
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
475
|
+
if (objectMatch) {
|
|
476
|
+
try {
|
|
477
|
+
return JSON.parse(objectMatch[0]);
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
success: true,
|
|
483
|
+
summary: text.substring(0, 200),
|
|
484
|
+
data: { raw: text },
|
|
485
|
+
actions: [],
|
|
486
|
+
confidence: 0.5
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/utils/anonymize.ts
|
|
492
|
+
var patterns = {
|
|
493
|
+
// API keys and tokens (long alphanumeric strings near sensitive keywords)
|
|
494
|
+
apiKey: /(?:api[_-]?key|token|secret|password|credential|auth)['":\s=]+['"]?([A-Za-z0-9_\-/.]{20,})['"]?/gi,
|
|
495
|
+
// Bearer tokens
|
|
496
|
+
bearer: /Bearer\s+[A-Za-z0-9_\-/.+]{20,}/gi,
|
|
497
|
+
// Email addresses
|
|
498
|
+
email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
499
|
+
// IPv4 addresses
|
|
500
|
+
ipv4: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
501
|
+
// IPv6 addresses (simplified)
|
|
502
|
+
ipv6: /(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}/g,
|
|
503
|
+
// AWS keys
|
|
504
|
+
awsKey: /(?:AKIA|ASIA)[A-Z0-9]{16}/g,
|
|
505
|
+
// Private keys
|
|
506
|
+
privateKey: /-----BEGIN (?:RSA )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA )?PRIVATE KEY-----/g,
|
|
507
|
+
// Connection strings
|
|
508
|
+
connectionString: /(?:mongodb|postgres|mysql|redis|amqp):\/\/[^\s'"]+/gi,
|
|
509
|
+
// .env style secrets
|
|
510
|
+
envSecret: /^(?:DATABASE_URL|DB_PASSWORD|SECRET_KEY|PRIVATE_KEY|AWS_SECRET|STRIPE_KEY|SENDGRID_KEY)[=:].+$/gm
|
|
511
|
+
};
|
|
512
|
+
function anonymize(content) {
|
|
513
|
+
let result = content;
|
|
514
|
+
result = result.replace(patterns.privateKey, "[REDACTED_PRIVATE_KEY]");
|
|
515
|
+
result = result.replace(patterns.connectionString, "[REDACTED_CONNECTION_STRING]");
|
|
516
|
+
result = result.replace(patterns.awsKey, "[REDACTED_AWS_KEY]");
|
|
517
|
+
result = result.replace(patterns.bearer, "Bearer [REDACTED_TOKEN]");
|
|
518
|
+
result = result.replace(patterns.apiKey, (match, _key) => {
|
|
519
|
+
const colonIdx = match.search(/['":\s=]/);
|
|
520
|
+
const prefix = match.substring(0, colonIdx);
|
|
521
|
+
return `${prefix}: [REDACTED]`;
|
|
522
|
+
});
|
|
523
|
+
result = result.replace(patterns.envSecret, (match) => {
|
|
524
|
+
const eqIdx = match.search(/[=:]/);
|
|
525
|
+
const key = match.substring(0, eqIdx);
|
|
526
|
+
return `${key}=[REDACTED]`;
|
|
527
|
+
});
|
|
528
|
+
result = result.replace(patterns.email, "[EMAIL]");
|
|
529
|
+
result = result.replace(patterns.ipv4, "[IP]");
|
|
530
|
+
result = result.replace(patterns.ipv6, "[IP]");
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
function anonymizeValue(value) {
|
|
534
|
+
if (typeof value === "string") {
|
|
535
|
+
return anonymize(value);
|
|
536
|
+
}
|
|
537
|
+
if (Array.isArray(value)) {
|
|
538
|
+
return value.map(anonymizeValue);
|
|
539
|
+
}
|
|
540
|
+
if (value !== null && typeof value === "object") {
|
|
541
|
+
const result = {};
|
|
542
|
+
for (const [k, v] of Object.entries(value)) {
|
|
543
|
+
result[k] = anonymizeValue(v);
|
|
544
|
+
}
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/utils/rate-limit.ts
|
|
551
|
+
var RateLimiter = class {
|
|
552
|
+
tokens;
|
|
553
|
+
maxTokens;
|
|
554
|
+
refillRate;
|
|
555
|
+
// tokens per millisecond
|
|
556
|
+
lastRefill;
|
|
557
|
+
/**
|
|
558
|
+
* @param maxCallsPerDay Maximum calls allowed per day
|
|
559
|
+
*/
|
|
560
|
+
constructor(maxCallsPerDay) {
|
|
561
|
+
this.maxTokens = maxCallsPerDay;
|
|
562
|
+
this.tokens = maxCallsPerDay;
|
|
563
|
+
this.refillRate = maxCallsPerDay / (24 * 60 * 60 * 1e3);
|
|
564
|
+
this.lastRefill = Date.now();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Attempt to consume one token.
|
|
568
|
+
* @returns true if allowed, false if rate limited
|
|
569
|
+
*/
|
|
570
|
+
tryConsume() {
|
|
571
|
+
this.refill();
|
|
572
|
+
if (this.tokens >= 1) {
|
|
573
|
+
this.tokens -= 1;
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get remaining tokens (calls available)
|
|
580
|
+
*/
|
|
581
|
+
remaining() {
|
|
582
|
+
this.refill();
|
|
583
|
+
return Math.floor(this.tokens);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Reset the limiter (e.g., for testing or manual override)
|
|
587
|
+
*/
|
|
588
|
+
reset() {
|
|
589
|
+
this.tokens = this.maxTokens;
|
|
590
|
+
this.lastRefill = Date.now();
|
|
591
|
+
}
|
|
592
|
+
refill() {
|
|
593
|
+
const now = Date.now();
|
|
594
|
+
const elapsed = now - this.lastRefill;
|
|
595
|
+
const newTokens = elapsed * this.refillRate;
|
|
596
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
597
|
+
this.lastRefill = now;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/utils/budget.ts
|
|
602
|
+
var BudgetTracker = class {
|
|
603
|
+
callsToday = 0;
|
|
604
|
+
tokensToday = 0;
|
|
605
|
+
costToday = 0;
|
|
606
|
+
dayStart;
|
|
607
|
+
config;
|
|
608
|
+
constructor(config2) {
|
|
609
|
+
this.config = config2;
|
|
610
|
+
this.dayStart = this.getStartOfDay();
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Check if a call is within budget. Resets counters at midnight UTC.
|
|
614
|
+
*/
|
|
615
|
+
canMakeCall() {
|
|
616
|
+
this.maybeResetDay();
|
|
617
|
+
if (this.callsToday >= this.config.maxCallsPerDay) {
|
|
618
|
+
return {
|
|
619
|
+
allowed: false,
|
|
620
|
+
reason: `Daily call limit reached (${this.config.maxCallsPerDay} calls/day)`
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
if (this.costToday >= this.config.costCapDaily) {
|
|
624
|
+
return {
|
|
625
|
+
allowed: false,
|
|
626
|
+
reason: `Daily cost cap reached ($${this.config.costCapDaily.toFixed(2)})`
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return { allowed: true };
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Record a completed call's usage.
|
|
633
|
+
*/
|
|
634
|
+
recordUsage(tokensUsed, costUsd) {
|
|
635
|
+
this.maybeResetDay();
|
|
636
|
+
this.callsToday += 1;
|
|
637
|
+
this.tokensToday += tokensUsed;
|
|
638
|
+
this.costToday += costUsd;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Get current usage stats.
|
|
642
|
+
*/
|
|
643
|
+
getStats() {
|
|
644
|
+
this.maybeResetDay();
|
|
645
|
+
return {
|
|
646
|
+
callsToday: this.callsToday,
|
|
647
|
+
callsRemaining: Math.max(0, this.config.maxCallsPerDay - this.callsToday),
|
|
648
|
+
tokensToday: this.tokensToday,
|
|
649
|
+
costToday: this.costToday,
|
|
650
|
+
costRemaining: Math.max(0, this.config.costCapDaily - this.costToday)
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Reset all counters (for testing).
|
|
655
|
+
*/
|
|
656
|
+
reset() {
|
|
657
|
+
this.callsToday = 0;
|
|
658
|
+
this.tokensToday = 0;
|
|
659
|
+
this.costToday = 0;
|
|
660
|
+
this.dayStart = this.getStartOfDay();
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Get the max tokens allowed per call.
|
|
664
|
+
*/
|
|
665
|
+
get maxTokensPerCall() {
|
|
666
|
+
return this.config.maxTokensPerCall;
|
|
667
|
+
}
|
|
668
|
+
maybeResetDay() {
|
|
669
|
+
const currentDayStart = this.getStartOfDay();
|
|
670
|
+
if (currentDayStart > this.dayStart) {
|
|
671
|
+
this.callsToday = 0;
|
|
672
|
+
this.tokensToday = 0;
|
|
673
|
+
this.costToday = 0;
|
|
674
|
+
this.dayStart = currentDayStart;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
getStartOfDay() {
|
|
678
|
+
const now = /* @__PURE__ */ new Date();
|
|
679
|
+
return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// src/agent.ts
|
|
684
|
+
var DEFAULT_CONFIG = {
|
|
685
|
+
provider: "google",
|
|
686
|
+
model: "gemini-2.5-flash-lite",
|
|
687
|
+
persona: "general",
|
|
688
|
+
budget: {
|
|
689
|
+
maxCallsPerDay: 100,
|
|
690
|
+
maxTokensPerCall: 8e3,
|
|
691
|
+
costCapDaily: 1
|
|
692
|
+
},
|
|
693
|
+
mode: "fire-and-forget",
|
|
694
|
+
timeout: 1e4,
|
|
695
|
+
anonymize: true,
|
|
696
|
+
localOnly: false,
|
|
697
|
+
dryRun: false,
|
|
698
|
+
logLevel: "info",
|
|
699
|
+
safetySettings: []
|
|
700
|
+
};
|
|
701
|
+
var config = { ...DEFAULT_CONFIG };
|
|
702
|
+
var rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
|
|
703
|
+
var budgetTracker = new BudgetTracker(config.budget);
|
|
704
|
+
function updateConfig(newConfig) {
|
|
705
|
+
config = { ...DEFAULT_CONFIG, ...newConfig };
|
|
706
|
+
if (newConfig.budget) {
|
|
707
|
+
config.budget = { ...DEFAULT_CONFIG.budget, ...newConfig.budget };
|
|
708
|
+
}
|
|
709
|
+
rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
|
|
710
|
+
budgetTracker = new BudgetTracker(config.budget);
|
|
711
|
+
}
|
|
712
|
+
function getConfig() {
|
|
713
|
+
return { ...config };
|
|
714
|
+
}
|
|
715
|
+
async function executeAgent(prompt, context, options) {
|
|
716
|
+
const personaName = options?.persona ?? config.persona;
|
|
717
|
+
const persona = options?.persona ? getPersona(options.persona) : detectPersona(prompt, personaName);
|
|
718
|
+
logDebug(`Selected persona: ${persona.name} (${persona.icon})`);
|
|
719
|
+
if (config.dryRun) {
|
|
720
|
+
formatDryRun(prompt, persona, context);
|
|
721
|
+
return createDryRunResult(persona.name);
|
|
722
|
+
}
|
|
723
|
+
if (!rateLimiter.tryConsume()) {
|
|
724
|
+
formatRateLimitWarning();
|
|
725
|
+
return createErrorResult("Rate limited \u2014 too many calls. Try again later.");
|
|
726
|
+
}
|
|
727
|
+
const budgetCheck = budgetTracker.canMakeCall();
|
|
728
|
+
if (!budgetCheck.allowed) {
|
|
729
|
+
formatBudgetWarning(budgetCheck.reason);
|
|
730
|
+
return createErrorResult(budgetCheck.reason);
|
|
731
|
+
}
|
|
732
|
+
let contextStr = "";
|
|
733
|
+
if (context !== void 0) {
|
|
734
|
+
const processed = config.anonymize ? anonymizeValue(context) : context;
|
|
735
|
+
contextStr = typeof processed === "string" ? processed : JSON.stringify(processed, null, 2);
|
|
736
|
+
if (context instanceof Error) {
|
|
737
|
+
const errObj = {
|
|
738
|
+
name: context.name,
|
|
739
|
+
message: context.message,
|
|
740
|
+
stack: context.stack,
|
|
741
|
+
...typeof context === "object" ? Object.getOwnPropertyNames(context).reduce((acc, key) => {
|
|
742
|
+
acc[key] = context[key];
|
|
743
|
+
return acc;
|
|
744
|
+
}, {}) : {}
|
|
745
|
+
};
|
|
746
|
+
const processed2 = config.anonymize ? anonymizeValue(errObj) : errObj;
|
|
747
|
+
contextStr = typeof processed2 === "string" ? processed2 : JSON.stringify(processed2, null, 2);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const processedPrompt = config.anonymize ? anonymizeValue(prompt) : prompt;
|
|
751
|
+
const spinner = startSpinner(persona, processedPrompt);
|
|
752
|
+
try {
|
|
753
|
+
const result = await Promise.race([
|
|
754
|
+
callGoogle(processedPrompt, contextStr, persona, config, options),
|
|
755
|
+
createTimeout(config.timeout)
|
|
756
|
+
]);
|
|
757
|
+
budgetTracker.recordUsage(
|
|
758
|
+
result.metadata.tokensUsed,
|
|
759
|
+
estimateCost(result.metadata.tokensUsed, result.metadata.model)
|
|
760
|
+
);
|
|
761
|
+
stopSpinner(spinner, result.success);
|
|
762
|
+
formatResult(result, persona);
|
|
763
|
+
return result;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
stopSpinner(spinner, false);
|
|
766
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
767
|
+
formatError(err, persona);
|
|
768
|
+
return createErrorResult(err.message);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function createTimeout(ms) {
|
|
772
|
+
return new Promise((_, reject) => {
|
|
773
|
+
setTimeout(() => reject(new Error(`Agent timed out after ${ms}ms`)), ms);
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
function createErrorResult(message) {
|
|
777
|
+
return {
|
|
778
|
+
success: false,
|
|
779
|
+
summary: message,
|
|
780
|
+
data: {},
|
|
781
|
+
actions: [],
|
|
782
|
+
confidence: 0,
|
|
783
|
+
metadata: {
|
|
784
|
+
model: config.model,
|
|
785
|
+
tokensUsed: 0,
|
|
786
|
+
latencyMs: 0,
|
|
787
|
+
toolCalls: [],
|
|
788
|
+
cached: false
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function createDryRunResult(personaName) {
|
|
793
|
+
return {
|
|
794
|
+
success: true,
|
|
795
|
+
summary: `[DRY RUN] Would have executed with ${personaName} persona`,
|
|
796
|
+
data: { dryRun: true },
|
|
797
|
+
actions: [],
|
|
798
|
+
confidence: 1,
|
|
799
|
+
metadata: {
|
|
800
|
+
model: config.model,
|
|
801
|
+
tokensUsed: 0,
|
|
802
|
+
latencyMs: 0,
|
|
803
|
+
toolCalls: [],
|
|
804
|
+
cached: false
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
function estimateCost(tokens, model) {
|
|
809
|
+
const costPer1M = {
|
|
810
|
+
"gemini-2.5-flash-lite": 0.01,
|
|
811
|
+
"gemini-3-flash-preview": 0.03
|
|
812
|
+
};
|
|
813
|
+
const rate = costPer1M[model] ?? 0.01;
|
|
814
|
+
return tokens / 1e6 * rate;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/index.ts
|
|
818
|
+
function init(config2 = {}) {
|
|
819
|
+
updateConfig(config2);
|
|
820
|
+
const fullConfig = getConfig();
|
|
821
|
+
setLogLevel(fullConfig.logLevel);
|
|
822
|
+
attachConsoleAgent();
|
|
823
|
+
}
|
|
824
|
+
function createAgentProxy() {
|
|
825
|
+
const agentFn = (prompt, context, options) => {
|
|
826
|
+
const config2 = getConfig();
|
|
827
|
+
if (config2.mode === "fire-and-forget" && !options?.mode) {
|
|
828
|
+
const promise = executeAgent(prompt, context, options);
|
|
829
|
+
promise.catch(() => {
|
|
830
|
+
});
|
|
831
|
+
return promise;
|
|
832
|
+
}
|
|
833
|
+
return executeAgent(prompt, context, options);
|
|
834
|
+
};
|
|
835
|
+
agentFn.security = (prompt, context, options) => {
|
|
836
|
+
return executeAgent(prompt, context, { ...options, persona: "security" });
|
|
837
|
+
};
|
|
838
|
+
agentFn.debug = (prompt, context, options) => {
|
|
839
|
+
return executeAgent(prompt, context, { ...options, persona: "debugger" });
|
|
840
|
+
};
|
|
841
|
+
agentFn.architect = (prompt, context, options) => {
|
|
842
|
+
return executeAgent(prompt, context, { ...options, persona: "architect" });
|
|
843
|
+
};
|
|
844
|
+
return agentFn;
|
|
845
|
+
}
|
|
846
|
+
var attached = false;
|
|
847
|
+
function attachConsoleAgent() {
|
|
848
|
+
if (attached) return;
|
|
849
|
+
const agentProxy = createAgentProxy();
|
|
850
|
+
console.agent = agentProxy;
|
|
851
|
+
attached = true;
|
|
852
|
+
}
|
|
853
|
+
attachConsoleAgent();
|
|
854
|
+
|
|
855
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
856
|
+
exports.init = init;
|
|
857
|
+
//# sourceMappingURL=index.cjs.map
|
|
858
|
+
//# sourceMappingURL=index.cjs.map
|