agentroam 0.2.0-preview.1 → 0.2.0-preview.2
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 +4 -4
- package/dist/cli.js +15 -4
- package/dist/cloudflared/installer.js +19 -4
- package/dist/cloudflared/installer.test.js +15 -0
- package/dist/native-runtime.js +15 -0
- package/dist/native-runtime.test.js +19 -0
- package/dist/network.js +17 -0
- package/dist/network.test.js +20 -0
- package/dist/runtime-manager.js +2 -1
- package/package.json +2 -2
- package/runtime/.next/BUILD_ID +1 -1
- package/runtime/.next/app-build-manifest.json +3 -3
- package/runtime/.next/app-path-routes-manifest.json +1 -1
- package/runtime/.next/build-manifest.json +5 -5
- package/runtime/.next/prerender-manifest.json +1 -1
- package/runtime/.next/react-loadable-manifest.json +1 -1
- package/runtime/.next/server/app/_not-found.html +1 -1
- package/runtime/.next/server/app/_not-found.rsc +1 -1
- package/runtime/.next/server/app/web.html +1 -1
- package/runtime/.next/server/app/web.rsc +1 -1
- package/runtime/.next/server/app-paths-manifest.json +2 -2
- package/runtime/.next/server/chunks/322.js +31 -31
- package/runtime/.next/server/middleware-build-manifest.js +1 -1
- package/runtime/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/runtime/.next/server/pages/404.html +1 -1
- package/runtime/.next/server/pages/500.html +1 -1
- package/runtime/.next/server/server-reference-manifest.json +1 -1
- package/runtime/.next/static/chunks/804.0e20fa702013ffef.js +1 -0
- package/runtime/.next/static/chunks/{webpack-3a28d671c5a42bf1.js → webpack-e56f3a1950168597.js} +1 -1
- package/runtime/node_modules/@agent/core/dist/domain/agent/AgentBuilder.d.ts +3 -0
- package/runtime/node_modules/@agent/core/dist/domain/agent/AgentBuilder.d.ts.map +1 -1
- package/runtime/node_modules/@agent/core/dist/domain/context/ContextLoader.d.ts +1 -0
- package/runtime/node_modules/@agent/core/dist/domain/context/ContextLoader.d.ts.map +1 -1
- package/runtime/node_modules/@agent/core/dist/domain/skill/SkillRegistry.d.ts +4 -0
- package/runtime/node_modules/@agent/core/dist/domain/skill/SkillRegistry.d.ts.map +1 -1
- package/runtime/node_modules/@agent/core/dist/domain/tool/builtin/WebSearchTool.d.ts +5 -2
- package/runtime/node_modules/@agent/core/dist/domain/tool/builtin/WebSearchTool.d.ts.map +1 -1
- package/runtime/node_modules/@agent/core/dist/index.js +206 -67
- package/runtime/node_modules/@agent/core/dist/index.js.map +8 -8
- package/runtime/node_modules/@agent/core/dist/infrastructure/SQLiteSessionStore.d.ts +4 -0
- package/runtime/node_modules/@agent/core/dist/infrastructure/SQLiteSessionStore.d.ts.map +1 -1
- package/runtime/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/runtime/.next/static/chunks/804.33d400571801aa9e.js +0 -1
- /package/runtime/.next/static/{DmJe8rRLl3EQW0weZvItS → 47UkTmBGxP5qz2mnMbNEp}/_buildManifest.js +0 -0
- /package/runtime/.next/static/{DmJe8rRLl3EQW0weZvItS → 47UkTmBGxP5qz2mnMbNEp}/_ssgManifest.js +0 -0
|
@@ -5398,9 +5398,37 @@ ${truncated}`
|
|
|
5398
5398
|
}
|
|
5399
5399
|
}
|
|
5400
5400
|
// src/domain/tool/builtin/WebSearchTool.ts
|
|
5401
|
+
var BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36";
|
|
5402
|
+
function stripTags(html) {
|
|
5403
|
+
return html.replace(/<[^>]+>/g, "");
|
|
5404
|
+
}
|
|
5405
|
+
function decodeEntities(text) {
|
|
5406
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'|'/g, "'").replace(/ /g, " ").replace(/&/g, "&");
|
|
5407
|
+
}
|
|
5408
|
+
function describeError(err) {
|
|
5409
|
+
if (!(err instanceof Error))
|
|
5410
|
+
return "Search failed";
|
|
5411
|
+
const cause = err.cause;
|
|
5412
|
+
const causeMsg = cause instanceof Error ? cause.message : typeof cause === "string" ? cause : "";
|
|
5413
|
+
return causeMsg ? `${err.message}: ${causeMsg}` : err.message;
|
|
5414
|
+
}
|
|
5415
|
+
function decodeBingRedirect(href) {
|
|
5416
|
+
const decoded = decodeEntities(href);
|
|
5417
|
+
const u = /[?&]u=([^&]+)/.exec(decoded)?.[1];
|
|
5418
|
+
if (u && u.length > 2) {
|
|
5419
|
+
try {
|
|
5420
|
+
const b64 = u.slice(2).replace(/-/g, "+").replace(/_/g, "/");
|
|
5421
|
+
const real = atob(b64);
|
|
5422
|
+
if (/^https?:\/\//.test(real))
|
|
5423
|
+
return real;
|
|
5424
|
+
} catch {}
|
|
5425
|
+
}
|
|
5426
|
+
return decoded;
|
|
5427
|
+
}
|
|
5428
|
+
|
|
5401
5429
|
class WebSearchTool {
|
|
5402
5430
|
name = "web_search";
|
|
5403
|
-
description = "Search the web for information using DuckDuckGo HTML";
|
|
5431
|
+
description = "Search the web for information using DuckDuckGo HTML, falling back to Bing";
|
|
5404
5432
|
schema = exports_external.object({
|
|
5405
5433
|
query: exports_external.string().min(2).describe("The search query")
|
|
5406
5434
|
});
|
|
@@ -5420,44 +5448,63 @@ class WebSearchTool {
|
|
|
5420
5448
|
isError: true
|
|
5421
5449
|
};
|
|
5422
5450
|
}
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
};
|
|
5434
|
-
}
|
|
5435
|
-
const html = await response.text();
|
|
5436
|
-
const results = this.parseResults(html);
|
|
5437
|
-
return {
|
|
5438
|
-
toolCallId: "",
|
|
5439
|
-
content: results.length > 0 ? results.map((r, i) => `${i + 1}. ${r.title}
|
|
5451
|
+
const errors2 = [];
|
|
5452
|
+
let anySourceResponded = false;
|
|
5453
|
+
for (const search of [this.searchDuckDuckGo, this.searchBing]) {
|
|
5454
|
+
try {
|
|
5455
|
+
const results = await search.call(this, parsed.data.query);
|
|
5456
|
+
anySourceResponded = true;
|
|
5457
|
+
if (results.length > 0) {
|
|
5458
|
+
return {
|
|
5459
|
+
toolCallId: "",
|
|
5460
|
+
content: results.map((r, i) => `${i + 1}. ${r.title}
|
|
5440
5461
|
${r.snippet}
|
|
5441
5462
|
${r.url}`).join(`
|
|
5442
5463
|
|
|
5443
|
-
`)
|
|
5444
|
-
|
|
5445
|
-
|
|
5464
|
+
`)
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
5467
|
+
} catch (err) {
|
|
5468
|
+
errors2.push(describeError(err));
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
if (!anySourceResponded && errors2.length > 0) {
|
|
5446
5472
|
return {
|
|
5447
5473
|
toolCallId: "",
|
|
5448
|
-
content:
|
|
5474
|
+
content: `Search failed: ${errors2.join(" | ")}`,
|
|
5449
5475
|
isError: true
|
|
5450
5476
|
};
|
|
5451
5477
|
}
|
|
5478
|
+
return { toolCallId: "", content: "No results found." };
|
|
5479
|
+
}
|
|
5480
|
+
async searchDuckDuckGo(query) {
|
|
5481
|
+
const response = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
|
|
5482
|
+
headers: { "User-Agent": "customer-agent/0.1" },
|
|
5483
|
+
signal: AbortSignal.timeout(15000)
|
|
5484
|
+
});
|
|
5485
|
+
if (!response.ok) {
|
|
5486
|
+
throw new Error(`DuckDuckGo HTTP ${response.status}`);
|
|
5487
|
+
}
|
|
5488
|
+
return this.parseDuckDuckGoResults(await response.text());
|
|
5489
|
+
}
|
|
5490
|
+
async searchBing(query) {
|
|
5491
|
+
const response = await fetch(`https://www.bing.com/search?q=${encodeURIComponent(query)}`, {
|
|
5492
|
+
headers: { "User-Agent": BROWSER_UA, Accept: "text/html" },
|
|
5493
|
+
signal: AbortSignal.timeout(15000)
|
|
5494
|
+
});
|
|
5495
|
+
if (!response.ok) {
|
|
5496
|
+
throw new Error(`Bing HTTP ${response.status}`);
|
|
5497
|
+
}
|
|
5498
|
+
return this.parseBingResults(await response.text());
|
|
5452
5499
|
}
|
|
5453
|
-
|
|
5500
|
+
parseDuckDuckGoResults(html) {
|
|
5454
5501
|
const results = [];
|
|
5455
5502
|
const linkRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
5456
5503
|
const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
5457
5504
|
let linkMatch;
|
|
5458
5505
|
while ((linkMatch = linkRegex.exec(html)) !== null) {
|
|
5459
5506
|
const url = linkMatch[1];
|
|
5460
|
-
const title = linkMatch[2]
|
|
5507
|
+
const title = decodeEntities(stripTags(linkMatch[2])).trim();
|
|
5461
5508
|
if (url && title) {
|
|
5462
5509
|
results.push({ title, url, snippet: "" });
|
|
5463
5510
|
}
|
|
@@ -5465,11 +5512,30 @@ class WebSearchTool {
|
|
|
5465
5512
|
let snippetMatch;
|
|
5466
5513
|
let i = 0;
|
|
5467
5514
|
while ((snippetMatch = snippetRegex.exec(html)) !== null && i < results.length) {
|
|
5468
|
-
results[i].snippet = snippetMatch[1]
|
|
5515
|
+
results[i].snippet = decodeEntities(stripTags(snippetMatch[1])).trim();
|
|
5469
5516
|
i++;
|
|
5470
5517
|
}
|
|
5471
5518
|
return results.slice(0, 10);
|
|
5472
5519
|
}
|
|
5520
|
+
parseBingResults(html) {
|
|
5521
|
+
const results = [];
|
|
5522
|
+
const blockRegex = /<li class="b_algo"[\s\S]*?<\/li>/gi;
|
|
5523
|
+
let block;
|
|
5524
|
+
while ((block = blockRegex.exec(html)) !== null && results.length < 10) {
|
|
5525
|
+
const chunk = block[0];
|
|
5526
|
+
const link = /<h2[^>]*>\s*<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/.exec(chunk);
|
|
5527
|
+
if (!link)
|
|
5528
|
+
continue;
|
|
5529
|
+
const title = decodeEntities(stripTags(link[2])).trim();
|
|
5530
|
+
const snippetMatch = /<p[^>]*>([\s\S]*?)<\/p>/.exec(chunk);
|
|
5531
|
+
const snippet = snippetMatch ? decodeEntities(stripTags(snippetMatch[1])).trim() : "";
|
|
5532
|
+
const url = decodeBingRedirect(link[1]);
|
|
5533
|
+
if (url && title) {
|
|
5534
|
+
results.push({ title, snippet, url });
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
return results;
|
|
5538
|
+
}
|
|
5473
5539
|
}
|
|
5474
5540
|
// src/domain/tool/builtin/GrepTool.ts
|
|
5475
5541
|
import { readFile as readFile3, readdir, stat } from "node:fs/promises";
|
|
@@ -7302,6 +7368,7 @@ class SkillRegistry {
|
|
|
7302
7368
|
promptCache = new Map;
|
|
7303
7369
|
semanticCache = new Map;
|
|
7304
7370
|
modelProvider = null;
|
|
7371
|
+
semanticMatchingEnabled = true;
|
|
7305
7372
|
constructor(loader) {
|
|
7306
7373
|
this.loader = loader;
|
|
7307
7374
|
}
|
|
@@ -7309,6 +7376,10 @@ class SkillRegistry {
|
|
|
7309
7376
|
this.modelProvider = provider;
|
|
7310
7377
|
this.semanticCache.clear();
|
|
7311
7378
|
}
|
|
7379
|
+
setSemanticMatchingEnabled(enabled) {
|
|
7380
|
+
this.semanticMatchingEnabled = enabled;
|
|
7381
|
+
this.semanticCache.clear();
|
|
7382
|
+
}
|
|
7312
7383
|
register(skill) {
|
|
7313
7384
|
this.skills.set(skill.name, skill);
|
|
7314
7385
|
if ("prompt" in skill) {
|
|
@@ -7326,9 +7397,12 @@ class SkillRegistry {
|
|
|
7326
7397
|
return Array.from(this.skills.values());
|
|
7327
7398
|
}
|
|
7328
7399
|
findMatching(input) {
|
|
7400
|
+
return this.findMatchingFrom(input, this.skills.values());
|
|
7401
|
+
}
|
|
7402
|
+
findMatchingFrom(input, skills) {
|
|
7329
7403
|
const lowerInput = input.toLowerCase();
|
|
7330
7404
|
const matched = [];
|
|
7331
|
-
for (const skill of
|
|
7405
|
+
for (const skill of skills) {
|
|
7332
7406
|
for (const trigger of skill.triggers) {
|
|
7333
7407
|
if (lowerInput.includes(trigger.toLowerCase())) {
|
|
7334
7408
|
matched.push(skill);
|
|
@@ -7338,14 +7412,15 @@ class SkillRegistry {
|
|
|
7338
7412
|
}
|
|
7339
7413
|
return matched;
|
|
7340
7414
|
}
|
|
7341
|
-
async findMatchingSemantic(input) {
|
|
7342
|
-
if (!this.modelProvider ||
|
|
7415
|
+
async findMatchingSemantic(input, candidates) {
|
|
7416
|
+
if (!this.modelProvider || candidates.length === 0)
|
|
7343
7417
|
return [];
|
|
7344
|
-
const
|
|
7418
|
+
const candidateKey = candidates.map((skill) => skill.name).sort().join("\x00");
|
|
7419
|
+
const cacheKey = `${input.trim().slice(0, 200)}\x01${candidateKey}`;
|
|
7345
7420
|
const cached = this.semanticCache.get(cacheKey);
|
|
7346
7421
|
if (cached)
|
|
7347
7422
|
return cached;
|
|
7348
|
-
const skillList =
|
|
7423
|
+
const skillList = candidates.map((s, i) => `${i + 1}. ${s.name}: ${s.description || "(no description)"}`).join(`
|
|
7349
7424
|
`);
|
|
7350
7425
|
const matchPrompt = `You are a skill matching assistant. Given the user's input and a list of available skills with their descriptions, determine which skills are semantically relevant to the user's request.
|
|
7351
7426
|
|
|
@@ -7379,8 +7454,9 @@ Return ONLY the names of relevant skills, one per line. If none are relevant, re
|
|
|
7379
7454
|
const names = trimmed.split(`
|
|
7380
7455
|
`).map((line) => line.replace(/^\d+\.\s*/, "").replace(/^[\s\-•*]+/, "").trim()).filter(Boolean);
|
|
7381
7456
|
const matched = [];
|
|
7457
|
+
const candidatesByName = new Map(candidates.map((skill) => [skill.name, skill]));
|
|
7382
7458
|
for (const name of names) {
|
|
7383
|
-
const skill =
|
|
7459
|
+
const skill = candidatesByName.get(name);
|
|
7384
7460
|
if (skill && !matched.find((m) => m.name === skill.name)) {
|
|
7385
7461
|
matched.push(skill);
|
|
7386
7462
|
}
|
|
@@ -7392,24 +7468,22 @@ Return ONLY the names of relevant skills, one per line. If none are relevant, re
|
|
|
7392
7468
|
}
|
|
7393
7469
|
}
|
|
7394
7470
|
async getSkillPrompts(input, enabledSkills) {
|
|
7395
|
-
|
|
7471
|
+
const allowed = enabledSkills && enabledSkills.length > 0 ? new Set(enabledSkills) : null;
|
|
7472
|
+
const eligibleSkills = Array.from(this.skills.values()).filter((skill) => !allowed || allowed.has(skill.name));
|
|
7473
|
+
let matched = this.findMatchingFrom(input, eligibleSkills);
|
|
7396
7474
|
const slashMatch = input.match(/^\/([\w-]+)/);
|
|
7397
7475
|
if (slashMatch) {
|
|
7398
|
-
const byName =
|
|
7476
|
+
const byName = eligibleSkills.find((skill) => skill.name === slashMatch[1]);
|
|
7399
7477
|
if (byName && !matched.find((m) => m.name === byName.name)) {
|
|
7400
7478
|
matched.unshift(byName);
|
|
7401
7479
|
}
|
|
7402
7480
|
}
|
|
7403
|
-
if (matched.length === 0) {
|
|
7404
|
-
const semanticMatches = await this.findMatchingSemantic(input);
|
|
7481
|
+
if (matched.length === 0 && this.semanticMatchingEnabled && eligibleSkills.length > 0) {
|
|
7482
|
+
const semanticMatches = await this.findMatchingSemantic(input, eligibleSkills);
|
|
7405
7483
|
if (semanticMatches.length > 0) {
|
|
7406
7484
|
matched = semanticMatches;
|
|
7407
7485
|
}
|
|
7408
7486
|
}
|
|
7409
|
-
if (enabledSkills && enabledSkills.length > 0) {
|
|
7410
|
-
const allowed = new Set(enabledSkills);
|
|
7411
|
-
matched = matched.filter((s) => allowed.has(s.name));
|
|
7412
|
-
}
|
|
7413
7487
|
if (matched.length === 0)
|
|
7414
7488
|
return "";
|
|
7415
7489
|
const parts = [];
|
|
@@ -7432,35 +7506,46 @@ ${prompt}`);
|
|
|
7432
7506
|
}
|
|
7433
7507
|
|
|
7434
7508
|
// src/domain/context/ContextLoader.ts
|
|
7435
|
-
import {
|
|
7436
|
-
import {
|
|
7437
|
-
|
|
7509
|
+
import { constants } from "node:fs";
|
|
7510
|
+
import { open, readFile as readFile6, readdir as readdir4, realpath, stat as stat4 } from "node:fs/promises";
|
|
7511
|
+
import { join as join6, basename as basename2, isAbsolute, relative as relative3, resolve as resolve8, sep } from "node:path";
|
|
7512
|
+
var ROOT_PROJECT_FILES = [
|
|
7513
|
+
"AGENTS.md",
|
|
7438
7514
|
"CLAUDE.md",
|
|
7439
7515
|
"README.md",
|
|
7440
7516
|
"CONTRIBUTING.md",
|
|
7441
7517
|
".cursorrules",
|
|
7442
7518
|
".github/copilot-instructions.md"
|
|
7443
7519
|
];
|
|
7520
|
+
var CUSTOMER_AGENT_FILES = ROOT_PROJECT_FILES.map((file) => join6(".customer-agent", file));
|
|
7521
|
+
var INSTRUCTION_FILES = new Set(["AGENTS.md", "CLAUDE.md"]);
|
|
7522
|
+
var EXCLUDED_DIRECTORIES = new Set(["node_modules", ".git", "dist"]);
|
|
7444
7523
|
|
|
7445
7524
|
class ContextLoader {
|
|
7446
7525
|
async loadProjectContext(rootDir) {
|
|
7447
7526
|
const files = [];
|
|
7448
|
-
|
|
7527
|
+
const loadedPaths = new Set;
|
|
7528
|
+
for (const relPath of [...ROOT_PROJECT_FILES, ...CUSTOMER_AGENT_FILES]) {
|
|
7449
7529
|
const fullPath = join6(rootDir, relPath);
|
|
7530
|
+
if (loadedPaths.has(fullPath))
|
|
7531
|
+
continue;
|
|
7450
7532
|
try {
|
|
7451
7533
|
await stat4(fullPath);
|
|
7452
7534
|
const file = await this.loadFile(fullPath);
|
|
7453
7535
|
files.push(file);
|
|
7536
|
+
loadedPaths.add(fullPath);
|
|
7454
7537
|
} catch {}
|
|
7455
7538
|
}
|
|
7456
7539
|
try {
|
|
7457
|
-
const
|
|
7458
|
-
|
|
7459
|
-
|
|
7540
|
+
const resolvedRoot = await realpath(rootDir);
|
|
7541
|
+
const instructionFiles = await this.findClaudeMdFiles(rootDir);
|
|
7542
|
+
for (const filePath of instructionFiles) {
|
|
7543
|
+
if (loadedPaths.has(filePath))
|
|
7460
7544
|
continue;
|
|
7461
7545
|
try {
|
|
7462
|
-
const file = await this.
|
|
7546
|
+
const file = await this.loadRecursiveFile(rootDir, resolvedRoot, filePath);
|
|
7463
7547
|
files.push(file);
|
|
7548
|
+
loadedPaths.add(filePath);
|
|
7464
7549
|
} catch {}
|
|
7465
7550
|
}
|
|
7466
7551
|
} catch {}
|
|
@@ -7470,7 +7555,7 @@ class ContextLoader {
|
|
|
7470
7555
|
const content = await readFile6(filePath, "utf-8");
|
|
7471
7556
|
const fileName = basename2(filePath);
|
|
7472
7557
|
let type = "other";
|
|
7473
|
-
if (fileName
|
|
7558
|
+
if (INSTRUCTION_FILES.has(fileName))
|
|
7474
7559
|
type = "claude_md";
|
|
7475
7560
|
else if (fileName === "README.md" || fileName === "CONTRIBUTING.md")
|
|
7476
7561
|
type = "readme";
|
|
@@ -7480,25 +7565,52 @@ class ContextLoader {
|
|
|
7480
7565
|
type = "code";
|
|
7481
7566
|
return { path: filePath, content, type };
|
|
7482
7567
|
}
|
|
7568
|
+
async loadRecursiveFile(rootDir, resolvedRoot, filePath) {
|
|
7569
|
+
const relativePath = relative3(rootDir, filePath);
|
|
7570
|
+
if (!relativePath || isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(`..${sep}`)) {
|
|
7571
|
+
throw new Error("Recursive context candidate is outside the configured root");
|
|
7572
|
+
}
|
|
7573
|
+
const expectedPath = resolve8(resolvedRoot, relativePath);
|
|
7574
|
+
const fileHandle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
7575
|
+
try {
|
|
7576
|
+
const openedStat = await fileHandle.stat();
|
|
7577
|
+
if (!openedStat.isFile()) {
|
|
7578
|
+
throw new Error("Recursive context candidate is not a regular file");
|
|
7579
|
+
}
|
|
7580
|
+
const resolvedPath = await realpath(filePath);
|
|
7581
|
+
if (resolvedPath !== expectedPath) {
|
|
7582
|
+
throw new Error("Recursive context candidate traverses a symbolic link");
|
|
7583
|
+
}
|
|
7584
|
+
const resolvedStat = await stat4(resolvedPath);
|
|
7585
|
+
if (resolvedStat.dev !== openedStat.dev || resolvedStat.ino !== openedStat.ino) {
|
|
7586
|
+
throw new Error("Recursive context candidate changed while opening");
|
|
7587
|
+
}
|
|
7588
|
+
const content = await fileHandle.readFile({ encoding: "utf-8" });
|
|
7589
|
+
return { path: filePath, content, type: "claude_md" };
|
|
7590
|
+
} finally {
|
|
7591
|
+
await fileHandle.close();
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7483
7594
|
async findClaudeMdFiles(rootDir) {
|
|
7484
7595
|
const results = [];
|
|
7485
7596
|
const scan = async (dir) => {
|
|
7486
7597
|
try {
|
|
7487
7598
|
const entries = await readdir4(dir, { withFileTypes: true });
|
|
7599
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
7488
7600
|
for (const entry of entries) {
|
|
7489
|
-
if (
|
|
7601
|
+
if (EXCLUDED_DIRECTORIES.has(entry.name))
|
|
7490
7602
|
continue;
|
|
7491
7603
|
const fullPath = join6(dir, entry.name);
|
|
7492
7604
|
if (entry.isDirectory()) {
|
|
7493
7605
|
await scan(fullPath);
|
|
7494
|
-
} else if (entry.
|
|
7606
|
+
} else if (entry.isFile() && INSTRUCTION_FILES.has(entry.name)) {
|
|
7495
7607
|
results.push(fullPath);
|
|
7496
7608
|
}
|
|
7497
7609
|
}
|
|
7498
7610
|
} catch {}
|
|
7499
7611
|
};
|
|
7500
7612
|
await scan(rootDir);
|
|
7501
|
-
return results;
|
|
7613
|
+
return results.sort((left, right) => left.localeCompare(right));
|
|
7502
7614
|
}
|
|
7503
7615
|
}
|
|
7504
7616
|
|
|
@@ -8612,6 +8724,7 @@ class AgentBuilder {
|
|
|
8612
8724
|
compactThreshold;
|
|
8613
8725
|
enabledTools = null;
|
|
8614
8726
|
enabledSkills = null;
|
|
8727
|
+
semanticSkillMatching = true;
|
|
8615
8728
|
withWorkingDirectory(path) {
|
|
8616
8729
|
this.workingDirectory = path;
|
|
8617
8730
|
return this;
|
|
@@ -8699,6 +8812,10 @@ class AgentBuilder {
|
|
|
8699
8812
|
this.enabledSkills = skillNames.length > 0 ? skillNames : null;
|
|
8700
8813
|
return this;
|
|
8701
8814
|
}
|
|
8815
|
+
withSemanticSkillMatching(enabled) {
|
|
8816
|
+
this.semanticSkillMatching = enabled;
|
|
8817
|
+
return this;
|
|
8818
|
+
}
|
|
8702
8819
|
async build() {
|
|
8703
8820
|
if (!this.modelProvider) {
|
|
8704
8821
|
throw new Error("Model provider is required. Call withModelProvider() or withModel()");
|
|
@@ -8706,6 +8823,7 @@ class AgentBuilder {
|
|
|
8706
8823
|
const remoteToolStore = this.remoteToolStore;
|
|
8707
8824
|
const projectId = this.projectId;
|
|
8708
8825
|
this.skillRegistry.setModelProvider(this.modelProvider);
|
|
8826
|
+
this.skillRegistry.setSemanticMatchingEnabled(this.semanticSkillMatching);
|
|
8709
8827
|
const memoryStore = this.memoryStore ?? new FileSystemMemoryStore(this.workingDirectory);
|
|
8710
8828
|
const toolRegistry = this.createToolRegistry(remoteToolStore, projectId);
|
|
8711
8829
|
this.toolRegistry = toolRegistry;
|
|
@@ -8753,6 +8871,7 @@ class AgentBuilder {
|
|
|
8753
8871
|
const remoteToolStore = this.remoteToolStore;
|
|
8754
8872
|
const projectId = this.projectId;
|
|
8755
8873
|
this.skillRegistry.setModelProvider(this.modelProvider);
|
|
8874
|
+
this.skillRegistry.setSemanticMatchingEnabled(this.semanticSkillMatching);
|
|
8756
8875
|
const memoryStore = this.memoryStore ?? new FileSystemMemoryStore(this.workingDirectory);
|
|
8757
8876
|
const toolRegistry = this.createToolRegistry(remoteToolStore, projectId);
|
|
8758
8877
|
this.toolRegistry = toolRegistry;
|
|
@@ -8845,7 +8964,7 @@ class MCPClient {
|
|
|
8845
8964
|
if (!config.command) {
|
|
8846
8965
|
return Promise.reject(new Error(`stdio 服务器 "${config.id}" 缺少 command 字段`));
|
|
8847
8966
|
}
|
|
8848
|
-
return new Promise((
|
|
8967
|
+
return new Promise((resolve9, reject) => {
|
|
8849
8968
|
const proc = spawn2(config.command, config.args ?? [], {
|
|
8850
8969
|
env: { ...process.env, ...config.env },
|
|
8851
8970
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8884,7 +9003,7 @@ class MCPClient {
|
|
|
8884
9003
|
clientInfo: { name: "customer-agent", version: "0.1.0" }
|
|
8885
9004
|
}).then(() => {
|
|
8886
9005
|
this._connected = true;
|
|
8887
|
-
|
|
9006
|
+
resolve9();
|
|
8888
9007
|
}).catch(reject);
|
|
8889
9008
|
});
|
|
8890
9009
|
}
|
|
@@ -8934,7 +9053,7 @@ class MCPClient {
|
|
|
8934
9053
|
if (this.config?.transport === "streamableHttp") {
|
|
8935
9054
|
return this.sendRequestHttp(method, params);
|
|
8936
9055
|
}
|
|
8937
|
-
return new Promise((
|
|
9056
|
+
return new Promise((resolve9, reject) => {
|
|
8938
9057
|
if (!this.process || !this.process.stdin) {
|
|
8939
9058
|
reject(new Error("MCP client not connected"));
|
|
8940
9059
|
return;
|
|
@@ -8950,7 +9069,7 @@ class MCPClient {
|
|
|
8950
9069
|
if (response.error) {
|
|
8951
9070
|
reject(new Error(response.error.message));
|
|
8952
9071
|
} else {
|
|
8953
|
-
|
|
9072
|
+
resolve9(response.result);
|
|
8954
9073
|
}
|
|
8955
9074
|
});
|
|
8956
9075
|
const timeout = setTimeout(() => {
|
|
@@ -8968,7 +9087,7 @@ class MCPClient {
|
|
|
8968
9087
|
});
|
|
8969
9088
|
}
|
|
8970
9089
|
sendRequestSSE(method, params) {
|
|
8971
|
-
return new Promise((
|
|
9090
|
+
return new Promise((resolve9, reject) => {
|
|
8972
9091
|
if (!this.ssePostUrl) {
|
|
8973
9092
|
reject(new Error("SSE post URL not established"));
|
|
8974
9093
|
return;
|
|
@@ -8981,7 +9100,7 @@ class MCPClient {
|
|
|
8981
9100
|
if (response.error)
|
|
8982
9101
|
reject(new Error(response.error.message));
|
|
8983
9102
|
else
|
|
8984
|
-
|
|
9103
|
+
resolve9(response.result);
|
|
8985
9104
|
});
|
|
8986
9105
|
const timeout = setTimeout(() => {
|
|
8987
9106
|
this.pendingRequests.delete(id);
|
|
@@ -9047,7 +9166,7 @@ class MCPClient {
|
|
|
9047
9166
|
this._connected = true;
|
|
9048
9167
|
}
|
|
9049
9168
|
sendRequestHttp(method, params) {
|
|
9050
|
-
return new Promise((
|
|
9169
|
+
return new Promise((resolve9, reject) => {
|
|
9051
9170
|
const urlStr = this.config?.url ?? this.config?.sseUrl;
|
|
9052
9171
|
if (!urlStr) {
|
|
9053
9172
|
reject(new Error("URL not set"));
|
|
@@ -9118,7 +9237,7 @@ class MCPClient {
|
|
|
9118
9237
|
if (resp.error)
|
|
9119
9238
|
reject(new Error(resp.error.message));
|
|
9120
9239
|
else
|
|
9121
|
-
|
|
9240
|
+
resolve9(resp.result);
|
|
9122
9241
|
}
|
|
9123
9242
|
} catch {}
|
|
9124
9243
|
evType = "";
|
|
@@ -9146,7 +9265,7 @@ class MCPClient {
|
|
|
9146
9265
|
if (resp.error)
|
|
9147
9266
|
reject(new Error(resp.error.message));
|
|
9148
9267
|
else
|
|
9149
|
-
|
|
9268
|
+
resolve9(resp.result);
|
|
9150
9269
|
} catch {
|
|
9151
9270
|
reject(new Error(`Invalid JSON response: ${data.slice(0, 100)}`));
|
|
9152
9271
|
}
|
|
@@ -9166,7 +9285,7 @@ class MCPClient {
|
|
|
9166
9285
|
});
|
|
9167
9286
|
}
|
|
9168
9287
|
connectSSE(config) {
|
|
9169
|
-
return new Promise((
|
|
9288
|
+
return new Promise((resolve9, reject) => {
|
|
9170
9289
|
const sseUrl = config.url ?? config.sseUrl;
|
|
9171
9290
|
if (!sseUrl) {
|
|
9172
9291
|
reject(new Error("SSE URL is required for remote MCP servers"));
|
|
@@ -9220,7 +9339,7 @@ class MCPClient {
|
|
|
9220
9339
|
clientInfo: { name: "customer-agent", version: "0.1.0" }
|
|
9221
9340
|
}).then(() => {
|
|
9222
9341
|
this._connected = true;
|
|
9223
|
-
|
|
9342
|
+
resolve9();
|
|
9224
9343
|
}).catch(reject);
|
|
9225
9344
|
}
|
|
9226
9345
|
} else if (eventType === "message" || eventType === "") {
|
|
@@ -10135,11 +10254,31 @@ class SQLiteSessionStore {
|
|
|
10135
10254
|
}
|
|
10136
10255
|
async create(session) {
|
|
10137
10256
|
const db = getDatabase(this.baseDir);
|
|
10138
|
-
const projectId = session.projectId
|
|
10139
|
-
const parentId = session.parentSessionId
|
|
10257
|
+
const projectId = this.resolveProjectId(session.projectId);
|
|
10258
|
+
const parentId = this.resolveParentId(session.parentSessionId);
|
|
10140
10259
|
db.db.prepare("INSERT INTO sessions (id, project_id, parent_id, title, status, created, updated, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(session.id, projectId, parentId, session.title, session.status, session.created, session.updated, JSON.stringify(session.metadata));
|
|
10141
10260
|
return session;
|
|
10142
10261
|
}
|
|
10262
|
+
resolveProjectId(projectId) {
|
|
10263
|
+
if (!projectId)
|
|
10264
|
+
return null;
|
|
10265
|
+
const db = getDatabase(this.baseDir);
|
|
10266
|
+
const exists = db.db.prepare("SELECT 1 FROM projects WHERE id = ?").get(projectId);
|
|
10267
|
+
if (exists)
|
|
10268
|
+
return projectId;
|
|
10269
|
+
console.warn(`[SQLiteSessionStore] project_id "${projectId}" not found — storing session without project (FK-safe).`);
|
|
10270
|
+
return null;
|
|
10271
|
+
}
|
|
10272
|
+
resolveParentId(parentId) {
|
|
10273
|
+
if (!parentId)
|
|
10274
|
+
return null;
|
|
10275
|
+
const db = getDatabase(this.baseDir);
|
|
10276
|
+
const exists = db.db.prepare("SELECT 1 FROM sessions WHERE id = ?").get(parentId);
|
|
10277
|
+
if (exists)
|
|
10278
|
+
return parentId;
|
|
10279
|
+
console.warn(`[SQLiteSessionStore] parent_id "${parentId}" not found — storing session as root (FK-safe).`);
|
|
10280
|
+
return null;
|
|
10281
|
+
}
|
|
10143
10282
|
async get(id) {
|
|
10144
10283
|
const db = getDatabase(this.baseDir);
|
|
10145
10284
|
const row = db.db.prepare("SELECT * FROM sessions WHERE id = ?").get(id);
|
|
@@ -10153,7 +10292,7 @@ class SQLiteSessionStore {
|
|
|
10153
10292
|
if (!existing)
|
|
10154
10293
|
throw new Error(`Session not found: ${id}`);
|
|
10155
10294
|
const merged = { ...existing, ...update, updated: new Date().toISOString() };
|
|
10156
|
-
db.db.prepare("UPDATE sessions SET project_id=?, title=?, status=?, updated=?, metadata=? WHERE id=?").run(merged.projectId, merged.title, merged.status, merged.updated, JSON.stringify(merged.metadata), id);
|
|
10295
|
+
db.db.prepare("UPDATE sessions SET project_id=?, title=?, status=?, updated=?, metadata=? WHERE id=?").run(this.resolveProjectId(merged.projectId), merged.title, merged.status, merged.updated, JSON.stringify(merged.metadata), id);
|
|
10157
10296
|
return merged;
|
|
10158
10297
|
}
|
|
10159
10298
|
async delete(id) {
|
|
@@ -10547,7 +10686,7 @@ class SQLiteProjectStore {
|
|
|
10547
10686
|
}
|
|
10548
10687
|
async delete(id) {
|
|
10549
10688
|
const db = getDatabase(this.baseDir);
|
|
10550
|
-
db.db.prepare("UPDATE sessions SET project_id =
|
|
10689
|
+
db.db.prepare("UPDATE sessions SET project_id = NULL WHERE project_id = ?").run(id);
|
|
10551
10690
|
db.db.prepare("DELETE FROM projects WHERE id = ?").run(id);
|
|
10552
10691
|
}
|
|
10553
10692
|
async list() {
|
|
@@ -10898,4 +11037,4 @@ export {
|
|
|
10898
11037
|
AgentBuilder
|
|
10899
11038
|
};
|
|
10900
11039
|
|
|
10901
|
-
//# debugId=
|
|
11040
|
+
//# debugId=05A3BC3FF39651A264756E2164756E21
|