Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/agent-tool-set.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@openrouter/agent-tool-set": minor
"@openrouter/agent": minor
---

Add `@openrouter/agent-tool-set` (port of ai-tool-set v1.0.0, MIT © zirkelc): declarative activate / deactivate / activateWhen / deactivateWhen for tools with state- and context-aware predicates. Integrates with a new `activeTools?: readonly string[]` option on `callModel` that filters which tools are sent to the model for a given call.
70 changes: 70 additions & 0 deletions packages/agent-tool-set/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# @openrouter/agent-tool-set

Declarative, state-aware activation and deactivation for tools used with `@openrouter/agent`.

Port of [`ai-tool-set`](https://github.com/zirkelc/ai-tool-set) v1.0.0 (MIT © zirkelc), adapted for this SDK:

- Input is an ordered array of `Tool` (as used by `callModel`), not a name-keyed record.
- Predicates receive `{ state, context }` where `state` is the SDK's `ConversationState` and `context` is the typed shared context.
- Integrates with a new `activeTools` option on `callModel` — you can spread `inferTools()` directly into the request.

## Install

```bash
pnpm add @openrouter/agent-tool-set
```

## Usage

```ts
import { OpenRouter, tool, callModel } from '@openrouter/agent';
import { createToolSet } from '@openrouter/agent-tool-set';
import { z } from 'zod/v4';

type AppContext = {
isAuthenticated: boolean;
};

const listOrders = tool({
name: 'list_orders',
inputSchema: z.object({}),
execute: async () => ({ orders: [] }),
});

const cancelOrder = tool({
name: 'cancel_order',
inputSchema: z.object({ id: z.string() }),
execute: async () => ({ ok: true }),
});

const allTools = [listOrders, cancelOrder] as const;

const toolSet = createToolSet<typeof allTools, AppContext>({ tools: allTools })
.activateWhen('list_orders', ({ context }) => context?.isAuthenticated === true)
.deactivateWhen('cancel_order', ({ state }) => (state?.messages?.length ?? 0) === 0);

const client = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });

const { tools, activeTools } = toolSet.inferTools({ context: { isAuthenticated: true } });

const result = callModel(client, {
model: 'openai/gpt-4o-mini',
input: 'List my orders.',
tools,
activeTools,
});
```

## API

- `createToolSet<T, TShared?>({ tools, mutable? })` — build a set from an ordered tool array. Optional `TShared` generic types the `context` argument passed to predicates.
- `.tools` — all tools in construction order, regardless of activation. Includes both client tools and server tools.
- `.activate(name | names[])` / `.deactivate(name | names[])` — static flip (client tools only).
- `.activateWhen(name, predicate)` / `.activateWhen({ [name]: predicate, ... })` — conditional activation (defaults inactive).
- `.deactivateWhen(name, predicate)` / `.deactivateWhen({ [name]: predicate, ... })` — conditional deactivation (defaults active).
- `.inferTools(input?)` → `{ tools: Tool[]; activeTools: string[] }` — resolve against an input. Server tools (which have no `function.name`) are always included in `tools` and never appear in `activeTools`; only client tools participate in activation.
- `.clone({ mutable? })` — copy state, optionally flipping mode.

Last-call-wins: each directive on a given tool replaces any prior one for that tool.

Immutable by default (every mutator returns a new `ToolSet`). Pass `mutable: true` to mutate in place.
55 changes: 55 additions & 0 deletions packages/agent-tool-set/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@openrouter/agent-tool-set",
"version": "0.1.0",
"author": "OpenRouter",
"description": "Declarative activation/deactivation for @openrouter/agent tools. Port of ai-tool-set (MIT © zirkelc) adapted for callModel + tool().",
"keywords": [
"openrouter",
"agent",
"tools",
"toolset",
"typescript",
"ai"
],
"license": "Apache-2.0",
"type": "module",
"main": "./esm/index.js",
"exports": {
".": {
"types": "./esm/index.d.ts",
"default": "./esm/index.js"
},
"./package.json": "./package.json"
},
"sideEffects": false,
"repository": {
"type": "git",
"url": "https://github.com/OpenRouterTeam/typescript-agent.git",
"directory": "packages/agent-tool-set"
},
"publishConfig": {
"access": "public",
"provenance": true
},
"files": [
"esm",
"package.json",
"README.md"
],
"scripts": {
"lint": "biome check src tests",
"lint:fix": "biome check --write src tests",
"build": "tsc",
"test": "vitest --run --project unit",
"test:e2e": "vitest --run --project e2e",
"test:watch": "vitest --watch --project unit",
"typecheck": "tsc --noEmit",
"compile": "tsc"
},
"dependencies": {
"@openrouter/agent": "workspace:*"
},
"peerDependencies": {
"zod": "^4.0.0"
}
}
2 changes: 2 additions & 0 deletions packages/agent-tool-set/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { createToolSet, ToolSet } from './tool-set.js';
export type { ActivationInput, ActivationPredicate, InferToolSet } from './types.js';
Loading
Loading