0agent 1.0.76 → 1.0.78
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/dist/daemon.mjs +426 -34
- package/package.json +1 -1
package/dist/daemon.mjs
CHANGED
|
@@ -1978,6 +1978,13 @@ var init_LLMExecutor = __esm({
|
|
|
1978
1978
|
constructor(config) {
|
|
1979
1979
|
this.config = config;
|
|
1980
1980
|
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Create a new LLMExecutor with a different model but same provider/key.
|
|
1983
|
+
* Used by SmartModelRouter to switch to a fast model for simple messages.
|
|
1984
|
+
*/
|
|
1985
|
+
withModel(model) {
|
|
1986
|
+
return new _LLMExecutor({ ...this.config, model });
|
|
1987
|
+
}
|
|
1981
1988
|
get isConfigured() {
|
|
1982
1989
|
if (this.config.provider === "ollama") return true;
|
|
1983
1990
|
return !!this.config.api_key?.trim();
|
|
@@ -5043,6 +5050,88 @@ ${lines.join("\n")}`,
|
|
|
5043
5050
|
}
|
|
5044
5051
|
});
|
|
5045
5052
|
|
|
5053
|
+
// packages/daemon/src/capabilities/SessionSearchCapability.ts
|
|
5054
|
+
var SessionSearchCapability;
|
|
5055
|
+
var init_SessionSearchCapability = __esm({
|
|
5056
|
+
"packages/daemon/src/capabilities/SessionSearchCapability.ts"() {
|
|
5057
|
+
"use strict";
|
|
5058
|
+
SessionSearchCapability = class {
|
|
5059
|
+
name = "session_search";
|
|
5060
|
+
description = "Search across past conversation history to recall previous interactions and decisions.";
|
|
5061
|
+
toolDefinition = {
|
|
5062
|
+
name: "session_search",
|
|
5063
|
+
description: "Search across all past conversations for relevant context. Use this when the user references something from a previous session, or when you need to recall past decisions, outcomes, or context. Returns matching conversation excerpts with timestamps.",
|
|
5064
|
+
input_schema: {
|
|
5065
|
+
type: "object",
|
|
5066
|
+
properties: {
|
|
5067
|
+
query: { type: "string", description: "Search query \u2014 keywords, phrases, or natural language question" },
|
|
5068
|
+
max_results: { type: "string", description: "Maximum results to return (default 5)" }
|
|
5069
|
+
},
|
|
5070
|
+
required: ["query"]
|
|
5071
|
+
}
|
|
5072
|
+
};
|
|
5073
|
+
getDbPath;
|
|
5074
|
+
constructor(getDbPath) {
|
|
5075
|
+
this.getDbPath = getDbPath;
|
|
5076
|
+
}
|
|
5077
|
+
async execute(input) {
|
|
5078
|
+
const start = Date.now();
|
|
5079
|
+
const query = String(input.query ?? "").trim();
|
|
5080
|
+
const maxResults = Number(input.max_results ?? 5);
|
|
5081
|
+
if (!query) return { success: false, output: "query is required", duration_ms: 0 };
|
|
5082
|
+
try {
|
|
5083
|
+
const Database2 = (await import("better-sqlite3")).default;
|
|
5084
|
+
const db = new Database2(this.getDbPath());
|
|
5085
|
+
const keywords = query.split(/\s+/).filter((w) => w.length > 2);
|
|
5086
|
+
const likeClause = keywords.map(() => `content LIKE ?`).join(" OR ");
|
|
5087
|
+
const likeParams = keywords.map((k) => `%${k}%`);
|
|
5088
|
+
const rows = db.prepare(`
|
|
5089
|
+
SELECT session_id, role, content, created_at
|
|
5090
|
+
FROM conversations
|
|
5091
|
+
WHERE ${likeClause || "1=1"}
|
|
5092
|
+
ORDER BY created_at DESC
|
|
5093
|
+
LIMIT ?
|
|
5094
|
+
`).all(...likeParams, maxResults * 3);
|
|
5095
|
+
db.close();
|
|
5096
|
+
if (!rows.length) {
|
|
5097
|
+
return {
|
|
5098
|
+
success: true,
|
|
5099
|
+
output: `No past conversations found matching "${query}".`,
|
|
5100
|
+
duration_ms: Date.now() - start
|
|
5101
|
+
};
|
|
5102
|
+
}
|
|
5103
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
5104
|
+
for (const row of rows) {
|
|
5105
|
+
if (!sessions.has(row.session_id)) sessions.set(row.session_id, []);
|
|
5106
|
+
sessions.get(row.session_id).push(row);
|
|
5107
|
+
}
|
|
5108
|
+
const results = [];
|
|
5109
|
+
let count = 0;
|
|
5110
|
+
for (const [sessionId, msgs] of sessions) {
|
|
5111
|
+
if (count >= maxResults) break;
|
|
5112
|
+
const date = new Date(msgs[0].created_at).toISOString().split("T")[0];
|
|
5113
|
+
const excerpt = msgs.slice(0, 4).map((m) => ` ${m.role}: ${m.content.slice(0, 200)}${m.content.length > 200 ? "\u2026" : ""}`).join("\n");
|
|
5114
|
+
results.push(`[${date}] Session ${sessionId.slice(0, 8)}:
|
|
5115
|
+
${excerpt}`);
|
|
5116
|
+
count++;
|
|
5117
|
+
}
|
|
5118
|
+
return {
|
|
5119
|
+
success: true,
|
|
5120
|
+
output: `Found ${sessions.size} matching session(s):
|
|
5121
|
+
|
|
5122
|
+
${results.join("\n\n")}`,
|
|
5123
|
+
structured: { sessions: sessions.size, total_messages: rows.length },
|
|
5124
|
+
duration_ms: Date.now() - start
|
|
5125
|
+
};
|
|
5126
|
+
} catch (err) {
|
|
5127
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5128
|
+
return { success: false, output: `Session search failed: ${msg}`, duration_ms: Date.now() - start, error: msg };
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
};
|
|
5132
|
+
}
|
|
5133
|
+
});
|
|
5134
|
+
|
|
5046
5135
|
// packages/daemon/src/capabilities/CodespaceBrowserCapability.ts
|
|
5047
5136
|
var CodespaceBrowserCapability_exports = {};
|
|
5048
5137
|
__export(CodespaceBrowserCapability_exports, {
|
|
@@ -5135,6 +5224,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
5135
5224
|
init_OCRExtractCapability();
|
|
5136
5225
|
init_CredentialVaultCapability();
|
|
5137
5226
|
init_MonitorWatchCapability();
|
|
5227
|
+
init_SessionSearchCapability();
|
|
5138
5228
|
CapabilityRegistry = class {
|
|
5139
5229
|
capabilities = /* @__PURE__ */ new Map();
|
|
5140
5230
|
/**
|
|
@@ -5147,7 +5237,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
5147
5237
|
* task_type: browser_task). The main agent does NOT have direct access
|
|
5148
5238
|
* to browser_open without going through a subagent spawn.
|
|
5149
5239
|
*/
|
|
5150
|
-
constructor(codespaceManager, graph, onMemoryWrite) {
|
|
5240
|
+
constructor(codespaceManager, graph, onMemoryWrite, dbPath) {
|
|
5151
5241
|
this.register(new WebSearchCapability());
|
|
5152
5242
|
if (codespaceManager) {
|
|
5153
5243
|
try {
|
|
@@ -5169,6 +5259,9 @@ var init_CapabilityRegistry = __esm({
|
|
|
5169
5259
|
this.register(new OCRExtractCapability());
|
|
5170
5260
|
this.register(new CredentialVaultCapability());
|
|
5171
5261
|
this.register(new MonitorWatchCapability());
|
|
5262
|
+
if (dbPath) {
|
|
5263
|
+
this.register(new SessionSearchCapability(() => dbPath));
|
|
5264
|
+
}
|
|
5172
5265
|
if (graph) {
|
|
5173
5266
|
this.register(new MemoryCapability(graph, onMemoryWrite));
|
|
5174
5267
|
}
|
|
@@ -5199,6 +5292,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
5199
5292
|
const lower = task.toLowerCase();
|
|
5200
5293
|
const active = /* @__PURE__ */ new Set(["shell_exec", "file_op", "surge_publish"]);
|
|
5201
5294
|
if (this.capabilities.has("memory_write")) active.add("memory_write");
|
|
5295
|
+
if (this.capabilities.has("session_search")) active.add("session_search");
|
|
5202
5296
|
if (/search|web|browse|scrape|research|website|url|http|google|fetch|crawl|find.*online/i.test(lower)) {
|
|
5203
5297
|
active.add("web_search");
|
|
5204
5298
|
active.add("scrape_url");
|
|
@@ -5254,6 +5348,150 @@ var init_capabilities = __esm({
|
|
|
5254
5348
|
init_OCRExtractCapability();
|
|
5255
5349
|
init_CredentialVaultCapability();
|
|
5256
5350
|
init_MonitorWatchCapability();
|
|
5351
|
+
init_SessionSearchCapability();
|
|
5352
|
+
}
|
|
5353
|
+
});
|
|
5354
|
+
|
|
5355
|
+
// packages/daemon/src/IterationBudget.ts
|
|
5356
|
+
var IterationBudget;
|
|
5357
|
+
var init_IterationBudget = __esm({
|
|
5358
|
+
"packages/daemon/src/IterationBudget.ts"() {
|
|
5359
|
+
"use strict";
|
|
5360
|
+
IterationBudget = class _IterationBudget {
|
|
5361
|
+
constructor(maxIterations, parent, childAllocation) {
|
|
5362
|
+
this.maxIterations = maxIterations;
|
|
5363
|
+
this.parent = parent ?? null;
|
|
5364
|
+
this.childAllocation = childAllocation ?? maxIterations;
|
|
5365
|
+
}
|
|
5366
|
+
used = 0;
|
|
5367
|
+
parent;
|
|
5368
|
+
childAllocation;
|
|
5369
|
+
/**
|
|
5370
|
+
* Consume N iterations. Returns true if budget remains, false if exhausted.
|
|
5371
|
+
* Also consumes from parent budget if this is a child.
|
|
5372
|
+
*/
|
|
5373
|
+
consume(n = 1) {
|
|
5374
|
+
this.used += n;
|
|
5375
|
+
if (this.parent) {
|
|
5376
|
+
return this.parent.consume(n) && this.used <= this.maxIterations;
|
|
5377
|
+
}
|
|
5378
|
+
return this.used <= this.maxIterations;
|
|
5379
|
+
}
|
|
5380
|
+
/**
|
|
5381
|
+
* Refund N iterations (e.g., for execute_code calls that don't count).
|
|
5382
|
+
*/
|
|
5383
|
+
refund(n = 1) {
|
|
5384
|
+
this.used = Math.max(0, this.used - n);
|
|
5385
|
+
this.parent?.refund(n);
|
|
5386
|
+
}
|
|
5387
|
+
remaining() {
|
|
5388
|
+
const local = this.maxIterations - this.used;
|
|
5389
|
+
if (this.parent) {
|
|
5390
|
+
return Math.min(local, this.parent.remaining());
|
|
5391
|
+
}
|
|
5392
|
+
return local;
|
|
5393
|
+
}
|
|
5394
|
+
exhausted() {
|
|
5395
|
+
return this.remaining() <= 0;
|
|
5396
|
+
}
|
|
5397
|
+
/**
|
|
5398
|
+
* Fork a child budget that draws from this parent's remaining pool.
|
|
5399
|
+
* The child has its own max but also decrements the parent on each consume.
|
|
5400
|
+
*/
|
|
5401
|
+
fork(childMax) {
|
|
5402
|
+
const max = childMax ?? Math.min(30, this.remaining());
|
|
5403
|
+
return new _IterationBudget(max, this, max);
|
|
5404
|
+
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Get a budget pressure message if running low.
|
|
5407
|
+
* Returns null if budget is healthy.
|
|
5408
|
+
*/
|
|
5409
|
+
pressureWarning() {
|
|
5410
|
+
const rem = this.remaining();
|
|
5411
|
+
const pct = rem / this.maxIterations;
|
|
5412
|
+
if (pct <= 0) {
|
|
5413
|
+
return "BUDGET EXHAUSTED: You have used all available iterations. Wrap up immediately.";
|
|
5414
|
+
}
|
|
5415
|
+
if (pct <= 0.1) {
|
|
5416
|
+
return `BUDGET CRITICAL: Only ${rem} iteration(s) remaining. Finish the current task NOW.`;
|
|
5417
|
+
}
|
|
5418
|
+
if (pct <= 0.25) {
|
|
5419
|
+
return `Budget warning: ${rem} iterations remaining (${Math.round(pct * 100)}%). Start wrapping up.`;
|
|
5420
|
+
}
|
|
5421
|
+
return null;
|
|
5422
|
+
}
|
|
5423
|
+
stats() {
|
|
5424
|
+
return {
|
|
5425
|
+
used: this.used,
|
|
5426
|
+
max: this.maxIterations,
|
|
5427
|
+
remaining: this.maxIterations - this.used,
|
|
5428
|
+
...this.parent ? { parentRemaining: this.parent.remaining() } : {}
|
|
5429
|
+
};
|
|
5430
|
+
}
|
|
5431
|
+
};
|
|
5432
|
+
}
|
|
5433
|
+
});
|
|
5434
|
+
|
|
5435
|
+
// packages/daemon/src/PromptInjectionScanner.ts
|
|
5436
|
+
function scanForInjection(content, source) {
|
|
5437
|
+
const warnings = [];
|
|
5438
|
+
let sanitized = content;
|
|
5439
|
+
const invisibleMatches = content.match(INVISIBLE_CHARS);
|
|
5440
|
+
if (invisibleMatches && invisibleMatches.length > 0) {
|
|
5441
|
+
warnings.push(`${source || "content"}: ${invisibleMatches.length} invisible Unicode chars detected and stripped`);
|
|
5442
|
+
sanitized = sanitized.replace(INVISIBLE_CHARS, "");
|
|
5443
|
+
}
|
|
5444
|
+
let hasBlock = false;
|
|
5445
|
+
for (const { pattern, severity, label } of INJECTION_PATTERNS) {
|
|
5446
|
+
if (pattern.test(content)) {
|
|
5447
|
+
const msg = `${source || "content"}: ${label} detected (${severity})`;
|
|
5448
|
+
warnings.push(msg);
|
|
5449
|
+
if (severity === "block") hasBlock = true;
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
const lines = content.split("\n");
|
|
5453
|
+
const uniqueLines = new Set(lines.map((l) => l.trim()).filter((l) => l.length > 10));
|
|
5454
|
+
if (lines.length > 20 && uniqueLines.size < lines.length * 0.3) {
|
|
5455
|
+
warnings.push(`${source || "content"}: suspicious repetition (${uniqueLines.size} unique of ${lines.length} lines)`);
|
|
5456
|
+
}
|
|
5457
|
+
return {
|
|
5458
|
+
safe: !hasBlock,
|
|
5459
|
+
warnings,
|
|
5460
|
+
sanitized: hasBlock ? `[BLOCKED: Prompt injection detected in ${source || "content"}. Content excluded for safety.]` : sanitized
|
|
5461
|
+
};
|
|
5462
|
+
}
|
|
5463
|
+
function sanitizeContextFile(content, filePath, log) {
|
|
5464
|
+
const result = scanForInjection(content, filePath);
|
|
5465
|
+
for (const w of result.warnings) {
|
|
5466
|
+
log?.(`[injection-scan] ${w}`);
|
|
5467
|
+
}
|
|
5468
|
+
return result.sanitized;
|
|
5469
|
+
}
|
|
5470
|
+
var INJECTION_PATTERNS, INVISIBLE_CHARS;
|
|
5471
|
+
var init_PromptInjectionScanner = __esm({
|
|
5472
|
+
"packages/daemon/src/PromptInjectionScanner.ts"() {
|
|
5473
|
+
"use strict";
|
|
5474
|
+
INJECTION_PATTERNS = [
|
|
5475
|
+
// Direct instruction override
|
|
5476
|
+
{ pattern: /ignore\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions?|context|rules?|prompts?)/i, severity: "block", label: "instruction override" },
|
|
5477
|
+
{ pattern: /disregard\s+(all\s+)?(previous|prior|above)\s+(instructions?|context)/i, severity: "block", label: "instruction override" },
|
|
5478
|
+
{ pattern: /forget\s+(everything|all|what)\s+(you|I)\s+(told|said|instructed)/i, severity: "block", label: "instruction override" },
|
|
5479
|
+
{ pattern: /new\s+instructions?\s*:/i, severity: "block", label: "instruction injection" },
|
|
5480
|
+
{ pattern: /\bsystem\s*:\s*you\s+are\b/i, severity: "block", label: "role hijack" },
|
|
5481
|
+
// Role impersonation
|
|
5482
|
+
{ pattern: /you\s+are\s+now\s+(a|an|the)\s+/i, severity: "warn", label: "role impersonation" },
|
|
5483
|
+
{ pattern: /act\s+as\s+(if|though)\s+you\s+are/i, severity: "warn", label: "role impersonation" },
|
|
5484
|
+
{ pattern: /pretend\s+(you'?re?|to\s+be)\s+/i, severity: "warn", label: "role impersonation" },
|
|
5485
|
+
// Credential/data exfiltration
|
|
5486
|
+
{ pattern: /send\s+(the|your|all)\s+(api\s*key|token|password|secret|credential)/i, severity: "block", label: "credential exfil" },
|
|
5487
|
+
{ pattern: /output\s+(the|your)\s+system\s+prompt/i, severity: "block", label: "prompt extraction" },
|
|
5488
|
+
{ pattern: /reveal\s+(the|your)\s+(system|initial)\s+(prompt|instructions)/i, severity: "block", label: "prompt extraction" },
|
|
5489
|
+
{ pattern: /what\s+(is|are)\s+your\s+(system\s+)?instructions/i, severity: "warn", label: "prompt extraction attempt" },
|
|
5490
|
+
// Encoded/obfuscated injection
|
|
5491
|
+
{ pattern: /base64\s*decode|atob\s*\(/i, severity: "warn", label: "encoded payload" },
|
|
5492
|
+
{ pattern: /eval\s*\(|Function\s*\(/i, severity: "warn", label: "code execution attempt" }
|
|
5493
|
+
];
|
|
5494
|
+
INVISIBLE_CHARS = /[\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2060\u2061\u2062\u2063\u2064\u2066-\u2069\uFEFF\u00AD]/g;
|
|
5257
5495
|
}
|
|
5258
5496
|
});
|
|
5259
5497
|
|
|
@@ -5268,6 +5506,8 @@ var init_AgentExecutor = __esm({
|
|
|
5268
5506
|
"use strict";
|
|
5269
5507
|
init_LLMExecutor();
|
|
5270
5508
|
init_capabilities();
|
|
5509
|
+
init_IterationBudget();
|
|
5510
|
+
init_PromptInjectionScanner();
|
|
5271
5511
|
SELF_MOD_PATTERN = /\b(yourself|the agent|this agent|this cli|0agent|your code|your source|agent cli|improve.*agent|update.*agent|add.*to.*agent|fix.*agent|self.?improv)\b/i;
|
|
5272
5512
|
AgentExecutor = class {
|
|
5273
5513
|
constructor(llm, config, onStep, onToken) {
|
|
@@ -5299,9 +5539,17 @@ var init_AgentExecutor = __esm({
|
|
|
5299
5539
|
const systemPrompt = this.buildSystemPrompt(systemContext, task);
|
|
5300
5540
|
const activeTools = this.registry.getToolDefinitionsFor(task);
|
|
5301
5541
|
let toolSet = activeTools;
|
|
5542
|
+
const budget = new IterationBudget(this.maxIterations);
|
|
5302
5543
|
const messages = [
|
|
5303
5544
|
{ role: "user", content: task }
|
|
5304
5545
|
];
|
|
5546
|
+
const isJustdo = /book|file.*itr|tax.*file|irctc|train.*ticket|flight|passport|appointment|login.*portal|pan.*card|aadhaar|monitor.*watch|price.*drop|slot.*available|justdo/i.test(task);
|
|
5547
|
+
if (isJustdo) {
|
|
5548
|
+
messages.push(
|
|
5549
|
+
{ role: "assistant", content: "I can help with that. Let me start collecting the information I need." },
|
|
5550
|
+
{ role: "user", content: "Yes, go ahead." }
|
|
5551
|
+
);
|
|
5552
|
+
}
|
|
5305
5553
|
const contextLimit = LLMExecutor.getContextWindowTokens(this.llm["config"]?.model ?? "claude-sonnet-4-6");
|
|
5306
5554
|
if (isSelfMod) {
|
|
5307
5555
|
this.maxIterations = Math.max(this.maxIterations, 50);
|
|
@@ -5313,6 +5561,15 @@ var init_AgentExecutor = __esm({
|
|
|
5313
5561
|
finalOutput = "Cancelled.";
|
|
5314
5562
|
break;
|
|
5315
5563
|
}
|
|
5564
|
+
if (budget.exhausted()) {
|
|
5565
|
+
this.onStep("Iteration budget exhausted \u2014 wrapping up.");
|
|
5566
|
+
break;
|
|
5567
|
+
}
|
|
5568
|
+
budget.consume(1);
|
|
5569
|
+
const pressureMsg = budget.pressureWarning();
|
|
5570
|
+
if (pressureMsg && messages.length > 2) {
|
|
5571
|
+
messages.push({ role: "user", content: `[SYSTEM] ${pressureMsg}` });
|
|
5572
|
+
}
|
|
5316
5573
|
this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
|
|
5317
5574
|
const estimatedTokens = this._estimateTokens(messages);
|
|
5318
5575
|
if (estimatedTokens > contextLimit - 16384) {
|
|
@@ -5383,35 +5640,23 @@ var init_AgentExecutor = __esm({
|
|
|
5383
5640
|
content: response.content,
|
|
5384
5641
|
tool_calls: response.tool_calls
|
|
5385
5642
|
});
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
result
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
}
|
|
5397
|
-
if (capResult.fallback_used) {
|
|
5398
|
-
this.onStep(` (used fallback: ${capResult.fallback_used})`);
|
|
5399
|
-
}
|
|
5400
|
-
if (tc.name === "file_op" && tc.input.op === "write" && tc.input.path) {
|
|
5401
|
-
filesWritten.push(String(tc.input.path));
|
|
5402
|
-
}
|
|
5403
|
-
if (tc.name === "shell_exec" && tc.input.command) {
|
|
5404
|
-
commandsRun.push(String(tc.input.command));
|
|
5405
|
-
}
|
|
5406
|
-
} catch (err) {
|
|
5407
|
-
result = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
5643
|
+
const toolCalls = response.tool_calls;
|
|
5644
|
+
const { parallel, serial } = this._partitionToolCalls(toolCalls);
|
|
5645
|
+
if (parallel.length > 0) {
|
|
5646
|
+
const results = await Promise.all(parallel.map(async (tc) => {
|
|
5647
|
+
this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)}) [parallel]`);
|
|
5648
|
+
return { tc, result: await this._executeSingleTool(tc, signal, filesWritten, commandsRun) };
|
|
5649
|
+
}));
|
|
5650
|
+
for (const { tc, result } of results) {
|
|
5651
|
+
this.onStep(` \u21B3 ${result.slice(0, 120)}${result.length > 120 ? "\u2026" : ""}`);
|
|
5652
|
+
messages.push({ role: "tool", content: result, tool_call_id: tc.id });
|
|
5408
5653
|
}
|
|
5654
|
+
}
|
|
5655
|
+
for (const tc of serial) {
|
|
5656
|
+
this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})`);
|
|
5657
|
+
const result = await this._executeSingleTool(tc, signal, filesWritten, commandsRun);
|
|
5409
5658
|
this.onStep(` \u21B3 ${result.slice(0, 120)}${result.length > 120 ? "\u2026" : ""}`);
|
|
5410
|
-
messages.push({
|
|
5411
|
-
role: "tool",
|
|
5412
|
-
content: result,
|
|
5413
|
-
tool_call_id: tc.id
|
|
5414
|
-
});
|
|
5659
|
+
messages.push({ role: "tool", content: result, tool_call_id: tc.id });
|
|
5415
5660
|
}
|
|
5416
5661
|
}
|
|
5417
5662
|
return {
|
|
@@ -5727,7 +5972,8 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
|
|
|
5727
5972
|
if (existsSync5(f)) {
|
|
5728
5973
|
const content = readFileSync4(f, "utf8").trim();
|
|
5729
5974
|
if (content && content.length < 4e3) {
|
|
5730
|
-
|
|
5975
|
+
const sanitized = sanitizeContextFile(content, f);
|
|
5976
|
+
lines.push(``, `Project instructions:`, sanitized);
|
|
5731
5977
|
break;
|
|
5732
5978
|
}
|
|
5733
5979
|
}
|
|
@@ -5832,6 +6078,75 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
|
|
|
5832
6078
|
if (toolName === "scrape_url") return `"${String(input.url ?? "").slice(0, 60)}" mode=${input.mode ?? "text"}`;
|
|
5833
6079
|
return JSON.stringify(input).slice(0, 60);
|
|
5834
6080
|
}
|
|
6081
|
+
/**
|
|
6082
|
+
* Execute a single tool call. Returns the result string.
|
|
6083
|
+
*/
|
|
6084
|
+
async _executeSingleTool(tc, signal, filesWritten, commandsRun) {
|
|
6085
|
+
let result;
|
|
6086
|
+
try {
|
|
6087
|
+
const capResult = await this.registry.execute(tc.name, tc.input, this.cwd, signal);
|
|
6088
|
+
result = capResult.output;
|
|
6089
|
+
const MAX_TOOL_OUTPUT = 4e3;
|
|
6090
|
+
if (result.length > MAX_TOOL_OUTPUT) {
|
|
6091
|
+
result = result.slice(0, MAX_TOOL_OUTPUT) + `
|
|
6092
|
+
[...${result.length - MAX_TOOL_OUTPUT} chars truncated]`;
|
|
6093
|
+
}
|
|
6094
|
+
if (capResult.fallback_used) {
|
|
6095
|
+
this.onStep(` (used fallback: ${capResult.fallback_used})`);
|
|
6096
|
+
}
|
|
6097
|
+
if (tc.name === "file_op" && tc.input.op === "write" && tc.input.path) {
|
|
6098
|
+
filesWritten.push(String(tc.input.path));
|
|
6099
|
+
}
|
|
6100
|
+
if (tc.name === "shell_exec" && tc.input.command) {
|
|
6101
|
+
commandsRun.push(String(tc.input.command));
|
|
6102
|
+
}
|
|
6103
|
+
} catch (err) {
|
|
6104
|
+
result = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
6105
|
+
}
|
|
6106
|
+
return result;
|
|
6107
|
+
}
|
|
6108
|
+
/**
|
|
6109
|
+
* Partition tool calls into parallelisable and serial groups.
|
|
6110
|
+
* Inspired by Hermes Agent's path-overlap analysis.
|
|
6111
|
+
*
|
|
6112
|
+
* Tools are safe to parallelize when they don't share file paths
|
|
6113
|
+
* and aren't in the never-parallel set (shell_exec, browser_execute).
|
|
6114
|
+
*/
|
|
6115
|
+
_partitionToolCalls(calls) {
|
|
6116
|
+
if (calls.length <= 1) return { parallel: [], serial: calls };
|
|
6117
|
+
const NEVER_PARALLEL = /* @__PURE__ */ new Set(["shell_exec", "browser_execute", "credential_vault", "monitor_watch"]);
|
|
6118
|
+
const parallel = [];
|
|
6119
|
+
const serial = [];
|
|
6120
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
6121
|
+
for (const tc of calls) {
|
|
6122
|
+
if (NEVER_PARALLEL.has(tc.name)) {
|
|
6123
|
+
serial.push(tc);
|
|
6124
|
+
continue;
|
|
6125
|
+
}
|
|
6126
|
+
const paths = this._extractPaths(tc);
|
|
6127
|
+
let hasOverlap = false;
|
|
6128
|
+
for (const p of paths) {
|
|
6129
|
+
if (usedPaths.has(p)) {
|
|
6130
|
+
hasOverlap = true;
|
|
6131
|
+
break;
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
if (hasOverlap) {
|
|
6135
|
+
serial.push(tc);
|
|
6136
|
+
} else {
|
|
6137
|
+
parallel.push(tc);
|
|
6138
|
+
for (const p of paths) usedPaths.add(p);
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
return { parallel, serial };
|
|
6142
|
+
}
|
|
6143
|
+
_extractPaths(tc) {
|
|
6144
|
+
const paths = [];
|
|
6145
|
+
if (tc.input.path) paths.push(String(tc.input.path));
|
|
6146
|
+
if (tc.input.image_path) paths.push(String(tc.input.image_path));
|
|
6147
|
+
if (tc.input.url) paths.push(String(tc.input.url));
|
|
6148
|
+
return paths;
|
|
6149
|
+
}
|
|
5835
6150
|
};
|
|
5836
6151
|
}
|
|
5837
6152
|
});
|
|
@@ -7002,6 +7317,76 @@ var ConversationStore = class {
|
|
|
7002
7317
|
}
|
|
7003
7318
|
};
|
|
7004
7319
|
|
|
7320
|
+
// packages/daemon/src/SmartModelRouter.ts
|
|
7321
|
+
var COMPLEX_PATTERNS = [
|
|
7322
|
+
// Code-related
|
|
7323
|
+
/\b(implement|build|write|fix|refactor|debug|test|deploy|compile|lint|bundle|migrate)\b/i,
|
|
7324
|
+
/\b(function|class|interface|module|component|endpoint|schema|type|hook|middleware)\b/i,
|
|
7325
|
+
/\b(error|bug|crash|fail|broken|issue|stack\s*trace|exception|undefined|null)\b/i,
|
|
7326
|
+
// Tool-heavy tasks
|
|
7327
|
+
/\b(search|scrape|browse|download|install|create\s+file|delete|execute|run|shell|command)\b/i,
|
|
7328
|
+
/\b(git|npm|pip|docker|kubectl|terraform|aws|gcp|azure)\b/i,
|
|
7329
|
+
// Justdo / web tasks
|
|
7330
|
+
/\b(book|file.*itr|irctc|train|ticket|flight|passport|appointment|monitor|watch|login|portal)\b/i,
|
|
7331
|
+
// Long or detailed requests
|
|
7332
|
+
/\b(explain|analyze|review|compare|design|plan|architect|optimise|optimize|improve)\b/i,
|
|
7333
|
+
// URLs, code blocks, file paths
|
|
7334
|
+
/https?:\/\//,
|
|
7335
|
+
/```/,
|
|
7336
|
+
/\/[\w\-]+\.[\w]+/,
|
|
7337
|
+
// file paths like /foo/bar.ts
|
|
7338
|
+
// Self-modification
|
|
7339
|
+
/\b(yourself|agent|0agent|daemon|capability|skill)\b/i
|
|
7340
|
+
// Multi-step or long messages (>200 chars likely complex)
|
|
7341
|
+
];
|
|
7342
|
+
var SIMPLE_PATTERNS = [
|
|
7343
|
+
/^(hey|hi|hello|sup|yo|hola|namaste|what'?s?\s*up|how\s+are\s+you)[!?.\s,]*$/i,
|
|
7344
|
+
/^(thanks|thank\s+you|thx|ok|okay|cool|great|nice|perfect|got\s+it|sure|yep|yeah|yes|no|nah)[!?.\s,]*$/i,
|
|
7345
|
+
/^(bye|goodbye|see\s+ya|later|good\s*(morning|evening|afternoon|night))[!?.\s,]*$/i,
|
|
7346
|
+
/^(what\s+is|who\s+is|when\s+was|where\s+is|how\s+do\s+you|what\s+does|can\s+you)\b.{0,80}$/i,
|
|
7347
|
+
/^(tell\s+me\s+about|summarize|summarise|tldr|tl;dr)\b.{0,80}$/i
|
|
7348
|
+
];
|
|
7349
|
+
function routeMessage(task) {
|
|
7350
|
+
const trimmed = task.trim();
|
|
7351
|
+
if (trimmed.length < 5) {
|
|
7352
|
+
return { decision: "skip", reason: "too short, likely noise" };
|
|
7353
|
+
}
|
|
7354
|
+
for (const p of SIMPLE_PATTERNS) {
|
|
7355
|
+
if (p.test(trimmed)) {
|
|
7356
|
+
return { decision: "fast", reason: `matches simple pattern: ${p.source.slice(0, 30)}` };
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7359
|
+
for (const p of COMPLEX_PATTERNS) {
|
|
7360
|
+
if (p.test(trimmed)) {
|
|
7361
|
+
return { decision: "primary", reason: `matches complex pattern: ${p.source.slice(0, 30)}` };
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
if (trimmed.length > 200) {
|
|
7365
|
+
return { decision: "primary", reason: "long message (>200 chars)" };
|
|
7366
|
+
}
|
|
7367
|
+
const sentences = trimmed.split(/[.!?]+/).filter((s) => s.trim().length > 0);
|
|
7368
|
+
if (sentences.length > 2) {
|
|
7369
|
+
return { decision: "primary", reason: "multi-sentence request" };
|
|
7370
|
+
}
|
|
7371
|
+
return { decision: "fast", reason: "short message, no complex indicators" };
|
|
7372
|
+
}
|
|
7373
|
+
function getFastModelId(provider, _currentModel) {
|
|
7374
|
+
switch (provider) {
|
|
7375
|
+
case "anthropic":
|
|
7376
|
+
return "claude-haiku-4-5-20251001";
|
|
7377
|
+
case "openai":
|
|
7378
|
+
return "gpt-4o-mini";
|
|
7379
|
+
case "gemini":
|
|
7380
|
+
return "gemini-2.0-flash";
|
|
7381
|
+
case "xai":
|
|
7382
|
+
return "grok-3-mini";
|
|
7383
|
+
case "groq":
|
|
7384
|
+
return "llama-3.1-8b-instant";
|
|
7385
|
+
default:
|
|
7386
|
+
return null;
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
|
|
7005
7390
|
// packages/daemon/src/SessionManager.ts
|
|
7006
7391
|
import { readFileSync as readFileSync7, existsSync as existsSync9 } from "node:fs";
|
|
7007
7392
|
import { resolve as resolve8 } from "node:path";
|
|
@@ -7241,14 +7626,21 @@ var SessionManager = class {
|
|
|
7241
7626
|
const activeLLM = this.getFreshLLM();
|
|
7242
7627
|
if (activeLLM?.isConfigured) {
|
|
7243
7628
|
const userEntityId = enrichedReq.entity_id ?? this.identity?.entity_node_id;
|
|
7244
|
-
const
|
|
7245
|
-
if (
|
|
7246
|
-
|
|
7629
|
+
const routing = routeMessage(enrichedReq.task);
|
|
7630
|
+
if (routing.decision === "skip") {
|
|
7631
|
+
this.completeSession(sessionId, { output: "", files_written: [], commands_run: [], tokens_used: 0, model: "skip" });
|
|
7632
|
+
return this.sessions.get(sessionId);
|
|
7633
|
+
}
|
|
7634
|
+
if (routing.decision === "fast") {
|
|
7635
|
+
const provider = activeLLM["config"]?.provider ?? "anthropic";
|
|
7636
|
+
const fastModel = getFastModelId(provider);
|
|
7637
|
+
const llmToUse = fastModel ? activeLLM.withModel(fastModel) : activeLLM;
|
|
7638
|
+
const resp = await llmToUse.complete(
|
|
7247
7639
|
[{ role: "user", content: enrichedReq.task }],
|
|
7248
|
-
"You are a helpful assistant."
|
|
7640
|
+
this.identity ? `You are 0agent, a helpful AI assistant. You are talking to ${this.identity.name}. Be concise and friendly.` : "You are 0agent, a helpful AI assistant. Be concise and friendly."
|
|
7249
7641
|
);
|
|
7250
7642
|
this.emit({ type: "session.token", session_id: sessionId, token: resp.content });
|
|
7251
|
-
this.addStep(sessionId, `Done (${resp.tokens_used} tokens,
|
|
7643
|
+
this.addStep(sessionId, `Done (${resp.tokens_used} tokens, fast model, reason: ${routing.reason})`);
|
|
7252
7644
|
this.completeSession(sessionId, {
|
|
7253
7645
|
output: resp.content,
|
|
7254
7646
|
files_written: [],
|