Skip to content

chore: version packages - #88

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main
Open

chore: version packages#88
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@openrouter/agent@0.9.0

Minor Changes

  • #73 78c562e Thanks @LukasParke! - Doom-loop detection for the tool-execution loop (opt-in via doomLoop on callModel).

    Catches runs that stop making progress while continuing to spend: the model re-issuing the same tool call with identical arguments in consecutive rounds (including repeated empty {} calls and repeated invalid-JSON calls), repeating identical server-tool requests (web_search_call etc., detected post-execution at the step checkpoint), or emitting the same text tokens over and over. Detection is deterministic — a verdict is a pure function of the transcript — and responds through a configurable graduated ladder: observe (emit the new DoomLoopDetected hook) → steer (inject corrective guidance; queued guidance persists across pauses) → block (refuse the call with an explanatory tool error, before execution) → stop (halt before any further model request; unresolved calls in the final turn get synthesized halt-error outputs so persisted history stays well-formed; SessionEnd.reason: 'doom_loop').

    Streaks are round-scoped: N identical calls fanned out in parallel within one round count once (a streak measures the model re-issuing a call after seeing its result). Tools declare call identity via loopKey on the tool definition — a function computing key material (null exempts a call), a declarative field list (['command', 'cwd'] — data, not code), or false (statically exempt); absent means the full validated arguments. MCP-wrapped tools accept loopKey via markMcp(tool, { loopKey }). Fingerprints are a cross-port contract: RFC 8785 (JCS) canonicalization + SHA-256 over UTF-8 via WebCrypto, with conformance vectors in tests/vectors/doom-loop-fingerprints.json for the Python/Go ports. Unhashable key material (bigint, circular, >64 deep) falls back to the full-arguments identity — detection never fails a run.

    Detector state persists inside ConversationState.doomLoop: streaks survive serialize → resume, a stop verdict survives decision-only resumes (approve/reject) and clears on a fresh conversational turn, and queued steer guidance is delivered on resume. Ladder configs warn on dead rungs and on block with stop: false (unbounded block/re-issue). Documented, test-locked limits: varying-input (nonce) loops evade the default identity without a loopKey; paraphrased text repetition is not detected; manual/client-executed calls are not recorded. New @openrouter/agent/doom-loop subpath exports the primitives; ModelResult.getDoomLoopVerdict() reports a stopping verdict.

  • #73 78c562e Thanks @LukasParke! - Doom-loop escalation recovery: a new escalate ladder rung between steer and block that unblocks a stuck run by throwing more intelligence at the next turn instead of refusing or halting.

    Configure via doomLoop.escalation: model runs the NEXT turn on a stronger model (one-turn override, automatic revert), and/or advisor forces an openrouter:advisor consult (the advisor server tool is appended with forwardTranscript: true and loop-diagnosing instructions, and toolChoice is pinned to it via allowed_tools/required so the stuck model must ask for guidance first; an object form passes through as advisor parameters). A user notice naming the detected loop accompanies the escalated turn.

    Escalations are real spend on a run already suspected of wasting it, so they are budgeted: maxEscalations (default 2) caps recoveries per conversation, budget is consumed when a recovery is applied (not at verdict time), escalationsUsed persists in ConversationState.doomLoop so resumes cannot reset it, and concurrent detector verdicts in one window escalate once. Exhausted or unconfigured escalations fall through to the weaker rungs; resolve-time warnings flag an escalate rung without a mechanism (and vice versa). The DoomLoopDetected hook's action/overrideAction enums gain 'escalate' — an override without config/budget downgrades to observe, never silently to a stronger action.

  • #73 78c562e Thanks @LukasParke! - Run-level cancellation and per-request timeout composition.

    New signal option on callModel: aborting it stops the tool-execution loop at the next turn boundary AND aborts the in-flight API request/stream, so a stalled provider fails fast with the abort reason instead of hanging until an outer caller/test timeout. A pre-aborted signal fails before any network dispatch.

    RequestOptions.timeoutMs (the third callModel argument) now reliably bounds each request the loop makes even when a signal is present: the underlying SDK skips its own timeoutMs wiring whenever a request carries a signal, so the engine composes {run signal, caller signal, per-request timeout} via AbortSignal.any per dispatch — each request gets a fresh timeout budget (not one shared per-run timer), and whichever bound fires first wins.

Patch Changes

  • #91 231fb65 Thanks @w0nche0l! - Thread the executed tool call into the hook execute context. context.toolCall is part of the tool-facing contract, but only the non-streaming orchestrator populated it — the streaming ModelResult loop builds its turn context with just numberOfTurns, so execute / onToolCalled hooks saw toolCall: undefined on the streaming path. buildExecuteCtx now fills the gap from the executed call: a caller-provided turnContext.toolCall still wins (the orchestrator's carries status), and otherwise the executed ParsedToolCall is converted back to a wire-shaped FunctionCallItem. The onResponseReceived path intentionally threads nothing — only the function_call_output item is in scope there.

@openrouter/mcp@0.1.0

Minor Changes

  • #74 f412281 Thanks @LukasParke! - Doom-loop loopKey support for MCP-wrapped tools (pairs with @openrouter/agent's doomLoop option).

    Two ways to declare a wrapped tool's call identity: a client-side loopKeys map on createMCPTools/rehydrateMCPTools (keyed by unprefixed MCP tool name; any ToolLoopKey form — function, field-name array, or false to exempt), and a server-advertised _meta['openrouter/loopKey'] on the tool definition (data-only: field-name array or false). Client config takes precedence. Server-advertised declarations ride the cache snapshot (SerializedMCPToolDef.loopKey), so rehydrated tool sets keep their identities without a listTools() round-trip; function forms are client-side only and cannot be cached.

    import { createMCPTools } from "@openrouter/mcp";
    
    const mcp = await createMCPTools({
      url: "https://mcp.example.com/mcp",
      // Keyed by the UNPREFIXED MCP tool name, even when toolNamePrefix is set.
      // Any ToolLoopKey form: a field-name array, `false` to exempt, or a
      // function computing key material (client-side only — not cacheable).
      loopKeys: {
        run_command: ["command", "cwd"],
        poll_job: false,
      },
      cache: { store },
    });
    
    const result = client.callModel({
      model: "z-ai/glm-5.2",
      input: "Get the build passing.",
      tools: mcp.tools,
      doomLoop: true,
    });

    A server can advertise the same thing itself via
    _meta['openrouter/loopKey'] on the tool definition (data-only: a field-name
    array or false). Client loopKeys win over a server declaration, and
    server-advertised values survive a cache round-trip via
    SerializedMCPToolDef.loopKey.

Patch Changes

@cortex-github-agent cortex-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 @github-actions[bot] is not a member of OpenRouterTeam (association: CONTRIBUTOR). Review posted as COMMENT; a maintainer must approve out-of-band.

Summary

Automated Changesets release PR: consumes the three pending changesets, bumps @openrouter/agent 0.8.0 → 0.9.0 (minor, matching three minor changesets) and @openrouter/mcp 0.0.1 → 0.0.2 (patch, per updateInternalDependencies: "patch" in .changeset/config.json), with matching CHANGELOG entries. No source changes; version/changelog math is internally consistent and no dependency range edit is needed since packages/mcp/package.json uses @openrouter/agent: workspace:*.

No findings.

@github-actions
github-actions Bot force-pushed the changeset-release/main branch from a97ef22 to 4125c61 Compare July 30, 2026 15:59

@cortex-github-agent cortex-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 @github-actions[bot] is not a member of OpenRouterTeam (association: CONTRIBUTOR). Review posted as COMMENT; a maintainer must approve out-of-band.

Summary

Update review of head 4125c61: the diff content is byte-identical to the version I reviewed at a97ef22 (same three changesets consumed, @openrouter/agent 0.8.0 → 0.9.0, @openrouter/mcp 0.0.1 → 0.0.2, same CHANGELOG bodies) — the synchronize was a regeneration/rebase of the Changesets branch, not a content change. Re-verified at the new head: .changeset/ contains no leftover pending changesets, packages/agent/package.json:3 reads 0.9.0 with the ./doom-loop subpath export present (matching the changelog's claim of a new @openrouter/agent/doom-loop entry point), and packages/mcp/package.json still depends on @openrouter/agent: workspace:*, so no dependency range rewrite is missing.

No findings.

@github-actions
github-actions Bot force-pushed the changeset-release/main branch 2 times, most recently from 9763a5b to 9ea3633 Compare August 3, 2026 19:17
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.

0 participants