Skip to content

Commit d0b4bb3

Browse files
chore: resolve origin/main merge conflicts
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2 parents 8f2cb86 + e31b50e commit d0b4bb3

31 files changed

Lines changed: 869 additions & 154 deletions

‎.changeset/approval-gate-fixes.md‎

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
'@openrouter/agent': minor
3+
---
4+
5+
Allow manual tools to provide a caller-owned JSON Schema for wire serialization.
6+
7+
```ts
8+
import { tool } from '@openrouter/agent';
9+
import { z } from 'zod';
10+
11+
const confirmTool = tool({
12+
name: 'confirm_action',
13+
inputSchema: z.object({ action: z.string() }),
14+
wireInputSchema: {
15+
type: 'object',
16+
properties: {
17+
action: { type: 'string' },
18+
},
19+
required: ['action'],
20+
},
21+
execute: false,
22+
});
23+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openrouter/agent': major
3+
---
4+
5+
Stop accumulating generator-tool `preliminaryResults` arrays. Yields are still broadcast live; the terminal `tool.result` event and `ToolExecutionResult` no longer carry the full yield history.

‎.changeset/quiet-replay-streams.md‎

Lines changed: 0 additions & 16 deletions
This file was deleted.

‎.changeset/reusable-stream-subpath-export.md‎

Lines changed: 0 additions & 5 deletions
This file was deleted.

‎.changeset/tidy-agents-smile.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@openrouter/agent': patch
3+
---
4+
5+
Identify Agent SDK requests with an Agent SDK user-agent suffix that includes the package version.
6+
7+
```ts
8+
import { OpenRouter } from '@openrouter/agent';
9+
10+
const client = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
11+
// Requests use the Agent SDK user agent by default.
12+
// Pass userAgent to override the default identification.
13+
```

‎.changeset/tool-subpath-startup-cost.md‎

Lines changed: 0 additions & 7 deletions
This file was deleted.

‎packages/agent/CHANGELOG.md‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
# @openrouter/agent
22

3+
## 0.11.0
4+
5+
### Minor Changes
6+
7+
- [#109](https://github.com/OpenRouterTeam/typescript-agent/pull/109) [`17418f7`](https://github.com/OpenRouterTeam/typescript-agent/commit/17418f7c469e09efd9d61980315b9727b1d11ff6) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Add opt-in replay compaction and terminal response-event handling for streamed model calls.
8+
9+
```ts
10+
import { callModel } from "@openrouter/agent";
11+
12+
const result = callModel(client, {
13+
model: "openai/gpt-4o",
14+
input: "Summarize this document.",
15+
// Retain only the history needed by currently attached consumers.
16+
streamReplay: "active-consumers",
17+
});
18+
```
19+
20+
### Patch Changes
21+
22+
- [#94](https://github.com/OpenRouterTeam/typescript-agent/pull/94) [`c610b6e`](https://github.com/OpenRouterTeam/typescript-agent/commit/c610b6ef0b880083821afd588717d635943c07ee) Thanks [@LukasParke](https://github.com/LukasParke)! - Fix two ways the tool-approval gate could be bypassed.
23+
24+
**`allowFinalResponse` executed pending tool calls with no approval check.** When a `stopWhen` condition halted the loop on a turn that still carried tool calls, the final-response path ran those calls directly — skipping the approval gate the normal loop applies on every round. A tool marked `requireApproval: true` (or gated by a predicate) would execute unguarded, and because the `PermissionRequest` hook's deny bookkeeping lives inside the approval check, hook-based `deny` never fired on this path either. That path now runs the same check as the in-loop call sites, so the run pauses with `status: 'awaiting_approval'` and the gated calls on `pendingToolCalls` instead of executing them.
25+
26+
**Function-based `requireApproval` received unvalidated arguments.** Tool-level and call-level predicates were called with the raw JSON-parsed tool arguments, while `execute` receives the arguments _after_ the tool's Zod `inputSchema` runs. Any default, coercion, or transform in the schema made them disagree — e.g. with `inputSchema: z.object({ dangerous: z.boolean().default(true) })`, a model emitting `{}` showed a predicate `dangerous: undefined` (no approval required) and then executed with `dangerous: true`. Predicates now see a parsed copy, so they decide on exactly what `execute` will receive without mutating the original executable call or parsing transformed output a second time. `PreToolUse` now runs before every auto-resolvable call is partitioned, so approval hooks and persisted pending calls see its effective arguments. Pending calls record an additive marker when preparation ran, preventing a resumed `ModelResult` from applying the hook twice while legacy state without the marker retains its prior behavior. Call-level checks remain unconditional and receive raw arguments when parsing fails; tool-level checks fail closed when schema parsing fails because a hook may later repair the input.
27+
28+
**Duplicate approval prompts for the same tool call.** The approval gate could run more than once over the same response — e.g. the pre-loop check plus the post-loop `allowFinalResponse` gate when a stop condition fired on the first iteration — re-emitting the `PermissionRequest` hook and re-running `requireApproval` predicates for calls that were already resolved. Each call occurrence in a response is now gated at most once per run, including responses containing duplicate call IDs and arguments.
29+
30+
```ts
31+
import { z } from "zod/v4";
32+
import { tool, type PendingToolCall } from "@openrouter/agent";
33+
34+
const deploy = tool({
35+
name: "deploy",
36+
inputSchema: z.object({
37+
environment: z.enum(["staging", "production"]).default("production"),
38+
}),
39+
requireApproval: ({ environment }) => environment === "production",
40+
execute: async ({ environment }) => deployEnvironment(environment),
41+
});
42+
43+
// `requireApproval` sees the normalized default: { environment: 'production' }.
44+
// Persist this additive marker when PreToolUse already produced effective args.
45+
const pending: PendingToolCall<typeof deploy> = {
46+
id: "call_deploy",
47+
name: "deploy",
48+
arguments: { environment: "production" },
49+
preToolUseApplied: true,
50+
};
51+
```
52+
53+
- [#112](https://github.com/OpenRouterTeam/typescript-agent/pull/112) [`8a922b5`](https://github.com/OpenRouterTeam/typescript-agent/commit/8a922b5360addf6b5670c7fc4c87780f4fdfa071) Thanks [@LukasParke](https://github.com/LukasParke)! - Add a `./reusable-stream` subpath export so consumers can import `ReusableReadableStream` directly (`@openrouter/agent/reusable-stream`) instead of going through the root barrel or patching the package. Mirrors the existing `./tool-event-broadcaster` entry; both replay classes are the units consumers need when asserting stream-retention behavior against the published package.
54+
55+
- [#111](https://github.com/OpenRouterTeam/typescript-agent/pull/111) [`7416059`](https://github.com/OpenRouterTeam/typescript-agent/commit/7416059a3644af577ef2969b932a6614771e0c43) Thanks [@w0nche0l](https://github.com/w0nche0l)! - Remove the runtime `@openrouter/sdk/models` import from `turn-context.ts`. The namespace import existed only to read `EasyInputMessageRoleUser.User` (the string `'user'`), but it made every consumer that statically imports `@openrouter/agent/tool` (via `agent-tool` → `conversation-state` → `turn-context`) evaluate the entire Speakeasy models barrel — hundreds of modules of top-level Zod schema construction — at module load. On Cloudflare Workers this added ~200ms of startup CPU per worker and pushed large workers past the 1s script-validation ceiling (error 10021).
56+
57+
The import is now type-only (erased at compile time) and the role literal is inlined, keeping behavior identical. A new unit test walks the static runtime import graph of the hot subpaths (`/tool`, `/tool-types`, `/stop-conditions`) and fails if any of them ever reaches `@openrouter/sdk` at runtime again.
58+
359
## 0.10.0
460

561
### Minor Changes

‎packages/agent/README.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ const analysisTool = tool({
221221
const confirmTool = tool({
222222
name: 'confirm_action',
223223
inputSchema: z.object({ action: z.string() }),
224+
// Forward an existing JSON Schema without converting it through Zod.
225+
wireInputSchema: {
226+
type: 'object',
227+
properties: {
228+
action: { type: 'string' },
229+
},
230+
required: ['action'],
231+
},
224232
execute: false,
225233
});
226234
```

‎packages/agent/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openrouter/agent",
3-
"version": "0.10.0",
3+
"version": "0.11.0",
44
"author": "OpenRouter",
55
"description": "Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.",
66
"keywords": [

0 commit comments

Comments
 (0)