@livx.cc/agentx 0.98.2 → 0.99.1
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 +2 -2
- package/dist/cli.js +66 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +29 -5
- package/dist/index.js +58 -5
- package/dist/index.js.map +1 -1
- package/dist/tools.shell.js +54 -3
- package/dist/tools.shell.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Claude Code is the floor; running isolated, on the edge, or hybrid is the ceilin
|
|
|
35
35
|
Plus things Claude Code simply doesn't do:
|
|
36
36
|
|
|
37
37
|
- **Runs where CC can't** — the *same* agent loop runs on real disk, an in-memory **sandbox**, the **browser/edge** (no Node, no `/bin/sh`), or a **database-backed** workspace. Swap the filesystem, not the agent.
|
|
38
|
-
- **Keyless web search, built in** — `WebSearch` works in any deployment with no API key (DuckDuckGo; auto-upgrades to Tavily if you set
|
|
38
|
+
- **Keyless web search, built in** — `WebSearch` works in any deployment with no API key (DuckDuckGo; auto-upgrades to Firecrawl or Tavily if you set a key). Plus optional `WebSearchAnthropic` — Anthropic's native search via a cheap model, on by default when an Anthropic key is set (API-billed; opt out with `anthropicWebSearch: false`).
|
|
39
39
|
- **Context-safe by default** — a 1 MB `Grep`/`Read`/MCP result is auto-paginated and can't blow the window; buried detail is recovered via a cheap context-isolated `Ask` peek — **~5.3× cheaper and more accurate** than re-fetching, in a head-to-head.
|
|
40
40
|
- **It improves its own efficiency** — an autonomous evolution loop cut its own tool-use **~50% (32 → 15** on the core suite, denoised), self-discovered, not hand-tuned — the same lever behind the efficiency lead above.
|
|
41
41
|
|
|
@@ -77,7 +77,7 @@ console.log(res.finishReason, await fs.readFile('/src/x.ts'));
|
|
|
77
77
|
- **`Edit`** — exact unique-substring replace, with a read-before-edit staleness guard.
|
|
78
78
|
- **`Grep`/`Glob`/`Write`/`MultiEdit`** — structured, typed results straight from the VFS (no `bash` parsing). The selectable tool set the self-evolution loop mutates over.
|
|
79
79
|
- **`TodoWrite`** — a planning scratchpad; **`Task`** — spawn a depth-limited child agent over the VFS (`subagents: true`); **`SlashCommand`** — reusable prompt templates from `<dir>/*.md` (`commandsDir`); plus a real **MCP client** (`src/mcp.client.ts`, node-only — stdio/HTTP JSON-RPC handshake + discovery) that feeds the edge-safe **MCP adapter** (`mcpToolsToAgentTools`), so any MCP server's tools become agent tools.
|
|
80
|
-
- **`WebFetch`/`WebSearch`** — fetch a URL as readable text, or search the web. **Keyless by default** (WebSearch uses DuckDuckGo; auto
|
|
80
|
+
- **`WebFetch`/`WebSearch`** — fetch a URL as readable text, or search the web. **Keyless by default** (WebSearch uses DuckDuckGo; `provider: 'auto'` upgrades by key presence — Firecrawl `FIRECRAWL_API_KEY` > Tavily `TAVILY_API_KEY`) and **auto-enabled in the CLI**. Factory-built with an injectable `fetch`, so they stay edge-portable and testable. (In the library they're opt-in by name: `tools: [...,'WebSearch']`.) **`WebSearchAnthropic`** is a separate provider-pinned tool — Anthropic's native search relayed through a cheap model (`claude-haiku-4-5`); CLI-default-on when an Anthropic key resolves, API-billed per call, opt out via `anthropicWebSearch: false`.
|
|
81
81
|
- **Oversized-output pagination** — any tool result over a byte ceiling (`maxToolResultBytes`, default 60k) is cropped to page 1 with a marker (refine the query / read further), so one big `Grep`/`Read`/MCP/web result can't blow the context window. In the CLI (**on by default**; `--no-scratch` to disable) the full output instead spills **losslessly** to a **scratch** file and the model recovers specifics via `Grep`/`Read` or **`Ask`** — a cheap, context-isolated peek that returns just the answer (the raw blob never re-enters context).
|
|
82
82
|
|
|
83
83
|
## Agentic subsystems
|
package/dist/cli.js
CHANGED
|
@@ -645,13 +645,48 @@ function formatHits(hits) {
|
|
|
645
645
|
${r.url}
|
|
646
646
|
${r.snippet.replace(/\s+/g, " ").slice(0, 240)}`).join("\n\n");
|
|
647
647
|
}
|
|
648
|
+
async function firecrawlSearch(q2, opts) {
|
|
649
|
+
const res = await opts.fetch(opts.endpoint, {
|
|
650
|
+
method: "POST",
|
|
651
|
+
signal: opts.signal,
|
|
652
|
+
headers: { authorization: `Bearer ${opts.key}`, "content-type": "application/json" },
|
|
653
|
+
body: JSON.stringify({ query: q2, limit: opts.maxResults })
|
|
654
|
+
});
|
|
655
|
+
if (!res.ok) return `Error: Firecrawl search returned ${res.status} ${res.statusText}`;
|
|
656
|
+
const data = await res.json();
|
|
657
|
+
const results = Array.isArray(data?.data) ? data.data.slice(0, opts.maxResults) : [];
|
|
658
|
+
return formatHits(results.map((r) => ({ title: r.title ?? "(untitled)", url: r.url ?? "", snippet: String(r.description ?? r.markdown ?? "") })));
|
|
659
|
+
}
|
|
660
|
+
async function anthropicSearch(q2, opts) {
|
|
661
|
+
const res = await opts.fetch("https://api.anthropic.com/v1/messages", {
|
|
662
|
+
method: "POST",
|
|
663
|
+
signal: opts.signal,
|
|
664
|
+
headers: { "x-api-key": opts.key, "anthropic-version": "2023-06-01", "content-type": "application/json" },
|
|
665
|
+
body: JSON.stringify({
|
|
666
|
+
model: opts.model,
|
|
667
|
+
max_tokens: 1024,
|
|
668
|
+
// Basic variant: Haiku-tier doesn't support the _20260209 dynamic-filtering variant (Opus 4.6+/Sonnet 4.6 only).
|
|
669
|
+
tools: [{ type: "web_search_20250305", name: "web_search", max_uses: 5 }],
|
|
670
|
+
messages: [{ role: "user", content: `Search the web for: ${q2}
|
|
671
|
+
|
|
672
|
+
Return only the relevant findings as concise bullet points, each with its source URL in parentheses. Do not add a preamble, conclusion, opinion, or commentary. If sources conflict, list each claim with its source rather than resolving it.` }]
|
|
673
|
+
})
|
|
674
|
+
});
|
|
675
|
+
if (!res.ok) return `Error: Anthropic search returned ${res.status} ${res.statusText}`;
|
|
676
|
+
const data = await res.json();
|
|
677
|
+
if (data?.stop_reason === "refusal") return "Error: Anthropic search refused the query";
|
|
678
|
+
let text = "";
|
|
679
|
+
for (const block of data?.content ?? []) if (block?.type === "text") text += block.text;
|
|
680
|
+
return text.trim() || "(no results)";
|
|
681
|
+
}
|
|
648
682
|
function makeWebSearchTool(options = {}) {
|
|
649
683
|
const tavilyEndpoint = options.endpoint ?? "https://api.tavily.com/search";
|
|
684
|
+
const firecrawlEndpoint = options.firecrawlEndpoint ?? "https://api.firecrawl.dev/v1/search";
|
|
650
685
|
const maxResults = options.maxResults ?? 5;
|
|
651
686
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
652
687
|
return {
|
|
653
|
-
name: "WebSearch",
|
|
654
|
-
description: "Search the web by query; returns ranked results (title, URL, snippet). Use to look things up, find pages, or research a topic \u2014 then WebFetch a result URL to read it in full.",
|
|
688
|
+
name: options.name ?? "WebSearch",
|
|
689
|
+
description: options.description ?? "Search the web by query; returns ranked results (title, URL, snippet). Use to look things up, find pages, or research a topic \u2014 then WebFetch a result URL to read it in full.",
|
|
655
690
|
parameters: { type: "object", required: ["query"], properties: { query: { type: "string" } } },
|
|
656
691
|
async run({ query }) {
|
|
657
692
|
const doFetch = options.fetch ?? globalThis.fetch;
|
|
@@ -659,11 +694,22 @@ function makeWebSearchTool(options = {}) {
|
|
|
659
694
|
const q2 = String(query ?? "").trim();
|
|
660
695
|
if (!q2) return "Error: empty query";
|
|
661
696
|
const key = options.apiKey ?? process.env.TAVILY_API_KEY;
|
|
697
|
+
const fcKey = options.firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY;
|
|
662
698
|
const provider = options.provider ?? "auto";
|
|
663
|
-
const
|
|
699
|
+
const useFirecrawl = provider === "firecrawl" || provider === "auto" && !!fcKey;
|
|
700
|
+
const useTavily = provider === "tavily" || provider === "auto" && !useFirecrawl && !!key;
|
|
664
701
|
const ctl = new AbortController();
|
|
665
702
|
const timer = setTimeout(() => ctl.abort(), timeoutMs);
|
|
666
703
|
try {
|
|
704
|
+
if (provider === "anthropic") {
|
|
705
|
+
const akey = options.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
706
|
+
if (!akey) return "Error: WebSearchAnthropic requires ANTHROPIC_API_KEY (set in env)";
|
|
707
|
+
return await anthropicSearch(q2, { key: akey, model: options.model ?? "claude-haiku-4-5", fetch: doFetch, signal: ctl.signal });
|
|
708
|
+
}
|
|
709
|
+
if (useFirecrawl) {
|
|
710
|
+
if (!fcKey) return "Error: Firecrawl provider selected but FIRECRAWL_API_KEY is not set";
|
|
711
|
+
return await firecrawlSearch(q2, { key: fcKey, endpoint: firecrawlEndpoint, maxResults, fetch: doFetch, signal: ctl.signal });
|
|
712
|
+
}
|
|
667
713
|
if (useTavily) {
|
|
668
714
|
if (!key) return "Error: Tavily provider selected but TAVILY_API_KEY is not set";
|
|
669
715
|
const res2 = await doFetch(tavilyEndpoint, {
|
|
@@ -692,7 +738,10 @@ function makeWebSearchTool(options = {}) {
|
|
|
692
738
|
}
|
|
693
739
|
};
|
|
694
740
|
}
|
|
695
|
-
|
|
741
|
+
function makeWebSearchAnthropicTool(opts = {}) {
|
|
742
|
+
return makeWebSearchTool({ provider: "anthropic", name: "WebSearchAnthropic", description: ANTHROPIC_SEARCH_DESC, anthropicApiKey: opts.anthropicApiKey, model: opts.model });
|
|
743
|
+
}
|
|
744
|
+
var log2, _dnsLookup, webFetchTool, webSearchTool, ANTHROPIC_SEARCH_DESC, webSearchAnthropicTool;
|
|
696
745
|
var init_tools_web = __esm({
|
|
697
746
|
"src/tools.web.ts"() {
|
|
698
747
|
"use strict";
|
|
@@ -700,6 +749,8 @@ var init_tools_web = __esm({
|
|
|
700
749
|
log2 = forComponent("web");
|
|
701
750
|
webFetchTool = makeWebFetchTool();
|
|
702
751
|
webSearchTool = makeWebSearchTool();
|
|
752
|
+
ANTHROPIC_SEARCH_DESC = "High-quality web search via Anthropic's native search index. Returns concise, sourced findings \u2014 one claim per line, each with its source URL. Prefer this over WebSearch when accuracy and citations matter; it is slower (~3\u20138s) and bills the Anthropic API account per call (one cheap-model turn + search fee).";
|
|
753
|
+
webSearchAnthropicTool = makeWebSearchAnthropicTool();
|
|
703
754
|
}
|
|
704
755
|
});
|
|
705
756
|
|
|
@@ -925,7 +976,7 @@ function defaultTools() {
|
|
|
925
976
|
return [bashTool, readTool, editTool];
|
|
926
977
|
}
|
|
927
978
|
function toolRegistry() {
|
|
928
|
-
const all = [bashTool, readTool, editTool, grepTool, globTool, writeTool, multiEditTool, applyEditsTool, repoMapTool, reviewTool(), todoWriteTool, webFetchTool, webSearchTool];
|
|
979
|
+
const all = [bashTool, readTool, editTool, grepTool, globTool, writeTool, multiEditTool, applyEditsTool, repoMapTool, reviewTool(), todoWriteTool, webFetchTool, webSearchTool, webSearchAnthropicTool];
|
|
929
980
|
return Object.fromEntries(all.map((t) => [t.name, t]));
|
|
930
981
|
}
|
|
931
982
|
function toolsByName(names) {
|
|
@@ -7413,8 +7464,12 @@ The filesystem root '/' is the real machine root \u2014 you have full filesystem
|
|
|
7413
7464
|
return { systemPrompt: basePrompt + "\n\n" + extra };
|
|
7414
7465
|
})(),
|
|
7415
7466
|
tools: (() => {
|
|
7416
|
-
const
|
|
7467
|
+
const requested = o.tools ?? DEFAULT_TOOLS;
|
|
7468
|
+
const base = toolsByName([...requested, ...autoWebTools()]);
|
|
7417
7469
|
const tail = [...o.extraTools ?? []];
|
|
7470
|
+
if (o.anthropicWebSearch !== false && o.anthropicKey && !requested.includes("WebSearchAnthropic")) {
|
|
7471
|
+
tail.push(makeWebSearchAnthropicTool({ anthropicApiKey: o.anthropicKey }));
|
|
7472
|
+
}
|
|
7418
7473
|
if (scratch) tail.push(makeAskTool({ fs, ai: o.ai, model: o.scratchAskModel ?? o.model ?? "anthropic/claude-sonnet-4-6", dir: scratchDir }));
|
|
7419
7474
|
tail.push(makeNotifyTool());
|
|
7420
7475
|
if (!realShell.length) return [...base, ...tail];
|
|
@@ -10810,7 +10865,7 @@ Providers: set any of ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GROQ
|
|
|
10810
10865
|
Env files: .env (CWD, bun auto-loads) > install-dir .env > ~/.agent/.env (user-wide).
|
|
10811
10866
|
Bodify secrets: set BODIFY_API_KEY + BODIFY_APP_ID (in ~/.agent/.env) to pull keys from a Bodify app.
|
|
10812
10867
|
Config: ./.agent/config.{ts,js,json} (project) or ~/.agent/config.* (user).
|
|
10813
|
-
export default { model, maxSteps, reasoning, permissionMode, editorMode, tools, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
10868
|
+
export default { model, maxSteps, reasoning, permissionMode, editorMode, tools, anthropicWebSearch, apiKeys, baseUrls, hooks, permissions, mcpServers, maxTokens,
|
|
10814
10869
|
timeoutMs, maxRepeats, maxToolCalls, keepToolOutputs, maxContextTokens,
|
|
10815
10870
|
learnFromMistakes, reflectOnFailure, budget: {\u2026} }
|
|
10816
10871
|
hooks: { preToolUse|postToolUse|onStop: [{ tool?, command, block? }] } \u2014 shell hooks
|
|
@@ -11410,7 +11465,10 @@ function optsFor(args, ai, cfg = {}, extraTools = []) {
|
|
|
11410
11465
|
learnFromMistakes: cfg.learnFromMistakes,
|
|
11411
11466
|
// Forwarded to cursor/* delegations for environment parity (chat-model providers ignore it).
|
|
11412
11467
|
// Raw config (pre-OAuth): unresolved-oauth http servers are skipped by the cursor mapper.
|
|
11413
|
-
mcpServers: cfg.mcpServers
|
|
11468
|
+
mcpServers: cfg.mcpServers,
|
|
11469
|
+
// Gates + powers default-on WebSearchAnthropic. Same precedence as the main client (env wins).
|
|
11470
|
+
anthropicKey: apiKeysFromEnv().anthropic ?? cfg.apiKeys?.anthropic,
|
|
11471
|
+
anthropicWebSearch: cfg.anthropicWebSearch
|
|
11414
11472
|
};
|
|
11415
11473
|
}
|
|
11416
11474
|
async function makeAgent(args, ai, cfg, extraTools = []) {
|