Skip to content

Filter unexecutable local calls and add a deterministic fallback at the iteration limit #108

Description

@shibayan

Summary

On the final permitted iteration, the function-invocation loop removes local function declarations. If the provider nevertheless emits a local call or approval, TypeScript returns content it will neither execute nor remove, despite the public contract promising a final answer with no unanswered local tool calls.

Python hardened this behavior with per-update filtering and the deterministic fallback text:

Function invocation limit reached before a final answer could be produced.

Required implementation

  • On the final request, remove local function declarations and send toolChoice: "none".
  • Treat only actionable local function_call content and local function_approval_request content as unexecutable. Use the existing hosted-approval predicate; hosted approvals are provider-owned and survive.
  • In streaming mode, filter each update before accumulation/yield. Do not add a full-final-round gate: retained updates continue flowing as they arrive.
  • Preserve text, reasoning, metadata, raw representation, hosted/informational content, and provider-executed informationalOnly call/result pairs.
  • If removing local content leaves a metadata-bearing update, retain it. Omit only a truly empty update.
  • Append the exact fallback only when finalization leaves no non-blank user-visible answer and no hosted approval. Do not replace retained content to make room for it.

Acceptance criteria

  • The final request has no local function declarations and uses toolChoice: "none".
  • A provider-emitted actionable local call or local approval on the final round is absent from yielded updates and the folded response and is never executed.
  • A streaming test proves retained content is yielded before the source stream completes; no new full-round buffering is introduced.
  • Mixed updates retain text/reasoning/metadata while removing only unexecutable local content.
  • A metadata-only update remains visible after its local call is removed.
  • Provider-executed informationalOnly call/result pairs and hosted approvals survive unchanged in awaited and streamed modes.
  • When no visible answer or hosted approval remains, the exact deterministic fallback is appended in both modes.
  • When visible content or a hosted approval remains, no fallback is added.
  • Awaited output equals the fold of streamed output for the same provider sequence.

Activity

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

Metadata

Metadata

Assignees

Labels

coreUsage: [Issues, PRs], Target: packages/core

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions