Skip to content

feat(agent): pass function-tool strict through serialization - #99

Merged
LukasParke merged 4 commits into
mainfrom
devin/dev-786-agent-sdk-drops-strict-on-function-tools-serializer
Aug 6, 2026
Merged

feat(agent): pass function-tool strict through serialization#99
LukasParke merged 4 commits into
mainfrom
devin/dev-786-agent-sdk-drops-strict-on-function-tools-serializer

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes DEV-786: convertToolsToAPIFormat hardcoded strict: null on every function tool, silently dropping a caller's strict: true — so providers never schema-constrained tool-call argument generation (OpenAI structured-outputs style) on the root model loop.

  • strict?: boolean | null added to BaseToolFunction and to every tool() config shape (regular, generator, manual, HITL, shared-context, unified run); the factory copies it onto the built tool's function.
  • Serializer now threads it through:
 const apiTool: APITool = {
   type: 'function',
   name: tool.function.name,
   description: tool.function.description || null,
-  strict: null,
+  strict: tool.function.strict ?? null,
   parameters: convertZodToJsonSchema(tool.function.inputSchema),
 };

Unit tests pin both strict: true pass-through and the null default; changeset included (minor).

Note on the other DEV-786 bun-patch absorption items: the HITL toolCall threading into buildExecuteCtx and the invokeOnResponseReceived fix already exist on main (0.8.0), so this PR only adds the strict gap. Threading t.function.strict in openrouter-web's buildCallModelInput is a follow-up there after the SDK bump.

API example

import { tool } from '@openrouter/agent';
import { z } from 'zod/v4';

const searchTool = tool({
  name: 'search',
  inputSchema: z.object({ query: z.string() }),
  strict: true, // was: silently serialized as strict: null
  // now: serialized as strict: true on the wire tool definition
  execute: async ({ query }) => runSearch(query),
});

Link to Devin session: https://openrouter.devinenterprise.com/sessions/347b0632ce814dcebd12a6e5a70de122

Strict schema behavior

The SDK forwards the caller-generated schema unchanged and propagates provider validation errors. OpenAI-style strict mode requires every object property to be listed in required; use Zod .nullable() for conceptually optional values because .optional() allows the key to be omitted. The option is supported by regular, unified run, and tool.agent() builders.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author
Original prompt from dennis.jeong

can you pull the agent typescript sdk and implement this issue: https://linear.app/openrouter/issue/DEV-786/agent-sdk-drops-strict-on-function-tools-serializer-hardcodes-strict

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@w0nche0l
w0nche0l marked this pull request as ready for review August 4, 2026 04:24
@synapse-github-agent

Copy link
Copy Markdown
Contributor
  • Keep up to date — merge the base branch into this PR as it moves
  • Merge when ready — GitHub auto-merges once its required checks and approvals pass

cortex review — 2b2b27c

⚠️ APPROVE withheld by policy — PR author @devin-ai-integration[bot] is not a member of OpenRouterTeam (association: CONTRIBUTOR). Review posted as COMMENT; a maintainer must approve out-of-band.

Security · ✅ Experience (DX · UX · A11y) · ✅ Performance

Automatic first-pass review · updated in place on every push

@synapse-github-agent synapse-github-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ APPROVE withheld by policy — PR author @devin-ai-integration[bot] is not a member of OpenRouterTeam (association: CONTRIBUTOR). Review posted as COMMENT; a maintainer must approve out-of-band.

cortex panel verdict: comment — details in the consolidated review comment.

perry-the-pr-reviewer[bot]

This comment was marked as outdated.

@LukasParke LukasParke left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adversarial review of the strict pass-through. The core change is correct — strict flows through all six tool() config shapes, the serializer's ?? null preserves the prior default, and the built-in task tool's hardcoded strict: null remains intentional. Three gaps found (confirmed with maintainer):

Comment thread packages/agent/src/lib/tool.ts
Comment thread packages/agent/src/lib/tool-types.ts
Comment thread packages/agent/tests/unit/server-tool.test.ts
w0nche0l and others added 2 commits August 5, 2026 23:14
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
perry-the-pr-reviewer[bot]

This comment was marked as outdated.

Co-authored-by: Cursor <cursoragent@cursor.com>

@perry-the-pr-reviewer perry-the-pr-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Downgrade notice: Perry's maintainer app is not authorized to post APPROVE verdicts on OpenRouterTeam. This review is posted as COMMENT with the reviewer identity. A human reviewer should approve this PR — the assessment below supports approval.

Perry's Review

Verdict: ✅ LGTM

Risk: 🟢 Low

Estimated impact: 🟩 Low — callers who don't set strict see no change; those who do get an additive feature. Worst case: strict doesn't work for a specific provider dialect, surfaced as a provider error.

PR: OpenRouterTeam/typescript-agent#99feat(agent): pass function-tool strict through serialization

All five prior review threads are resolved, CI is green (lint, typecheck, unit-tests, e2e-tests, structural-gate), and the implementation is complete and consistent across every tool kind.

What changed. convertToolsToAPIFormat hardcoded strict: null on every function tool, silently dropping a caller's strict: true (DEV-786). This PR adds strict?: boolean | null to BaseToolFunction and every tool() config shape, copies it onto the built tool's function in all factory branches, and serializes it via tool.function.strict ?? null — preserving the existing default while passing through true and false.

Coverage of all tool kinds verified:

  • Regular tools — spread syntax in tool.ts (line 742)
  • Generator tools — explicit if (config.strict !== undefined) (line 703)
  • Manual tools — same pattern (line 645)
  • HITL tools — same pattern (line 586)
  • Unified run tools — assignCommonToolFields (line 846)
  • Agent tools — optionalFields loop in agentToolBuilder (line 337)

Design decision on preflight. An earlier revision added schema preflight validation for strict tools; commit 25df967 removed it in favor of faithful passthrough. This is the correct call — the SDK forwards the caller's Zod schema unchanged and lets the provider validate its own strict dialect, propagating errors unchanged. The README and JSDoc guide users toward .nullable() over .optional() for strict tools.

Test coverage. Five new tests pin strict: true (regular, unified run, agent), strict: false (not coerced to null — guards the ?? vs || distinction), and the null default. One inline suggestion below asks for a test covering the generator/manual/HITL copy path.

Risk assessment
Dimension Severity Risk Reasoning
Implementation risk 🟩 Low Additive change — optional field + null ?? null default. All seven tool-config types and all six factory copy paths covered.
Premise risk 🟩 Low The hardcoded strict: null bug is real and verified in the diff; passthrough with ?? null is the correct fix.
Estimated impact 🟩 Low Callers who don't set strict see no change; those who do get an additive feature. Worst case: strict doesn't work for a specific provider dialect, surfaced as a provider error.
Risk Factor Severity Risk Reasoning
Reversibility 🟩 Low Fully reversible — removing the field or reverting to null restores prior behavior.
Detectability 🟩 Low Provider 400 errors on strict schemas are immediately visible.
Blast radius 🟩 Low Only affects tool-call argument generation for callers who opt into strict: true.
Data integrity None No persisted state is touched.
Financial exposure None No billing or payment paths.
Security and privacy exposure None No credentials, auth, or tenant isolation involved.
Propagation 🟩 Low Serialized tools flow into API requests only; no downstream records or decisions absorb the change.
Availability None The change cannot affect whether anything serves.
Recovery cost 🟩 Low A revert or field removal fully recovers.
Time to correct 🟩 Low Any issue is a one-commit fix in the serializer or factory.

Comment thread packages/agent/tests/unit/server-tool.test.ts
@LukasParke
LukasParke merged commit 3028554 into main Aug 6, 2026
5 checks passed
@LukasParke
LukasParke deleted the devin/dev-786-agent-sdk-drops-strict-on-function-tools-serializer branch August 6, 2026 13:59
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