Skip to content

v0.12.0 — single-turn translation (all six ports)

Latest

Choose a tag to compare

@muthuishere muthuishere released this 30 Jul 04:30

Single-turn translation — the inbound half of the adapters

SPEC.md §0 item 7 pinned the format adapters as schema only: toOpenAI/toAnthropic/toGemini translated tool declarations outbound, and nothing in the library read a provider's tool calls back inbound. Every user of those public functions hit the same wall — they could tell a provider about their tools, but not receive the calls it made.

New: translate (idiomatic naming per port). Exactly one provider call, returned in OpenAI shape. No agent loop, no tool execution, no conversation state — every call is self-contained, so it runs statelessly and scales horizontally.

  • Request takes the OpenAI messages, tools and tool_choice verbatim, or an ordinary toolkit (MCP tools, skills, native functions, A2A agents, builtins) which is declared and never executed. They compose.
  • Inbound preserves the tool structure a text flattening destroys: assistant tool_calls → native tool-use blocks with arguments re-parsed to objects; tool results → tool-result blocks keyed by tool_call_id, merged into one user turn when consecutive; system hoisted to the provider's own field; content-parts flattened. Both arguments wire forms accepted.
  • Outbound returns arguments as a JSON string (the wire form, echoable byte-for-byte) plus a mapped finishReasonany tool call wins, giving "tool_calls".
  • Shares the loop's retries/backoff, request-param merging and llm metric. beforeLLM/afterLLM fire once; tool hooks never fire.

Use it when the caller owns the conversation and executes tools itself — the standard OpenAI function-calling posture. golang/examples/translator is a stateless OpenAI-compatible proxy in ~60 lines.

Parity verified by byte-diff, not assumed

One adversarial fixture hitting every §11 rule at once was run through all six ports and diffed against Go: js, python, java, csharp and elixir are byte-identical, first diff, no corrections. Committed at docs/spikes/0003-translation-parity-fixture.json.

Tests: js 13 · python 20 · golang 12 · java 13 · csharp 20 · elixir 27. Every port's full suite green; Elixir coverage 96.9%.

Fixed: python was broken for new installs

pyproject allowed mcp>=1.0.0, but mcp 2.x renamed streamablehttp_client, so mcp_source.py failed at import — a fresh resolve could not load the package at all. Now pinned mcp>=1.0.0,<2.0.0.

Relay tools + durable resume — golang ONLY, a preview

golang also gains relay (declaration-only) tools and an answer-carrying durable resume, built on the §10 suspension primitive (ADR-0010, #37). The other five ports do not implement this yet, so it is deliberately not part of the SPEC.md §0 conformance contract — the §10 subsections carry a status banner, and the remaining ports are tracked in openspec/changes/add-tool-relay-mode/tasks.md.

If you want cross-port behaviour, use §11 translation. ADR-0011 explains the split: translation for the pass-through posture (the caller owns the conversation), relay for proxy-managed memory (toolnexus owns it).

Full detail in CHANGELOG.md. Closes #37 via #38.