Skip to content

v0.9.0

Choose a tag to compare

@trevhud trevhud released this 30 Jul 04:18

Added

  • Live compilation progress — the compiler now emits structured
    CompilationEvents (rote.compiler.events): phase transitions
    (driven by progress.ndjson markers the compiler skill writes at
    the start of each phase), per-turn token counts, tool calls, and
    artifacts. rote compile renders them live on stderr; hosts embed
    the stream via Compiler(on_event=...). Works across the Claude
    subprocess driver (stream-json) and both in-process API drivers.

  • OpenAI-compatible API driver (--agent openai-api) — the same
    in-process compilation loop against any OpenAI-shaped endpoint
    (GPT, GLM, Kimi, …), sharing one filesystem-tool surface with the
    Anthropic driver.

  • Gateway-friendly driver auth — both API drivers accept
    base_url and default_headers (through the new
    Compiler(driver_kwargs=...)), so compilations can route through
    proxies like Cloudflare AI Gateway with no provider key in the
    environment.

  • Compiler.compile() accepts extra_instructions, appended to the
    compiler skill prompt (e.g. pinning emitted judge calls to a
    specific runtime client).

  • The Cloudflare adapter emits manifest.json at the runtime-dir
    root — machine-readable pipeline identity (name, version, pipeline
    hash, class name, node ids, entry) for deploy tooling; regex
    fallback retained for older emits.

  • rote.eval.build_scorecard_for is now public API (was a CLI
    private).

  • Cloudflare Workers call MCP tools, authenticated by provisioning
    — Workers have no filesystem for the token store, so
    rote mcp export <server> turns a completed rote mcp login into
    Worker secrets (dotenv form for .dev.vars, --json for
    npx wrangler secret bulk). MCP-bound nodes emit working calls
    through a Workers helper that mints access tokens at runtime via the
    OAuth refresh grant and caches them — with rotated refresh tokens —
    in a ROTE_MCP_TOKENS KV namespace declared in the emitted
    wrangler.jsonc. The Env interface, .dev.vars.example, and
    README document the per-server provisioning surface. Emitted output
    typechecks against @cloudflare/workers-types v5 + the MCP SDK.

  • Node TypeScript runtimes call MCP tools, authenticated — the
    DBOS-TS and Inngest adapters now emit working bodies for
    mcp:-bound nodes (previously always throwing stubs): the module
    calls the tool via the official @modelcontextprotocol/sdk (^1.29)
    through an emitted src/extracted/_roteMcp.ts helper that reads the
    same rote registry/token store the CLI writes, refreshes stale access
    tokens with a refresh-token grant against the stored
    token_endpoint, writes rotated refresh tokens back atomically, and
    retries once on 401. --backend api keeps the direct-vendor-SDK
    stubs. Proven end-to-end across languages: Python rote mcp login
    seeds the store, compiled TS authenticates, refreshes a forced-stale
    token, and rotates credentials Python reads back
    (tests/test_mcp_ts_e2e.py).

  • A full MCP client with OAuth 2.1 (rote mcp, design in
    docs/mcp-client.md) — real streamable-HTTP MCP
    servers authenticate with OAuth; without a client that can run the
    flow, store tokens durably, and refresh them, MCP-backed workflows
    only worked against unauthenticated servers:

    • rote mcp add / list / remove — a user-level server registry
      (~/.config/rote/mcp.json, 0600) mapping the logical server names
      pipelines carry in their mcp: bindings to endpoints, with
      pre-registered client_id/client_secret support for servers
      without dynamic client registration (Slack/GitHub-class) and static
      headers for API-key schemes.
    • rote mcp login — the full spec dance (protected-resource
      discovery, PKCE, dynamic client registration, refresh) via
      fastmcp's OAuth provider, persisting into a rote-owned token store
      (one 0600 JSON file per server, ~/.local/share/rote/mcp-tokens/)
      whose layout is a documented cross-language contract.
      --no-browser prints the authorization URL for SSH boxes.
    • rote mcp headers — mints a currently-valid Authorization header
      (auto-refreshing through the stored refresh token), the
      machine-facing token API.
    • Emitted DBOS apps authenticate: MCP-backed steps now open the
      client through an emitted extracted/_rote_mcp.py helper (the
      verbatim source of rote.mcp._runtime_helper — one tested
      implementation) that resolves endpoints (env > registry > IR) and
      credentials (OAuth store > static headers > none) at runtime, with
      in-place token refresh. Emitted apps still never import rote.
    • eval --run trials authenticate: the generated --mcp-config
      injects registry headers verbatim, or — for logged-in servers — a
      headersHelper invoking rote mcp headers, which Claude Code
      re-runs per connection and on 401, so tokens refresh mid-run on
      long trials.
    • Verified end-to-end against a real OAuth-protected server
      (tests/test_mcp_oauth_e2e.py): live authorization dance with
      dynamic registration, cross-process token reuse, an authenticated
      tool call through the emitted helper, and a forced-stale refresh.
    • New optional extra: pip install 'rote-cli[mcp]'; python -m rote
      now works (used by the headersHelper wiring).

Fixed

  • The API drivers no longer misread per-turn output truncation
    (stop_reason: max_tokens / finish_reason: length) as completion —
    the per-turn cap is raised to 32k tokens and the loop continues
    automatically with a warning event. Surfaced by Claude 5 models,
    which spend far more of the turn budget on thinking.
  • The Anthropic driver sets an explicit client timeout sized from the
    per-turn token cap (the SDK otherwise refuses non-streaming requests
    that may exceed 10 minutes at the new cap).
  • The Anthropic driver survives content: null assistant turns some
    gateway endpoints return for all-thinking responses: content is
    normalized, empty assistant turns are never replayed into history,
    and an empty natural stop nudge-continues with a warning.
  • Emitted Cloudflare package.json pinned @cloudflare/workers-types
    ^4, which current wrangler (4.110+) rejects with a peer-dependency
    conflict on a fresh install — bumped to ^5.