@livx.cc/agentx 0.98.1 → 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/dist/index.d.ts CHANGED
@@ -467,14 +467,27 @@ interface WebFetchOptions {
467
467
  declare function makeWebFetchTool(options?: WebFetchOptions): AgentTool;
468
468
  interface WebSearchOptions {
469
469
  fetch?: typeof globalThis.fetch;
470
- /** Provider: 'auto' (default) uses Tavily if an API key is present, else keyless DuckDuckGo. */
471
- provider?: 'auto' | 'tavily' | 'duckduckgo';
470
+ /** Provider: 'auto' (default) prefers Firecrawl, then Tavily (by key presence), else keyless DuckDuckGo.
471
+ * 'anthropic' delegates to a cheap model with Anthropic's native server-side web_search. */
472
+ provider?: 'auto' | 'tavily' | 'duckduckgo' | 'anthropic' | 'firecrawl';
472
473
  /** API key for Tavily (default: process.env.TAVILY_API_KEY). */
473
474
  apiKey?: string;
474
475
  /** Tavily endpoint override. */
475
476
  endpoint?: string;
477
+ /** API key for Firecrawl (default: process.env.FIRECRAWL_API_KEY). */
478
+ firecrawlApiKey?: string;
479
+ /** Firecrawl search endpoint override. */
480
+ firecrawlEndpoint?: string;
476
481
  maxResults?: number;
477
482
  timeoutMs?: number;
483
+ /** Tool name (default 'WebSearch'). Set to register a provider-pinned variant, e.g. 'WebSearchAnthropic'. */
484
+ name?: string;
485
+ /** Tool description override (default: the generic ranked-results blurb). */
486
+ description?: string;
487
+ /** Anthropic key for provider 'anthropic' (default: process.env.ANTHROPIC_API_KEY). */
488
+ anthropicApiKey?: string;
489
+ /** Model for provider 'anthropic' (default 'claude-haiku-4-5'). */
490
+ model?: string;
478
491
  }
479
492
  interface SearchHit {
480
493
  title: string;
@@ -486,13 +499,24 @@ declare function decodeDdgUrl(href: string): string;
486
499
  /** Parse DuckDuckGo's HTML results page into hits (title/url/snippet) — dependency-free, zips anchors to snippets in order. */
487
500
  declare function parseDdgHtml(html: string, max: number): SearchHit[];
488
501
  /**
489
- * Build a WebSearch tool. Keyless by default (DuckDuckGo HTML) so it works in any deployment with
490
- * no setup; auto-upgrades to Tavily (better, agent-oriented results) when TAVILY_API_KEY is present.
502
+ * Build a WebSearch tool. Keyless by default (DuckDuckGo HTML) so it works in any deployment with no
503
+ * setup; on provider 'auto' it upgrades to an agent-oriented API by key presence — Firecrawl
504
+ * (FIRECRAWL_API_KEY) preferred, then Tavily (TAVILY_API_KEY). Provider 'anthropic' is a separate path
505
+ * (native search via a cheap model) exposed as the WebSearchAnthropic tool.
491
506
  */
492
507
  declare function makeWebSearchTool(options?: WebSearchOptions): AgentTool;
493
508
  /** Default instances (registered in the tool registry; opt-in by name). */
494
509
  declare const webFetchTool: AgentTool;
495
510
  declare const webSearchTool: AgentTool;
511
+ /** Anthropic-native search as a provider-pinned WebSearch variant. The credential is API-key billing
512
+ * (x-api-key); pass `anthropicApiKey` to inject a resolved key, else it falls back to ANTHROPIC_API_KEY. */
513
+ declare function makeWebSearchAnthropicTool(opts?: {
514
+ anthropicApiKey?: string;
515
+ model?: string;
516
+ }): AgentTool;
517
+ /** Env-keyed singleton: registered in the tool registry so 'WebSearchAnthropic' resolves by name. The
518
+ * CLI default-on path builds its own instance with a config-resolved key (see cli/core.ts). */
519
+ declare const webSearchAnthropicTool: AgentTool;
496
520
 
497
521
  /**
498
522
  * Scratch — keep large tool/subagent outputs OUT of the main context, but queryable AS FILES.
@@ -952,6 +976,7 @@ declare class DuplexAgent {
952
976
  private turnDispatched;
953
977
  private turnBriefs;
954
978
  private spokeThisTurn;
979
+ private heldThisTurn;
955
980
  private nudging;
956
981
  private reflexBuf;
957
982
  private reflexForwarded;
@@ -1498,4 +1523,4 @@ declare function exitWorktree(): {
1498
1523
  reason?: string;
1499
1524
  } | null;
1500
1525
 
1501
- export { Agent, type AgentDef, AgentOptions, AgentTool, type AskOptions, type Attempt, type AudioSink, type AudioSource, type AuthProvider, BodDbFilesystem, CartesiaTTS, CartesiaTTSOptions, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type LoadMemoryOpts, MEMORY_PROMPT, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, type ReflectOptions, RunResult, SCRATCH_DIR, STT_SAMPLE_RATE, type ScheduledJob, type ScheduledJobSnapshot, Scheduler, type SchedulerOptions, Scratch, type ScratchOptions, ScriptedHostBridge, type SiblingResolver, type SkillInfo, SonioxSTT, SonioxSTTOptions, type SttLike, TTS_SAMPLE_RATE, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, type Trigger, type TriggerCron, type TriggerInterval, type TriggerOneOff, type TtsLike, UserQuestion, VOICE_MEMORY_PROMPT, VOICE_SYSTEM_PROMPT, VoiceEngine, VoiceEngineOptions, type VoiceState, type WebFetchOptions, type WebSearchOptions, type WorkerTier, type WorktreeInfo, type WorktreeSession, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, cleanupWorktree, compileSynthesizedTool, cronMatches, decodeDdgUrl, diskAgentOptions, enterWorktree, exitWorktree, expandCommand, expandEntry, expandTemplate, findGitRoot, forComponent, fullAgentOptions, getOrCreateWorktree, getWorktreeSession, globTool, grepTool, htmlToText, idfWeights, lessonCapture, loadAgents, loadCommands, loadInstructions, loadMemory, loadSkills, makeAskTool, makeScheduleTools, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchTool, mkdirp, multiEditTool, nextCronAfter, parseCron, parseDdgHtml, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, resolveAuth, rollbackTool, sandboxAgentOptions, scanCommands, scanSkills, slugify, tokenize, toolCall, topByRelevance, validateToolCode, validateWorktreeSlug, webFetchTool, webSearchTool, worktreeBranchName, writeFact, writeTool };
1526
+ export { Agent, type AgentDef, AgentOptions, AgentTool, type AskOptions, type Attempt, type AudioSink, type AudioSource, type AuthProvider, BodDbFilesystem, CartesiaTTS, CartesiaTTSOptions, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type LoadMemoryOpts, MEMORY_PROMPT, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, type ReflectOptions, RunResult, SCRATCH_DIR, STT_SAMPLE_RATE, type ScheduledJob, type ScheduledJobSnapshot, Scheduler, type SchedulerOptions, Scratch, type ScratchOptions, ScriptedHostBridge, type SiblingResolver, type SkillInfo, SonioxSTT, SonioxSTTOptions, type SttLike, TTS_SAMPLE_RATE, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, type Trigger, type TriggerCron, type TriggerInterval, type TriggerOneOff, type TtsLike, UserQuestion, VOICE_MEMORY_PROMPT, VOICE_SYSTEM_PROMPT, VoiceEngine, VoiceEngineOptions, type VoiceState, type WebFetchOptions, type WebSearchOptions, type WorkerTier, type WorktreeInfo, type WorktreeSession, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, cleanupWorktree, compileSynthesizedTool, cronMatches, decodeDdgUrl, diskAgentOptions, enterWorktree, exitWorktree, expandCommand, expandEntry, expandTemplate, findGitRoot, forComponent, fullAgentOptions, getOrCreateWorktree, getWorktreeSession, globTool, grepTool, htmlToText, idfWeights, lessonCapture, loadAgents, loadCommands, loadInstructions, loadMemory, loadSkills, makeAskTool, makeScheduleTools, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchAnthropicTool, makeWebSearchTool, mkdirp, multiEditTool, nextCronAfter, parseCron, parseDdgHtml, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, resolveAuth, rollbackTool, sandboxAgentOptions, scanCommands, scanSkills, slugify, tokenize, toolCall, topByRelevance, validateToolCode, validateWorktreeSlug, webFetchTool, webSearchAnthropicTool, webSearchTool, worktreeBranchName, writeFact, writeTool };
package/dist/index.js CHANGED
@@ -644,13 +644,48 @@ function formatHits(hits) {
644
644
  ${r.url}
645
645
  ${r.snippet.replace(/\s+/g, " ").slice(0, 240)}`).join("\n\n");
646
646
  }
647
+ async function firecrawlSearch(q, opts) {
648
+ const res = await opts.fetch(opts.endpoint, {
649
+ method: "POST",
650
+ signal: opts.signal,
651
+ headers: { authorization: `Bearer ${opts.key}`, "content-type": "application/json" },
652
+ body: JSON.stringify({ query: q, limit: opts.maxResults })
653
+ });
654
+ if (!res.ok) return `Error: Firecrawl search returned ${res.status} ${res.statusText}`;
655
+ const data = await res.json();
656
+ const results = Array.isArray(data?.data) ? data.data.slice(0, opts.maxResults) : [];
657
+ return formatHits(results.map((r) => ({ title: r.title ?? "(untitled)", url: r.url ?? "", snippet: String(r.description ?? r.markdown ?? "") })));
658
+ }
659
+ async function anthropicSearch(q, opts) {
660
+ const res = await opts.fetch("https://api.anthropic.com/v1/messages", {
661
+ method: "POST",
662
+ signal: opts.signal,
663
+ headers: { "x-api-key": opts.key, "anthropic-version": "2023-06-01", "content-type": "application/json" },
664
+ body: JSON.stringify({
665
+ model: opts.model,
666
+ max_tokens: 1024,
667
+ // Basic variant: Haiku-tier doesn't support the _20260209 dynamic-filtering variant (Opus 4.6+/Sonnet 4.6 only).
668
+ tools: [{ type: "web_search_20250305", name: "web_search", max_uses: 5 }],
669
+ messages: [{ role: "user", content: `Search the web for: ${q}
670
+
671
+ 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.` }]
672
+ })
673
+ });
674
+ if (!res.ok) return `Error: Anthropic search returned ${res.status} ${res.statusText}`;
675
+ const data = await res.json();
676
+ if (data?.stop_reason === "refusal") return "Error: Anthropic search refused the query";
677
+ let text = "";
678
+ for (const block of data?.content ?? []) if (block?.type === "text") text += block.text;
679
+ return text.trim() || "(no results)";
680
+ }
647
681
  function makeWebSearchTool(options = {}) {
648
682
  const tavilyEndpoint = options.endpoint ?? "https://api.tavily.com/search";
683
+ const firecrawlEndpoint = options.firecrawlEndpoint ?? "https://api.firecrawl.dev/v1/search";
649
684
  const maxResults = options.maxResults ?? 5;
650
685
  const timeoutMs = options.timeoutMs ?? 15e3;
651
686
  return {
652
- name: "WebSearch",
653
- 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.",
687
+ name: options.name ?? "WebSearch",
688
+ 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.",
654
689
  parameters: { type: "object", required: ["query"], properties: { query: { type: "string" } } },
655
690
  async run({ query }) {
656
691
  const doFetch = options.fetch ?? globalThis.fetch;
@@ -658,11 +693,22 @@ function makeWebSearchTool(options = {}) {
658
693
  const q = String(query ?? "").trim();
659
694
  if (!q) return "Error: empty query";
660
695
  const key = options.apiKey ?? process.env.TAVILY_API_KEY;
696
+ const fcKey = options.firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY;
661
697
  const provider = options.provider ?? "auto";
662
- const useTavily = provider === "tavily" || provider === "auto" && !!key;
698
+ const useFirecrawl = provider === "firecrawl" || provider === "auto" && !!fcKey;
699
+ const useTavily = provider === "tavily" || provider === "auto" && !useFirecrawl && !!key;
663
700
  const ctl = new AbortController();
664
701
  const timer = setTimeout(() => ctl.abort(), timeoutMs);
665
702
  try {
703
+ if (provider === "anthropic") {
704
+ const akey = options.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
705
+ if (!akey) return "Error: WebSearchAnthropic requires ANTHROPIC_API_KEY (set in env)";
706
+ return await anthropicSearch(q, { key: akey, model: options.model ?? "claude-haiku-4-5", fetch: doFetch, signal: ctl.signal });
707
+ }
708
+ if (useFirecrawl) {
709
+ if (!fcKey) return "Error: Firecrawl provider selected but FIRECRAWL_API_KEY is not set";
710
+ return await firecrawlSearch(q, { key: fcKey, endpoint: firecrawlEndpoint, maxResults, fetch: doFetch, signal: ctl.signal });
711
+ }
666
712
  if (useTavily) {
667
713
  if (!key) return "Error: Tavily provider selected but TAVILY_API_KEY is not set";
668
714
  const res2 = await doFetch(tavilyEndpoint, {
@@ -691,7 +737,10 @@ function makeWebSearchTool(options = {}) {
691
737
  }
692
738
  };
693
739
  }
694
- var log2, _dnsLookup, webFetchTool, webSearchTool;
740
+ function makeWebSearchAnthropicTool(opts = {}) {
741
+ return makeWebSearchTool({ provider: "anthropic", name: "WebSearchAnthropic", description: ANTHROPIC_SEARCH_DESC, anthropicApiKey: opts.anthropicApiKey, model: opts.model });
742
+ }
743
+ var log2, _dnsLookup, webFetchTool, webSearchTool, ANTHROPIC_SEARCH_DESC, webSearchAnthropicTool;
695
744
  var init_tools_web = __esm({
696
745
  "src/tools.web.ts"() {
697
746
  "use strict";
@@ -699,6 +748,8 @@ var init_tools_web = __esm({
699
748
  log2 = forComponent("web");
700
749
  webFetchTool = makeWebFetchTool();
701
750
  webSearchTool = makeWebSearchTool();
751
+ 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).";
752
+ webSearchAnthropicTool = makeWebSearchAnthropicTool();
702
753
  }
703
754
  });
704
755
 
@@ -924,7 +975,7 @@ function defaultTools() {
924
975
  return [bashTool, readTool, editTool];
925
976
  }
926
977
  function toolRegistry() {
927
- const all = [bashTool, readTool, editTool, grepTool, globTool, writeTool, multiEditTool, applyEditsTool, repoMapTool, reviewTool(), todoWriteTool, webFetchTool, webSearchTool];
978
+ const all = [bashTool, readTool, editTool, grepTool, globTool, writeTool, multiEditTool, applyEditsTool, repoMapTool, reviewTool(), todoWriteTool, webFetchTool, webSearchTool, webSearchAnthropicTool];
928
979
  return Object.fromEntries(all.map((t) => [t.name, t]));
929
980
  }
930
981
  function toolsByName(names) {
@@ -4983,6 +5034,8 @@ var DuplexAgent = class _DuplexAgent {
4983
5034
  // briefs dispatched this turn (detect identical re-dispatch)
4984
5035
  spokeThisTurn = false;
4985
5036
  // any non-empty text_delta streamed this turn
5037
+ heldThisTurn = false;
5038
+ // Hold called this turn → turn is INTENTIONALLY silent (suppress reflex text + no dead-air ack)
4986
5039
  nudging = false;
4987
5040
  // re-ack pass in flight: block ALL tools, prevent recursion
4988
5041
  reflexBuf = "";
@@ -5045,6 +5098,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
5045
5098
  confirm: host.confirm ? (p, m) => host.confirm(p, m) : void 0,
5046
5099
  notify: (ev) => {
5047
5100
  if (ev?.kind === "text_delta" && typeof ev.message === "string") {
5101
+ if (this.heldThisTurn) return;
5048
5102
  if (this.fabricationCut) return;
5049
5103
  const msg = ev.message;
5050
5104
  this.reflexBuf += msg;
@@ -5114,6 +5168,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
5114
5168
  this.turnDispatched = false;
5115
5169
  this.turnBriefs.clear();
5116
5170
  this.spokeThisTurn = false;
5171
+ this.heldThisTurn = false;
5117
5172
  this.reflexBuf = "";
5118
5173
  this.reflexForwarded = 0;
5119
5174
  this.fabricationCut = false;
@@ -5142,7 +5197,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
5142
5197
  * voice) and emits an empty `final`, so no text_delta ever streams. Both ship silence; both repair.
5143
5198
  * Requires a host: without one there's no stream to detect speech on (and no one to speak to). */
5144
5199
  get silentTurn() {
5145
- return !!this.options.host && !this.spokeThisTurn;
5200
+ return !!this.options.host && !this.spokeThisTurn && !this.heldThisTurn;
5146
5201
  }
5147
5202
  /** A turn that voiced nothing is dead air. Re-prompt the reflex ONCE so the LLM itself voices a short
5148
5203
  * line (no template). If it STILL says nothing, fall back to a minimal line so silence never ships.
@@ -5703,6 +5758,7 @@ Another agent just implemented the above. Independently check the CURRENT state
5703
5758
  }
5704
5759
  },
5705
5760
  run: async ({ filler }) => {
5761
+ this.heldThisTurn = true;
5706
5762
  if (filler) this.notify("hold_filler", String(filler));
5707
5763
  return "Holding \u2014 listening for the rest of the user's thought. Do not respond further this turn.";
5708
5764
  }
@@ -6812,6 +6868,7 @@ export {
6812
6868
  makeTaskBatchTool,
6813
6869
  makeTaskTool,
6814
6870
  makeWebFetchTool,
6871
+ makeWebSearchAnthropicTool,
6815
6872
  makeWebSearchTool,
6816
6873
  mcpToolToAgentTool,
6817
6874
  mcpToolsToAgentTools,
@@ -6845,6 +6902,7 @@ export {
6845
6902
  validateToolCode,
6846
6903
  validateWorktreeSlug,
6847
6904
  webFetchTool,
6905
+ webSearchAnthropicTool,
6848
6906
  webSearchTool,
6849
6907
  worktreeBranchName,
6850
6908
  writeFact,