Skip to content

Add code-style tool-call dialect (Python / TypeScript) - #16

Merged
senamakel merged 6 commits into
mainfrom
code-tool-dialect
Sep 22, 2026
Merged

senamakel merged 6 commits into
mainfrom
code-tool-dialect

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

Adds a fourth text dialect, CodeDialect, for small code-trained models: the catalogue is rendered as function signatures and the model calls tools as code.

## Tools

def read_file(path: str, limit: int = None) -> str  # Read a file from the workspace
<tool_call>
read_file(path="src/main.rs", limit=20)
</tool_call>

One grammar (src/codecall/) reads both the Python and the TypeScript spelling (read_file({path: "src/main.rs"})). It hooks into tagged.rs::decode_body after P-Format and before the JSON paths, so streaming and every dialect inherit it. It reuses PFormatRegistry for positional binding and type coercion, so the catalogue order and the binder order come from the same from_schema call.

Bounds kept: a call still needs a <tool_call> / ```tool_call marker (a top-level ```python fence stays an example); literals only (a bare identifier is a variable → refused); unknown tool / unknown keyword / duplicate / positional overflow → refused, never guessed; all-or-nothing per tag body, so prose mentioning read_file(...) never binds arguments.

Measured against qwen3:8b via Ollama with a 10-tool / 20-task fixture (OpenHuman tool-dialect-bench): python 517 prompt tokens vs xml 703 vs pformat 795, with exact-argument accuracy 90% vs 80% (xml) vs 60% (pformat) and identical call recovery (95%).

Draft until the consuming PRs are up: tinyhumansai/tinyagents (ToolDispatcher::{Python, Typescript}) and tinyhumansai/openhuman (agent.tool_dispatcher = "python" | "typescript"). Merge order: this → tinyagents → openhuman.

Related issue

None.

API or behavior changes

  • New public module codecall (CodeStyle, parse_calls, render_code_signature, render_code_type), dialect::CodeDialect, render::{render_code_catalogue, code_instructions}; re-exported parse_code_calls.
  • CallSource::Code (enum is #[non_exhaustive], additive).
  • Breaking for exhaustive matchers: dialect::ToolCallFormat gains Python and TypeScript (it is not #[non_exhaustive]).
  • pformat::coerce_value is now pub(crate).
  • A <tool_call> body that is a code call to a registered tool now parses (previously Malformed). Bodies that are P-Format / JSON / Kimi / GLM are unchanged (they never start with NAME().

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo build --all-targets --all-features
  • cargo test --all-features — 289 passed in tinytools-agent
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features

Tests

src/codecall/test.rs (45): keyword / positional / mixed binding, every literal kind incl. delimiters inside strings, escapes, triple quotes, raw and template strings, nested lists/dicts, trailing commas, comments, ; and newline separators, prefix stripping, the three branches of the single-object rule, prose-in-body refusal, unknown-name / unknown-keyword / duplicate / overflow refusal, coercion, both signature renderers, enum/array/union/nested/depth-capped types.
parse/test/tagged.rs (+9): hook ordering — Code / P-Format / JSON / GLM siblings each keep their own CallSource; fence-in-tag unwrapped; top-level ```python is not a call; registry required.
stream/test.rs (+1): a call split mid-string across three deltas is released once and never shown.
dialect/test.rs (+7): both spellings parse, fallback per tag, protocol block pinned, catalogue snapshots for both styles (asserting optional params are alphabetical), signature-order == binding-order, code catalogue < JSON catalogue and code block < half of the P-Format block.

Deliberately untested: a string literal containing </tool_call> truncates the body — the same inherited limit JSON bodies have.

Documentation

crates/tinytools-agent/README.md (module and grammar tables), docs/specs/agent-tool-protocols.md (new "Text dialects" section, compatibility note), module docs on every new file.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints
  • No secrets, tokens, or .env contents in the diff or the description

Co-authored-by: Medulla medulla@tinyhumans.ai

Render a tool catalogue as function signatures and read function calls
back, for small code-trained models that have never seen P-Format.
One grammar reads both spellings; calls live in the existing
<tool_call> markers, are registry-gated, accept literals only, and
refuse whole bodies that are not entirely calls to known tools.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 22 billable files and costs up to $5.50.

Or wait 28 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d2a62f4f-b67c-4415-917a-83804d5299ed

📥 Commits

Reviewing files that changed from the base of the PR and between b3ba431 and 369cd3d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • Cargo.toml
  • crates/tinytools-agent/Cargo.toml
  • crates/tinytools-agent/README.md
  • crates/tinytools-agent/src/codecall/literal.rs
  • crates/tinytools-agent/src/codecall/mod.rs
  • crates/tinytools-agent/src/codecall/signature.rs
  • crates/tinytools-agent/src/codecall/test.rs
  • crates/tinytools-agent/src/codecall/types.rs
  • crates/tinytools-agent/src/dialect/code.rs
  • crates/tinytools-agent/src/dialect/mod.rs
  • crates/tinytools-agent/src/dialect/test.rs
  • crates/tinytools-agent/src/dialect/types.rs
  • crates/tinytools-agent/src/lib.rs
  • crates/tinytools-agent/src/parse/grammar/tagged.rs
  • crates/tinytools-agent/src/parse/test/tagged.rs
  • crates/tinytools-agent/src/pformat.rs
  • crates/tinytools-agent/src/render/catalogue.rs
  • crates/tinytools-agent/src/render/instructions.rs
  • crates/tinytools-agent/src/render/mod.rs
  • crates/tinytools-agent/src/stream/test.rs
  • crates/tinytools-agent/src/types.rs
  • docs/specs/agent-tool-protocols.md

Comment @coderabbitai help to get the list of available commands.

@tinysweeper

tinysweeper Bot commented Sep 22, 2026

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 6 lane(s) and found 10 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Ready for maintainer review
Priority: medium
Reviewed head: 369cd3dde215
Updated: 1790043408 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 14 Active findings 9
Tests 4 Noted findings 0
Documentation 2 Resolved findings 72
Configuration 2 Pending checks/questions 0

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

  • medium · critique · Reject leading-zero numbers with separators — The leading-zero check only rejects a `0` followed directly by another digit. Inputs such as `0_1` and `0_1.5` therefore pass `consume_digits`, have their underscores removed, and (crates/tinytools\-agent/src/codecall/literal\.rs:274)
  • medium · critique · Render Python literals with Python spellings — This helper is used for both Python and TypeScript signatures, but `other.to_string()` uses JSON spellings. A Python schema such as `{"const": true}` or `{"enum": [null, false]}` t (crates/tinytools\-agent/src/codecall/signature\.rs:243)
  • medium · critique · Bound recursion through schema combinators — The advertised `MAX_DEPTH` limit is not applied to nested `anyOf` or `oneOf` branches: each recursive call receives the unchanged `depth`. A schema containing deeply nested combina (crates/tinytools\-agent/src/codecall/signature\.rs:126)
  • medium · critique · Handle escaped quotes in raw strings — In a raw string, a backslash before the matching quote escapes that quote in Python syntax and the quote must remain part of the string. This branch appends the backslash but then (crates/tinytools\-agent/src/codecall/literal\.rs:213)
  • medium · critique · Handle dollar-prefixed identifiers consistently — This scanner accepts `$` at the start and inside every identifier, including Python calls and bare dictionary keys. For example, `tool($value=1)` or `tool({$value: 1})` is accepted (crates/tinytools\-agent/src/codecall/literal\.rs:112)
  • medium · critique · Reject all reserved TypeScript tool names — The TypeScript name check omits reserved words such as `interface`, `implements`, `private`, and `public`. A tool named `interface` therefore passes validation and produces `functi (crates/tinytools\-agent/src/codecall/signature\.rs:308)
  • medium · critique · Reject unescaped newlines in quoted strings — For non-triple single- or double-quoted Python/JavaScript strings, an unescaped newline is not valid syntax. `string` currently appends `\n` like any other character, so malformed (crates/tinytools\-agent/src/codecall/literal\.rs:213)
  • medium · critique · Reject malformed Unicode escapes — The invalid-escape branch preserves only `\\u` and leaves the malformed digits unread, so inputs such as `"\\u12"` or `"\\u{not-hex}"` are accepted as literal text. These are malfo (crates/tinytools\-agent/src/codecall/literal\.rs:250)
  • medium · security · Reject all TypeScript reserved tool names — The TypeScript validation list omits reserved words such as `interface`, `implements`, `let`, `package`, `private`, `protected`, `public`, and `static`. A tool named `interface` th (crates/tinytools\-agent/src/codecall/signature\.rs:308)

Resolved this pass

  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them
  • Reject malformed numeric spellings
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Render Python parameter names as valid identifiers
  • Remove the blanket Clippy allowance
  • Handle dollar-prefixed identifiers consistently
  • Escape enum strings with a source-language-safe format
  • Reject malformed numeric spellings
  • Preserve empty objects for single-parameter tools
  • Validate the rendered tool name before emitting the signature
  • Bound recursive literal nesting before parsing
  • Reject non-finite numeric literals
  • Validate Python parameter names before rendering them

Before merge

None.

How this fits together

flowchart LR
  n0["ToolCallFormat<br/>changed"]:::changed
  n1["render_json_catalogue<br/>changed"]:::changed
  n2["ToolDialect"]:::impacted
  n3["...s_one_signature_per_line_in_binding_order"]:::impacted
  n4["render_code_catalogue"]:::impacted
  n5["...alogue_is_smaller_than_the_json_catalogue"]:::impacted
  n6["ToolSpec"]:::impacted
  n7["weather_schema"]:::impacted
  n1 -->|uses| n6
  n2 -->|uses| n0
  n2 -->|uses| n6
  n3 -->|calls| n4
  n3 -->|tests| n4
  n3 -->|calls| n7
  n3 -->|tests| n7
  n4 -->|uses| n6
  n5 -->|calls| n1
  n5 -->|tests| n1
  n5 -->|calls| n4
  n5 -->|tests| n4
  n5 -->|calls| n7
  n5 -->|tests| n7
  n7 -->|uses| n6
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 7 files; 8 findings. (1 observation(s) grouped into shared inline comments) _The code index is behind this pull request (indexed at `705677c82084`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer: timed out after 20s), so this review saw part of what the engine holds._
  • Evidence: crates/tinytools\-agent/src/codecall/literal\.rs — Reject leading-zero numbers with separators
  • Evidence: crates/tinytools\-agent/src/codecall/signature\.rs — Render Python literals with Python spellings
  • Evidence: crates/tinytools\-agent/src/codecall/signature\.rs — Bound recursion through schema combinators
  • Evidence: crates/tinytools\-agent/src/codecall/literal\.rs — Handle escaped quotes in raw strings
  • Evidence: crates/tinytools\-agent/src/codecall/literal\.rs — Handle dollar-prefixed identifiers consistently
  • Evidence: crates/tinytools\-agent/src/codecall/signature\.rs — Reject all reserved TypeScript tool names
  • Evidence: crates/tinytools\-agent/src/codecall/literal\.rs — Reject unescaped newlines in quoted strings
  • Evidence: crates/tinytools\-agent/src/codecall/literal\.rs — Reject malformed Unicode escapes

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 7 files; 1 finding. _The code index is behind this pull request (indexed at `705677c82084`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer: timed out after 20s), so this review saw part of what the engine holds._
  • Evidence: crates/tinytools\-agent/src/codecall/signature\.rs — Reject all TypeScript reserved tool names

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: This pull request adds a code-call dialect to tinytools-agent, with a parser, renderer, stream detection, and a stream test for a split mid-string call. The change is sound as far as the visible diff shows, but I could not verify the full parsing and stream logic because the diff only exposes a truncated portion; the stream test exists and asserts the key invariants (call released at most once, no markup leaked, correct split-string value). (10 earlier finding(s) still open) _The code index is behind this pull request (indexed at `705677c82084`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer: timed out after 20s), so this review saw part of what the engine holds._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The pull request adds a code-call grammar and code dialect to tinytools-agent, with documentation updates and streaming tests. The change is coherent and the test coverage for the new streaming path is appropriate; it appears safe to merge. I was unable to verify the full build or check integration details, but nothing in the diff indicates a problem. (10 earlier finding(s) still open) _The code index is behind this pull request (indexed at `705677c82084`), so retrieved context may be out of date._ _3 memory call(s) failed (model: cortex: v1/answer: timed out after 20s), so this review saw part of what the engine holds._

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: No end-to-end harness in this repository: no e2e test files and no e2e workflow.
Evidence and run details
  • Models: ladder/vectors, gpt-5.6-luna, deepseek-v4-flash, deepseek/deepseek-v4-flash
  • Spend: $0.047812
  • Tokens: 961346 input · 43983 output · 183312 cached · 1223 embedding
Head State Pass summary
705677c82084 changes requested 10 active finding(s), 0 resolved finding(s) (at 1790041740)
369cd3dde215 ready for maintainer review 9 active finding(s), 72 resolved finding(s) (at 1790043408)

tinysweeper 0.1.0

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-22T02:15:56.703194Z 369cd3d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 705677c820

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinytools-agent/src/dialect/types.rs
Comment thread crates/tinytools-agent/src/codecall/literal.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/literal.rs
Comment thread crates/tinytools-agent/src/codecall/signature.rs
@senamakel senamakel self-assigned this Sep 22, 2026

@tinysweeper tinysweeper 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.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.1024 · 2,078,856 in / 52,262 out · 260,688 cached (13%) · ladder/vectors, gpt-5.6-luna, deepseek/deepseek-v4-flash, deepseek-v4-flash · 1,210 embedded
critique:    $0.0575 · 1,140,810 in / 31,693 out · 117,157 cached (10%) · gpt-5.6-luna, deepseek/deepseek-v4-flash, deepseek-v4-flash
security:    $0.0425 · 826,613 in   / 18,717 out · 43,999 cached (5%)   · gpt-5.6-luna
tests:       $0.0007 · 43,414 in    / 72 out     · 39,447 cached (91%)  · deepseek/deepseek-v4-flash
description: $0.0007 · 34,785 in    / 268 out    · 30,817 cached (89%)  · deepseek/deepseek-v4-flash

Comment thread crates/tinytools-agent/src/codecall/signature.rs
Comment thread crates/tinytools-agent/src/codecall/test.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/literal.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/signature.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/literal.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/mod.rs
Comment thread crates/tinytools-agent/src/codecall/signature.rs
Comment thread crates/tinytools-agent/src/codecall/literal.rs Outdated
Comment thread crates/tinytools-agent/src/codecall/literal.rs Outdated
senamakel and others added 5 commits September 22, 2026 04:54
The literal parser now enforces a maximum nesting depth of 64, rejects malformed numbers with leading zeros or trailing separators, and refuses f-strings containing interpolation braces. Tool names that are not valid identifiers now render as an explicit unsupported-tool comment, and invalid parameter names fall back to a generic `args: dict` signature. Empty argument objects are no longer treated as a single unpacked object, and the `$` character is accepted in bare words.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `one` helper in the codecall tests now binds the result of `parse_calls` to a mutable variable, allowing the test to potentially modify the parsed calls before assertions. This change aligns the helper with the mutability expectations of the test suite.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The condition for unpacking a single object argument now also requires that the function has more than one parameter, preventing premature unpacking when only one parameter is declared. This fixes the case where a single-parameter function with a dict argument was incorrectly treated as an object to unpack. The test expectation is updated to reflect the corrected ordering of properties in the rendered signature.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
# Conflicts:
#	crates/tinytools-agent/src/dialect/test.rs
The identifier validation now respects the target language's syntax rules, using ASCII-only checks for both Python and TypeScript, and rejecting reserved words in TypeScript. This prevents generating invalid code signatures for properties like "$value" in Python or "class" in TypeScript, with tests covering these cases.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel merged commit 970bfae into main Sep 22, 2026
10 checks passed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 369cd3dde2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| `PFormatDialect` | `name[0\|<a>\|1\|<b>]` signatures in the prompt's tool section | `<tool_call>name[0\|value]</tool_call>` | yes |
| `CodeDialect` | `def name(a: str, b: int = None) -> str` or `function name(a: string, b?: number): string;` signatures in the prompt's tool section | `<tool_call>name(a="value")</tool_call>` or `name({a: "value"})` | yes |

The code dialect exists for small, code-trained models: a function signature

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the required implementation plan

This adds the accepted behavior and full implementation for the new dialect, but a repository-wide search of docs/plans/ finds no linked, implementation-ordered code-call plan. Add the required plan and link it to this specification so the design and implementation sequence remain reviewable.

AGENTS.md reference: AGENTS.md:L206-L208

Useful? React with 👍 / 👎.

//! dialect — sees it, and a top-level ```` ```python ```` fence stays what it
//! is everywhere else in this crate: an example, not a call.

mod literal;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Document the complex codecall module

The new codecall module contains a parser, literal scanner, signature renderer, types, and more than a thousand lines of implementation and tests, but its directory has no module-level README.md. Add one covering the design, public surface, and operational constraints required for complex modules.

AGENTS.md reference: AGENTS.md:L202-L203

Useful? React with 👍 / 👎.

.any(|param| !is_identifier(param, style));
if has_invalid_param {
return match style {
CodeStyle::Python => format!("def {name}(args: dict) -> str"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the synthetic args keyword bind

Fresh evidence after the earlier non-identifier fix is that the Python protocol explicitly tells models to prefer keyword arguments, so this advertised signature naturally produces read_file(args={"file-path": "x"}); bind rejects args because it is not a real schema property, and single-object unpacking is disabled when keyword arguments are present. Either make this synthetic parameter an accepted wrapper or render/instruct a positional dictionary call.

Useful? React with 👍 / 👎.

}
self.skip_trivia();
match self.peek().ok_or(Refuse)? {
'"' | '\'' | '`' => self.string(false).map(Literal::Str),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject interpolation in backtick templates

When a TypeScript model emits read_file(path=${root}/data), this direct backtick branch decodes the template as the literal string ${root}/data and dispatches it without evaluating interpolation. The f-string guard only runs through word(), so backtick templates containing ${...} must also be refused to avoid silently corrupted arguments.

Useful? React with 👍 / 👎.

fn literal(value: &Value) -> String {
match value {
Value::String(s) => Value::String(s.clone()).to_string(),
other => other.to_string(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Render Python enum literals with Python spellings

For a valid schema such as {"enum":[true,null]} or a boolean/null const, this shared JSON serialization makes the Python catalogue emit Literal[true, null]; Python requires True and None, so the advertised signature is invalid and encourages the model to use the wrong language spelling. Render these scalar literals according to CodeStyle.

Useful? React with 👍 / 👎.

@tinysweeper tinysweeper 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.

The previously-blocking findings are resolved. Clearing the changes request.

             $0.0478 · 961,346 in / 43,983 out · 183,312 cached (19%) · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash, deepseek/deepseek-v4-flash · 1,223 embedded
critique:    $0.0240 · 443,760 in / 25,768 out · 48,384 cached (11%)  · gpt-5.6-luna, deepseek-v4-flash, deepseek/deepseek-v4-flash
security:    $0.0209 · 395,374 in / 15,648 out · 28,572 cached (7%)   · gpt-5.6-luna
tests:       $0.0012 · 46,875 in  / 1,230 out  · 40,924 cached (87%)  · deepseek/deepseek-v4-flash
description: $0.0007 · 38,358 in  / 110 out    · 32,418 cached (85%)  · deepseek/deepseek-v4-flash

let mut end = usize::from(matches!(bytes.first(), Some(b'+' | b'-')));
let integer_start = end;
consume_digits(bytes, &mut end)?;
if bytes.get(integer_start) == Some(&b'0')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Reject leading-zero numbers with separators

The leading-zero check only rejects a 0 followed directly by another digit. Inputs such as 0_1 and 0_1.5 therefore pass consume_digits, have their underscores removed, and are parsed as 1 and 1.5. These are malformed numeric spellings in the supported Python/JavaScript literal grammar and silently changing them to different values can invoke a tool with unintended arguments. Reject a zero followed by either a digit or a numeric separator before continuing the parse.

[RULE] reject-malformed-numeric-spellings ·

}

/// A JSON value spelled as a type-level literal (`"a"`, `1`, `true`).
fn literal(value: &Value) -> String {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Render Python literals with Python spellings

This helper is used for both Python and TypeScript signatures, but other.to_string() uses JSON spellings. A Python schema such as {"const": true} or {"enum": [null, false]} therefore renders Literal[true], Literal[null], or Literal[false], which is not valid Python syntax. Render booleans and null as True/False/None for CodeStyle::Python while retaining JSON spellings for TypeScript.

[RULE] source-language-literal ·

&& !variants.is_empty()
{
let mut seen = Vec::new();
for variant in variants {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Bound recursion through schema combinators

The advertised MAX_DEPTH limit is not applied to nested anyOf or oneOf branches: each recursive call receives the unchanged depth. A schema containing deeply nested combinators can consequently recurse until stack overflow instead of collapsing to the fallback type. Increment or otherwise check the depth before descending through these branches, as is done for arrays and objects.

[RULE] bounded-recursion ·

self.bump();
return Ok(out);
}
let c = self.bump().ok_or(Refuse)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Handle escaped quotes in raw strings

In a raw string, a backslash before the matching quote escapes that quote in Python syntax and the quote must remain part of the string. This branch appends the backslash but then the next loop iteration treats the quote as the terminator, so an input such as r"a\\\"b" is truncated and the remaining text makes the literal fail. Consume and append the escaped quote when raw is true, while still rejecting a raw string whose final backslash escapes its closing quote.

[RULE] raw-string-escaping ·

let rest = self.rest();
let mut end = 0;
for (idx, c) in rest.char_indices() {
let ok = if idx == 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Handle dollar-prefixed identifiers consistently

This scanner accepts $ at the start and inside every identifier, including Python calls and bare dictionary keys. For example, tool($value=1) or tool({$value: 1}) is accepted even though $value is not a valid Python identifier; if a tool exposes such a property, malformed Python text can be bound and dispatched. Since Cursor does not receive the CodeStyle, either pass the style through and apply language-specific identifier rules or reject $ in the shared grammar if Python compatibility is the required contract.

[RULE] language-specific-identifier-validation ·

self.bump();
return Ok(out);
}
let c = self.bump().ok_or(Refuse)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Reject unescaped newlines in quoted strings

For non-triple single- or double-quoted Python/JavaScript strings, an unescaped newline is not valid syntax. string currently appends \n like any other character, so malformed input such as a quoted value split across two physical lines is accepted as a string literal instead of being refused. Reject c == '\n' (and the relevant carriage-return case) here unless the string is triple-quoted or the newline was consumed by escape as a line continuation.

[RULE] reject-invalid-string-literals ·

} else {
(self.rest().get(..4).ok_or(Refuse)?, 4)
};
if let Some(decoded) = u32::from_str_radix(hex, 16).ok().and_then(char::from_u32) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique confident

Reject malformed Unicode escapes

The invalid-escape branch preserves only \\u and leaves the malformed digits unread, so inputs such as "\\u12" or "\\u{not-hex}" are accepted as literal text. These are malformed Unicode escapes in the supported source languages, not unknown escapes; refusing them avoids silently changing invalid source into a different argument value. Apply the same strictness to malformed \\x escapes if that is the intended grammar contract.

[RULE] reject-malformed-string-escapes ·

| "with"
| "yield"
),
CodeStyle::TypeScript => !matches!(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium security confident

Reject all TypeScript reserved tool names

The TypeScript validation list omits reserved words such as interface, implements, let, package, private, protected, public, and static. A tool named interface therefore passes is_identifier and renders as function interface(...): ...;, which is not a valid TypeScript declaration and makes the generated catalogue unusable for that tool. Extend the reserved-word check (or use a complete language-appropriate identifier validator) before emitting the signature.


Additional critique observation

priority medium confident

Reject all reserved TypeScript tool names

[RULE] source-identifier-validation

The TypeScript name check omits reserved words such as interface, implements, private, and public. A tool named interface therefore passes validation and produces function interface(): string;, which is not a valid TypeScript declaration. Expand the reserved-word check (including contextual TypeScript keywords that cannot be used here) or quote/reject such names before emitting the signature.

[RULE] invalid-identifier ·

@senamakel
senamakel deleted the code-tool-dialect branch September 22, 2026 02:23
@senamakel senamakel mentioned this pull request Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant