agent.libx.js 0.93.21 → 0.93.23

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 CHANGED
@@ -77,7 +77,7 @@ agentx --resume <id> "…" # resume a specific session
77
77
  - **@-file mentions & headless JSON** — reference files inline in a prompt with `@path` (e.g. `explain @src/Agent.ts`); script with `-p --output-format json` to get one machine-readable result object on stdout (activity stays on stderr).
78
78
  - **Tab-completion** — `Tab` completes `/<command>` names and `@<path>` file/dir references (descends subdirs, dotfiles hidden unless typed) straight from the working tree.
79
79
  - **Duplex mode** — `agentx --duplex` runs the full standard REPL (slash commands, sessions, postures, rewind, MCP) with the dual-model engine driving turns: a fast voice model (`--voice-model`, default haiku) answers every line instantly and delegates real work to background workers built with the same wiring as a normal run (fs mode, permissions, MCP); worker activity shows as dim chrome and results are re-voiced when ready.
80
- - **MCP servers** — declare `mcpServers: { name: { command, args } | { url } }` in config and they're auto-mounted at startup (in parallel, with an optional `mountTimeoutMs` deadline so one slow/dead server never blocks the rest): the client does the JSON-RPC handshake (stdio or HTTP) + `tools/list`, and the discovered tools appear as `mcp__<name>__<tool>` in `/tools` (inspect with `/mcp`). A bad server is logged and skipped, never blocking the agent. For large tool sets, **deferred mode** (`makeMcpToolSearch` / `mountMcpDeferred`) exposes just two bounded tools (`ToolSearch` + `McpCall`) instead of N defs — dodging the provider tool-cap and improving selection accuracy. **`mountMcpCatalog`** goes further: a cached, hash-keyed catalog + lazy connect means a turn that uses no MCP tool opens **zero** connections, and one that uses a tool connects exactly that server — latency scales with tools-used, not servers-configured.
80
+ - **MCP servers** — declare `mcpServers: { name: { command, args } | { url } }` in config and they're auto-mounted at startup (in parallel, with an optional `mountTimeoutMs` deadline so one slow/dead server never blocks the rest): the client does the JSON-RPC handshake (stdio or HTTP) + `tools/list`, and the discovered tools appear as `mcp__<name>__<tool>` in `/tools` (inspect with `/mcp`). A bad server is logged and skipped, never blocking the agent. For large tool sets, **deferred mode** (`makeMcpToolSearch` / `mountMcpDeferred`) exposes just two bounded tools (`ToolSearch` + `McpCall`) instead of N defs — dodging the provider tool-cap and improving selection accuracy. **`mountMcpCatalog`** goes further: a cached, hash-keyed catalog + lazy connect means a turn that uses no MCP tool opens **zero** connections, and one that uses a tool connects exactly that server — latency scales with tools-used, not servers-configured. A down server is **negative-cached** (`failureCooldownMs`) so it never re-floors a later turn at the deadline. For zero turn-path latency even on a cold process, call **`warmMcpCatalog`** at boot + on a timer (off-turn discovery) and mount with **`{ discover: 'cache-only' }`** — the turn then never synchronously connects: it serves the warmed catalog and discovers any miss in the background.
81
81
 
82
82
  ## 🧬 It improves itself
83
83
 
package/dist/cli.js CHANGED
@@ -3652,6 +3652,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
3652
3652
  this.turnDispatched = false;
3653
3653
  this.turnBriefs.clear();
3654
3654
  this.spokeThisTurn = false;
3655
+ this.voice.options.toolChoice = void 0;
3655
3656
  }
3656
3657
  /** preToolUse guard on the reflex: once it has dispatched this turn, a dispatch is "said my piece,
3657
3658
  * now wait for the push" (CC's Task model). Block the temptations — TaskStatus polling and identical
@@ -3964,6 +3965,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3964
3965
  run: async ({ brief, label }) => {
3965
3966
  this.turnDispatched = true;
3966
3967
  this.turnBriefs.add(String(brief ?? ""));
3968
+ this.voice.options.toolChoice = "none";
3967
3969
  const id = await this.dispatch(String(brief ?? ""), "act", label ? String(label) : void 0);
3968
3970
  return `Acting on task ${id}. Acknowledge briefly; the result will arrive as a [task ${id} completed] event.`;
3969
3971
  }
@@ -3984,6 +3986,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3984
3986
  run: async ({ brief, label }) => {
3985
3987
  this.turnDispatched = true;
3986
3988
  this.turnBriefs.add(String(brief ?? ""));
3989
+ this.voice.options.toolChoice = "none";
3987
3990
  const id = await this.dispatch(String(brief ?? ""), "think", label ? String(label) : void 0);
3988
3991
  return `Thinking on task ${id}. Acknowledge briefly; the result will arrive as a [task ${id} completed] event.`;
3989
3992
  }
@@ -4029,9 +4032,14 @@ Another agent just implemented the above. Independently check the CURRENT state
4029
4032
  const names = actTools.map((t) => t.name);
4030
4033
  if (!names.length)
4031
4034
  return "Your worker uses Act's default local toolset (reading/editing files, running shell commands). No extra tools (e.g. web/internet) are configured; if a request is not a basic file or shell operation, assume you can't do it and say so.";
4032
- const webish = names.some((n) => /WebFetch|browser/i.test(n));
4033
- const hasSearch = names.some((n) => /search/i.test(n) && !/WebFetch/i.test(n));
4034
- const webNote = webish && !hasSearch ? ` NOTE: WebFetch / browser tools act on a SPECIFIC URL you are given \u2014 they are NOT a search engine. You CANNOT search the web, Google a topic, or look up arbitrary/live info. If asked to "search the web", say plainly you can't \u2014 you can only fetch or open a URL they provide.` : "";
4035
+ const hasFetch = names.some((n) => /WebFetch/i.test(n));
4036
+ const hasBrowser = names.some((n) => /browser.*(navigate|click|page|type)/i.test(n));
4037
+ const hasSearch = names.some((n) => /(^|_)WebSearch$|search/i.test(n) && !/WebFetch|browser/i.test(n));
4038
+ const notes = [];
4039
+ if (hasFetch) notes.push("WebFetch retrieves ONE specific URL you are given \u2014 it is not a search engine.");
4040
+ if (hasBrowser) notes.push("The browser tools drive a real browser: you CAN open a site and, if needed, navigate to a search engine and search there \u2014 but it is manual and takes a moment, not an instant lookup.");
4041
+ else if (!hasSearch && hasFetch) notes.push('You have no general web-search tool, so for an instant "search the web" you can only fetch a URL they provide.');
4042
+ const webNote = notes.length ? " NOTE: " + notes.join(" ") : "";
4035
4043
  return `Tools your background worker (Act) can actually use: ${names.join(", ")}. Read each name literally and match the request to a SPECIFIC tool; if none fits, you do NOT have that ability \u2014 say so honestly.` + webNote;
4036
4044
  }
4037
4045
  case "time":