Skip to content

feat(agent-tool-set)#31

Open
mattapperson wants to merge 4 commits intomainfrom
toolkits
Open

feat(agent-tool-set)#31
mattapperson wants to merge 4 commits intomainfrom
toolkits

Conversation

@mattapperson
Copy link
Copy Markdown
Collaborator

@mattapperson mattapperson commented Apr 20, 2026

Summary

  • Adds @openrouter/agent-tool-set — a new workspace package for the SDK's tool() / callModel APIs.
  • Adds a new activeTools?: readonly string[] option to callModel so inferTools() output can be spread directly into a request, with filtering applied before both API conversion and executor registration.
  • Predicates receive { state: ConversationState, context: TShared }. InferToolSet<T> is mapped to this SDK's streaming event shapes (ToolPreliminaryResultEvent / ToolResultEvent).

Public API (@openrouter/agent-tool-set)

  • createToolSet({ tools, mutable? }) — array input; duplicate names throw at construction. ServerToolBase entries are accepted and skipped (client-tool-only activation).
  • .tools getter (full, ordered).
  • .activate / .deactivate — string or array of names.
  • .activateWhen / .deactivateWhen — name+predicate or { name: predicate, ... } bulk form. Last-call-wins per tool.
  • .inferTools(input?){ tools: Tool[]; activeTools: string[] }.
  • .clone({ mutable? }).
  • Types: ActivationInput, ActivationPredicate, InferToolSet.

Agent changes

  • BaseCallModelInput gains activeTools?: readonly string[]; clientOnlyFields includes it.
  • callModel filters tools by activeTools (Set membership) before convertToolsToAPIFormat and before passing to ModelResult, so filtered tools are neither advertised to the model nor callable by the executor. Server tools bypass the filter (they have no name to match against).

Test plan

  • pnpm build — both packages compile
  • pnpm typecheck — strict mode clean
  • pnpm test — 268 unit tests pass (249 agent + 19 agent-tool-set)
    • packages/agent-tool-set/tests/unit/tool-set.test.ts — 19 cases covering construction, duplicate-name error, activate/deactivate (string + array), activateWhen/deactivateWhen (single + bulk), last-call-wins, immutable vs mutable, clone, input handling, and predicate typing
    • packages/agent/tests/unit/call-model-active-tools.test.ts — 3 cases verifying the outbound request body via a capturing HTTPClient
  • pnpm lint — Biome clean
  • pnpm changeset status — both packages bumped minor

Release

.changeset/agent-tool-set.md — minor bump for both @openrouter/agent-tool-set (0.1.0 initial) and @openrouter/agent (new activeTools option).

@mattapperson mattapperson changed the title feat(agent-tool-set): port ai-tool-set to @openrouter/agent-tool-set feat(agent-tool-set) Apr 20, 2026
@mattapperson mattapperson changed the base branch from turborepo-migration to main April 20, 2026 19:00
Copy link
Copy Markdown
Collaborator

@robert-j-y robert-j-y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a rebase onto the current turborepo-migration — PR #30 landed and widened Tool to ClientTool | ServerToolBase. After rebase, two sites break:

1. packages/agent/src/inner-loop/call-model.ts:102 — produces TS2339: Property 'function' does not exist on type 'Tool'. Property 'function' does not exist on type 'ServerToolBase'. at compile time, and TypeError: Cannot read properties of undefined (reading 'name') at runtime if any serverTool(...) is in the array. Change:

const filteredTools = activeSet ? tools?.filter((t) => activeSet.has(t.function.name)) : tools;

to:

const filteredTools = activeSet
  ? tools?.filter((t) => isServerTool(t) || activeSet.has(t.function.name))
  : tools;

(import isServerTool from ../lib/tool-types.js)

2. packages/agent-tool-set/src/tool-set.ts buildToolsMap (lines 32–42) — same root cause. Compile error doesn't surface yet only because agent-tool-set resolves @openrouter/agent through the stale compiled .d.ts; runtime t.function.name still throws on any server tool passed to createToolSet. Change:

for (const t of tools) {
  const name = t.function.name;
  if (map.has(name)) throw new Error(`Duplicate tool name: "${name}"`);
  map.set(name, t);
}

to:

for (const t of tools) {
  if (isServerTool(t)) continue;
  const name = t.function.name;
  if (map.has(name)) throw new Error(`Duplicate tool name: "${name}"`);
  map.set(name, t);
}

(import isServerTool from @openrouter/agent)

Server tools have no name to activate by, so skipping them keeps ToolSet client-tool-only while still allowing users to pass a mixed array through.

3. PR description: InferActiveTools and InferInactiveTools are listed under "Types:" but are not exported from packages/agent-tool-set/src/index.ts. Remove or add.

Adds a new workspace package with declarative activate/deactivate/
activateWhen/deactivateWhen for tools, with predicates that receive the
SDK's ConversationState and typed shared context. Also adds an
`activeTools?: readonly string[]` option to callModel so inferTools()
output can be spread directly into a request.

Port of ai-tool-set v1.0.0 (MIT (C) zirkelc).
Drops the record-mapping InferToolSet and its InferActiveTools /
InferInactiveTools aliases (faithful-port artifacts without true
partition narrowing). The streaming-events discriminated union takes
the InferToolSet name.
…er and buildToolsMap

Addresses review feedback on PR #31 after rebasing onto current main
(PR #30 widened `Tool` to `ClientTool | ServerToolBase`).

- call-model.ts: filter keeps server tools unconditionally; name matching
  only applies to client tools, preventing `t.function.name` access on
  `ServerToolBase`.
- tool-set.ts: `buildToolsMap` skips server tools since they have no
  name to activate by; `createToolSet` remains client-tool-only while
  accepting mixed arrays.
Copy link
Copy Markdown
Collaborator Author

@mattapperson mattapperson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two concerns on the new ToolSet surface worth a look before merge.

Comment thread packages/agent-tool-set/src/tool-set.ts
Comment thread packages/agent-tool-set/src/tool-set.ts Outdated
Addresses review feedback on PR #31:

- Server tools are no longer silently dropped. ToolSet now tracks the
  full ordered list separately from the client-tool name index, so
  `.tools` and `.inferTools()` return both client and server tools.
  Server tools are always active (no name to filter by) and never appear
  in the `activeTools` list returned by `inferTools()`.
- `createToolSet` now exposes the `TShared` generic
  (`createToolSet<T, TShared>`), so predicates type `context` as the
  user's context shape instead of `Record<string, unknown>`.
Copy link
Copy Markdown
Collaborator Author

@mattapperson mattapperson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed, no issues found

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants