Public record
Software health reportschema 0.30.0 · metrics 2.3.1 · 2026-08-03 04:51 UTC

OpenRouterTeam / typescript-agent

OpenRouter Agent SDK

TypeScriptNo license detected★ 19 stars⑂ 11 forkssince Mar 2026View on GitHub ↗
KindMCP serverLibraryhow this is determined

OpenRouterTeam/typescript-agent holds a health index of 73 out of 100, placing it in the Good band. It scores highest on Vitality (85/100) and lowest on Community & Adoption (44/100). It was last updated 2 days ago. A single contributor accounts for most of its recent work.

73
overall / 100
Good

Software health index

Metrics are grouped into weighted categories on one standardized 1–100 scale. Overall starts as their weighted mean, calibrated against the distribution of the public record so bands carry percentile meaning; when public evidence triggers the High-Risk Jurisdiction Policy, the rating is adjusted and receives an At Risk ceiling of 34.

73
Exceptional93-100The record's top tier (≈ top 5%); essentially all checked criteria met
Excellent80-92Strong across the board; minor gaps
Good65-79Healthy; gaps are limited and manageable
Moderate50-64Acceptable with notable gaps; review recommended
Weak35-49Material weaknesses across several areas
At Risk20-34Significant weaknesses; adoption warrants caution
Critical1-19Severe problems (abandoned, single-maintainer, no hygiene)
VitalityCommunity &AdoptionSustainability &GovernanceEngineeringQualitySecurityAI Readiness

Score profile

Each axis is a category. The shape matters more than the average — a healthy subject fills the whole shape, while a spike-and-crater profile means strength in one dimension is masking risk in another.

The weighted overall 64 is calibrated to 73 on the published index scale (record calibration 2026-08-02).

Ownership

OpenRouterOrganization
3,536 followers36 public repossince Jul 2023

This repository is backed by an organization — shared, accountable stewardship that can outlive any single maintainer.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publishTags
npm@openrouter/mcp0.0.113319 days agoopenroutermcpmodel-context-protocolagenttypescriptaitoolsllm
npm@openrouter/agent0.8.0382,4541311 days agoopenrouteragenttypescriptaitoolsstreamingllm

Metrics by category

Vitality

Is the project alive — is code being written and are releases shipping?

85Excellent · 21% of overall
How it's scored
36/36Push recency — last push 2 days ago
11.1/36Commit cadence — 16/52 weeks with commits
18/18Commit volume — 152 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year152
human_commit_share0.94
days_since_last_push2
active_weeks_last_year16

Release discipline

100Exceptional
How it's scored
27/27Ships releases — 7 releases published
36/36Release recency — latest release 11 days ago
27/27Release cadence — a release every ~13 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count7
latest_release_tag@openrouter/agent@0.8.0
releases_from_tagsno
days_since_latest_release11
mean_days_between_releases13
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

Does the project have users, downloads, attention, and a welcoming setup for contributors?

44Weak · 17% of overall
How it's scored
20.4/60Stars — 19 stars
8.3/25Forks — 11 forks
0/15Watchers — 0 watchers
Inputs used
forks11
stars19
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
0/22.5License — no license file detected
0/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
0/6.3PR template
Inputs used
has_readmeyes
has_licenseno
readme_badges0
has_contributingno
has_issue_templateno
has_code_of_conductno
readme_badge_services
has_pull_request_templateno
How it's scored
74.4/80Monthly downloads — 382,587 downloads/month across npm
0/20Registry dependents — not reported by this ecosystem
Inputs used
packages@openrouter/mcp, @openrouter/agent
dependents
ecosystemsnpm
total_downloads
monthly_downloads382,587
Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.

Sustainability & Governance

Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?

60Moderate · 23% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
5.8/22.5Commit distribution — top contributor authored 74% of commits
12.2/13.5Contributor breadth — 9 contributors
10/10OpenSSF Scorecard: Contributors — project has 5 contributing companies or organizations
Inputs used
bus_factor1
contributors_sampled9
top_contributor_share0.743
How it's scored
6/42Issue resolution — 14% of issues closed
23.8/30PR acceptance — 58/73 decided PRs merged
7.5/13Newcomer PR acceptance — 15/26 first-time contributors' PRs merged in 30d
6/15OpenSSF Scorecard: Code-Review — Found 8/18 approved changesets -- score normalized to 4
Inputs used
merged_prs58
open_issues6
closed_issues1
prs_merged_7d3
prs_decided_7d3
prs_merged_30d17
prs_decided_30d29
issue_closed_ratio0.143
closed_unmerged_prs15
first_time_authors_30d3
first_time_prs_merged_30d15
first_time_prs_decided_30d26
How it's scored
30/30Ownership backing — organization-owned
0/20Verified domain
25/25Owner reach — 3,536 followers of OpenRouterTeam
17.5/25Track record — 36 public repos, account ~3 yr old
Inputs used
followers3,536
owner_typeOrganization
is_verified
owner_loginOpenRouterTeam
public_repos36
account_age_days1,117

Package maintenance

100Exceptional
How it's scored
25/25Published & resolvable — 2 package(s) on npm
35/35Publish recency — latest publish 9 days ago
20/20Version history — 13 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packages@openrouter/mcp, @openrouter/agent
ecosystemsnpm
any_deprecatedno
min_days_since_publish9

Engineering Quality

Are baseline engineering and documentation practices in place?

75Good · 19% of overall
How it's scored
24/24CI workflows — 3 workflow(s)
24/24Tests present
16/16Linter config — biome.json
0/9.6Pre-commit hooks
0/6.4.editorconfig
18/20OpenSSF Scorecard: CI-Tests — 18 out of 19 merged PRs checked by a CI test -- score normalized to 9
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configno
How it's scored
30/30README
0/25Documentation directory
15/15Documentation / homepage site — https://openrouter.ai/docs/sdks/typescript/call-model
10/10Repository description
0/10Topics
10/10Wiki
Inputs used
topics
has_wikiyes
homepagehttps://openrouter.ai/docs/sdks/typescript/call-model
has_readmeyes
has_docs_dirno
has_descriptionyes

Security

Are visible security and supply-chain practices strong, without unresolved high-risk jurisdiction exposure?

50Moderate · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
2.2/7.5Branch-Protection — branch protection is not maximal on development and all release branches
2.2/2.5CI-Tests — 18 out of 19 merged PRs checked by a CI test -- score normalized to 9
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
3/7.5Code-Review — Found 8/18 approved changesets -- score normalized to 4
2.5/2.5Contributors — project has 5 contributing companies or organizations
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
0/2.5License — license file not detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — no data
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 16 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3.8
Excluded from scoring (no data or not applicable): packaging, signed_releases. Remaining weights renormalized.

Dependency advisories

100Exceptional
How it's scored
35/35Direct dependencies free of known advisories — no direct dependency carries a known advisory
25/25Indirect dependencies free of known advisories — no indirect dependency carries a known advisory
0/40No advisories left outstanding — no advisory carries a publication date
Inputs used
sourceosv
advisories0
affected_packages0
assessed_packages95
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the npm:@openrouter/mcp@0.0.1 runtime dependency closure — what installing the published package pulls in — 95 packages. Reachability is not analyzed.

AI Readiness

How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight (4%): agent tooling is a real maintenance signal, but a repository with none can still reach 100/100.

55Moderate · 4% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 89 of 94 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.947
agent_instruction_files
agent_instruction_max_bytes
How it's scored
0/18One-command bootstrap
22/22Automated tests
11/11Lint / format config — biome.json
11/11Static type checking — packages/agent/tsconfig.json, packages/mcp/tsconfig.json
10/10Reproducible environment — lockfile
10/10Demonstrated agent practice — 12 of the last 100 commits agent-authored or agent-credited
0/8Automated maintenance — no automated dependency updates observed
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfilespnpm-lock.yaml
has_dockerfileno
typed_languageyes
bootstrap_files
has_devcontainerno
has_linter_configyes
typecheck_configspackages/agent/tsconfig.json, packages/mcp/tsconfig.json
agent_commit_share0.12
toolchain_manifests
dependency_bot_commit_share0
How it's scored
45/45Type-checkable code — TypeScript (statically typed)
54.1/55Manageable file sizes — 2/120 source files over 60KB
Inputs used
primary_languageTypeScript
largest_source_bytes181,323
source_files_sampled120
oversized_source_files2
How it's scored
0/40API schema (OpenAPI/GraphQL/proto)
20/20MCP server
0/40Runnable examples
Inputs used
example_dirs
has_mcp_signalyes
api_schema_files

Key facts

19GitHub stars
9contributors
152commits, last 12 months
2days since last push
7releases
1bus factor
6open issues
npmpackage ecosystems

Data collection warnings

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

More detail

Star and fork history 0 ★ / 11 ⇿
0Stars
11Forks
7Releases

When each star and fork was added, collected from GitHub and bucketed by day. Cumulative growth sits directly above the daily additions it is made of, so the two read against each other: steady organic accretion looks nothing like an abrupt, short-lived burst. Where that difference is measurable, it is reported as growth authenticity.

0246810121112026-042026-062026-07
Major 0Minor 0Patch 1
OpenSSF Scorecard 3.8 / 10
3.8aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).Scorecard v5.5.0 · 2026-08-03 04:51 UTC

10Binary-Artifactsno binaries found in the repo
3Branch-Protectionbranch protection is not maximal on development and all release branches
9CI-Tests18 out of 19 merged PRs checked by a CI test -- score normalized to 9
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
4Code-ReviewFound 8/18 approved changesets -- score normalized to 4
10Contributorsproject has 5 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
0Licenselicense file not detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
n/aPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities16 existing vulnerabilities detected
Direct dependencies 5
RegistryPackageVersion constraintManifest
npm@openrouter/sdk^0.13.7packages/agent/package.json
npmzod^4.0.0packages/agent/package.json
npm@modelcontextprotocol/sdk^1.29.0packages/mcp/package.json
npm@openrouter/agentworkspace:*packages/mcp/package.json
npmzod^4.0.0packages/mcp/package.json
All dependencies not collected

The resolved dependency set could not be collected for this report: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Dependency advisories 0

Installing npm:@openrouter/mcp@0.0.1 pulls in 95 packages, direct and transitive: 0 carry known advisories, of which 0 are direct dependencies.

No known advisories affect the assessed dependencies.

An advisory means the version recorded in the dependency graph falls inside an advisory’s affected range. Reachability is not analysed, and the graph includes development and test pins — a finding may concern tooling rather than shipped software.

Raw JSON report machine-readable
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 1889,
      "has_wiki": true,
      "homepage": "https://openrouter.ai/docs/sdks/typescript/call-model",
      "languages": {
        "TypeScript": 1376001
      },
      "pushed_at": "2026-07-31T21:54:32Z",
      "created_at": "2026-03-27T18:10:53Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-30T16:01:38Z",
      "description": "OpenRouter Agent SDK",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": null,
      "primary_language": "TypeScript",
      "significant_languages": [
        "TypeScript"
      ]
    },
    "owner": {
      "blog": "https://openrouter.ai",
      "name": "OpenRouter",
      "type": "Organization",
      "login": "OpenRouterTeam",
      "company": null,
      "location": null,
      "followers": 3536,
      "avatar_url": "https://avatars.githubusercontent.com/u/139423088?v=4",
      "created_at": "2023-07-13T03:37:47Z",
      "is_verified": null,
      "public_repos": 36,
      "account_age_days": 1117
    },
    "license": {
      "state": "absent",
      "spdx_id": null,
      "raw_spdx": null,
      "file_present": false,
      "scorecard_found": false,
      "profile_has_license": false
    },
    "activity": {
      "releases": [
        {
          "tag": "@openrouter/agent@0.8.0",
          "kind": "other",
          "published_at": "2026-07-22T20:50:38Z"
        },
        {
          "tag": "@openrouter/agent@0.7.2",
          "kind": "other",
          "published_at": "2026-06-17T13:50:00Z"
        },
        {
          "tag": "@openrouter/agent@0.7.1",
          "kind": "other",
          "published_at": "2026-06-03T17:10:40Z"
        },
        {
          "tag": "0.7.1",
          "kind": "patch",
          "published_at": "2026-06-03T14:41:39Z"
        },
        {
          "tag": "@openrouter/agent@0.7.0",
          "kind": "other",
          "published_at": "2026-05-18T21:45:22Z"
        },
        {
          "tag": "@openrouter/agent@0.6.0",
          "kind": "other",
          "published_at": "2026-05-14T15:59:51Z"
        },
        {
          "tag": "@openrouter/agent@0.5.0",
          "kind": "other",
          "published_at": "2026-05-06T00:53:03Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "d03060256c4c7d55432e508544d8d35d2f65d4bb",
          "body": "* ci(release): dispatch the Python and Go ports on publish (HOP C)\n\nThe Python and Go ports of @openrouter/agent track this repo as their reference\nspec, but nothing told them when a version shipped. Both were generated by hand\nagainst 0.7.2 and have sat a minor version behind since — missing HooksM\n[…]\nf the port\n  repos cannot resolve, making them fail on checkout instead of degrading.\n\n---------\n\nCo-authored-by: Claude Fable 5 <noreply@anthropic.com>\nCo-authored-by: LukasParke <luke@openrouter.ai>",
          "is_bot": false,
          "headline": "ci(release): dispatch the Python and Go ports on publish (HOP C) (#84)",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-30T15:58:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "78c562ef53da0edd84dfbcc6d6ee38a095d72b37",
          "body": "* feat(agent): doom-loop detection for the tool-execution loop\n\nOpt-in via doomLoop on callModel. Deterministic detection of runs that\nstop making progress: consecutive identical tool calls (per-tool\nfingerprint streaks over tool-declared loopKey identity, incl. repeated\nempty and invalid-JSON calls\n[…]\n--------\n\nCo-authored-by: cortex-github-agent[bot] <305999463+cortex-github-agent[bot]@users.noreply.github.com>\nCo-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(agent): doom-loop detection for the tool-execution loop (#73)",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-29T17:42:53Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "89ae3ff3f5a6b929337ed347b388937bc80339ac",
          "body": "…flow (#82)\n\n* fix(release): use real npm trusted publishing (OIDC) for publish workflow\n\nThe release workflow claimed to use OIDC but never did. changesets/action\nonly checks `process.env.NPM_TOKEN`, and the workflow passed the secret as\n`NODE_AUTH_TOKEN`, so the action logged \"No NPM_TOKEN found, \n[…]\nhat is the primary release path.\n\nSame doc-drift class flagged in review; no behavior change.\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n\n---------\n\nCo-authored-by: Claude <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(release): use real npm trusted publishing (OIDC) for publish work…",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-28T20:26:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fc8a7f7298ed3f9fdbb3009fbfbd1d05ec5a0264",
          "body": "…t npm publish (DEV-684) (#80)\n\n* fix(release): mark @openrouter/mcp private until its first npm publish\n\nThe push-to-main publish leg fails on every release because changeset\npublish retries @openrouter/mcp, whose first-ever publish 404s — trusted\npublishing (OIDC) cannot create a brand-new package\n[…]\nrusted publisher\nconfig still has to happen npm-side before the publish leg can pick it up.\n\nCo-authored-by: Cursor <cursoragent@cursor.com>\n\n---------\n\nCo-authored-by: Cursor <cursoragent@cursor.com>",
          "is_bot": false,
          "headline": "fix(release): reset unpublished @openrouter/mcp to 0.0.1 for its firs…",
          "author_name": "Dennis Jeong",
          "author_login": "w0nche0l",
          "committed_at": "2026-07-22T21:41:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "680bceb4598f228d3e2ec58e2416e4335cdff059",
          "body": "Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore: version packages (#57)",
          "author_name": "github-actions[bot]",
          "author_login": "github-actions[bot]",
          "committed_at": "2026-07-22T20:49:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61a2a9ab312f973b599bad886e97a270f798565f",
          "body": "* docs: update README banner to rebrand design system\n\nGenerated from the openrouter-web monorepo banner template\n(scripts/repo-banners/ — see its AGENTS.md for the process).\nInk background, Volt accent, Gordita/Plus Jakarta Sans type, and the\ncanonical brand lockup, per the rebrand DESIGN.md.\n\n* docs: restore README trailing newline",
          "is_bot": false,
          "headline": "docs: rebrand README banner (#71)",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-21T21:37:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6807c51d56a35e07a2c549d92ab6d8a0c106ac0a",
          "body": "…e: true (DEV-658) (#68)\n\n* fix(agent): append default final-answer directive for bare allowFinalResponse: true\n\nBare `allowFinalResponse: true` stripped tools on the forced final turn\nbut gave the model no signal that this was its last turn. Models that\nemit tool-call syntax as text (e.g. GLM) woul\n[…]\nl turn.\n\nChangeset bumped to minor: runs that previously ended on a halted\ntool-call turn now make one additional request by default.\n\n* docs(skill): update example for default-on final turn semantics",
          "is_bot": false,
          "headline": "fix(agent): default final-answer directive for bare allowFinalRespons…",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-21T19:20:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "620a5c8e42fece2b795a60526a4f9dd229c12f11",
          "body": "* testing actions\n\n* testing actions 2\n\n* testing force node24\n\n* testing force node24 2\n\n* force node24 final note: The warning about using node20 will still be present. However, node24 will be used with the environment variable set\n\n* removed comment from readme\n\n* decided against forcing node24, \n[…]\n chore(ci): bump remaining v4 actions to v6 (structural-gate, bump-openrouter-sdk); restore README trailing newline\n\n---------\n\nCo-authored-by: Luke Parke <5702154+LukasParke@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Fix/force node24 (#36)",
          "author_name": "Logan Lyons",
          "author_login": "okacodes",
          "committed_at": "2026-07-21T15:23:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e499be1c336b05238cf7479826675c74dc17a0fc",
          "body": "* testing actions\n\n* testing actions 2\n\n* testing force node24\n\n* testing force node24 2\n\n* force node24 final note: The warning about using node20 will still be present. However, node24 will be used with the environment variable set\n\n* implemented vitest coverage and update vitest to most up to dat\n[…]\ntted coverage artifacts, gitignore coverage/, package-relative coverage glob, align coverage-v8 pin, regen lockfile\n\n---------\n\nCo-authored-by: Luke Parke <5702154+LukasParke@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Feat/vitest upgrade (#37)",
          "author_name": "Logan Lyons",
          "author_login": "okacodes",
          "committed_at": "2026-07-21T15:22:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cb83f45209ff66f8c58077f4e0a85d35f884afdb",
          "body": "…#67)\n\n* feat: add HooksManager lifecycle hook system to callModel (rebased onto main)\n\nSquashed rebase of feat/hooks-manager (fe319a2) onto origin/main (8edae63).\nConflicts resolved:\n- async-params/call-model: keep both allowFinalResponse and hooks options\n- model-result: runToolWithHooks now propa\n[…]\n.ts priming the\nmanager with run B's id and asserting run A's tool hooks still see\nrun A's state id.\n\n---------\n\nCo-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(hooks): PostModelCall telemetry hook + SessionEnd usage totals (…",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-17T15:58:23Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c83cceb17ec1d66b9a1fd2d46ac8ac9b6e60fa4c",
          "body": "* feat(agent): versioned ConversationState serialization contract (PR-6)\n\n* fix: address review — restore createInitialState JSDoc placement, validate createdAt/updatedAt in deserializer\n\n* style: fix biome formatting\n\nCo-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.githu\n[…]\nn' alongside id/createdAt/updatedAt\n- new test: reshaped version-2 blob still gets the version error\n\n---------\n\nCo-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(agent): versioned ConversationState serialization contract (#66)",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-17T15:55:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "09a041ea717b384c6c85d7c81ef391b170b0dd8f",
          "body": "* fix(agent): infer tool context types from contextSchema (PR-3)\n\ntool() now preserves the concrete contextSchema type through its\noverloads: execute's ctx.local is typed from the schema, and\ncallModel's context map slots accept/reject the real per-tool shape.\nNo more 'ctx.local as X' or 'context: m\n[…]\ne behavior change (one sanctioned construction-\ntime write cast for the readonly field).\n\n315 tests green; type gates verified red/green.\n\n* refactor: remove no-op WithConcreteContextSchema per review",
          "is_bot": false,
          "headline": "fix(agent): infer tool context types from contextSchema (#65)",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-17T15:55:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "80ff8a730292aa00a3acfcce6ab1e9f5a6a7f0de",
          "body": "* feat: add HooksManager lifecycle hook system to callModel (rebased onto main)\n\nSquashed rebase of feat/hooks-manager (fe319a2) onto origin/main (8edae63).\nConflicts resolved:\n- async-params/call-model: keep both allowFinalResponse and hooks options\n- model-result: runToolWithHooks now propagates e\n[…]\nsult-hooks.test.ts priming the\nmanager with run B's id and asserting run A's tool hooks still see\nrun A's state id.\n\n---------\n\nCo-authored-by: Luke Parke <5702154+LukasParke@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat: add HooksManager lifecycle hook system to callModel (#7)",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-07-17T15:22:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e4d06e38215d6eafbd5c198e3485f476e65d26f0",
          "body": "…tools (#64)\n\n* feat(agent): persist unresolved manual tool calls as awaiting_client_tools\n\nManual tools (execute: false) stopped the loop without persisting\nanything; getPendingToolCalls() returned [] and cold-start consumers\ncould not see the calls in serialized state. Persist unresolved manual\nca\n[…]\nPR #63 hardened the no-tool exit to reject empty final outputs\n(validateFinalResponse), which this test's mock tripped after rebase.\nA real resumed response carries assistant content; fixture updated.",
          "is_bot": false,
          "headline": "feat(agent): persist unresolved manual tool calls as awaiting_client_…",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-15T17:20:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d96cd9fc589c27978bcdc2fd1921f754be88e3f0",
          "body": "… (#63)\n\n* fix(agent): tolerate empty final response after completed tool rounds\n\nMini-class models intermittently return an empty final turn when the\ntool call was the answer; the SDK threw away successful runs. Now:\nretry the follow-up once; if still empty, resolve with empty text.\nstrictFinalResp\n[…]\n\npassed validateFinalResponse but was never executed — silently dropped.\nThe retry now strips those fields (mirroring makeFinalResponseRequest)\nto coerce a text turn, with a test pinning the behavior.",
          "is_bot": false,
          "headline": "fix(agent): tolerate empty final response after completed tool rounds…",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-15T16:37:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c020bc7c86d2f743ecf9158ca3c9ff7b315e43b3",
          "body": "…(#61)\n\nA resumed ConversationState with a bare-string input pushed the raw\nstring into the request input array; OpenResponses rejects that with a\n400. Use normalizeInputToArray in the history+fresh branch, matching\nthe no-history branch. Found battletesting a production agent.\n\n- 3-line fix in ModelResult.initStream history branch\n- regression test: resume-string-input.test.ts (fails without fix)",
          "is_bot": false,
          "headline": "fix(agent): normalize string input when resuming with loaded history …",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-15T16:36:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1362232975f0254343f9842f30ec1b35d391f4fe",
          "body": "…semantics (#62)",
          "is_bot": false,
          "headline": "docs(agent): fix README/API drift — ctx.local, StateAccessor, stream …",
          "author_name": "Luke Parke",
          "author_login": "LukasParke",
          "committed_at": "2026-07-15T16:36:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8edae63f4f6fe89e146f3abbf6d24dab7a164681",
          "body": "… sending broken history (#59)\n\nWhen a round mixes auto-executed tools with manual (execute-less) client\ntools, executeToolRound returns no output for the manual calls, yet the\nloop still made a follow-up request whose input carried the manual\nfunction_call with no matching function_call_output. Pro\n[…]\n loop and surface the response when any tool call in the round\nhas no output — mirroring the existing all-manual hasExecutableToolCalls\nguard — so the caller can execute its manual calls and continue.",
          "is_bot": false,
          "headline": "fix(agent): stop tool loop on unresolved manual tool calls instead of…",
          "author_name": "ayush-or",
          "author_login": "ayush-or",
          "committed_at": "2026-07-08T17:00:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4e1f98d74902c845a0be5a7e161be832cce3d25f",
          "body": "…ckage\n\nfeat(mcp): add @openrouter/mcp package",
          "is_bot": false,
          "headline": "Merge pull request #56 from OpenRouterTeam/worktree-openrouter-mcp-pa…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-30T17:59:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d3539eea1056e629cd6930d1682f0496d7e98820",
          "body": "Tighten the fallback test to pin Date.now() substitution (before <= cachedAt <=\nnow) rather than mere non-negativity, catching a regression to a hard-coded\nsentinel.",
          "is_bot": false,
          "headline": "test(mcp): assert negative-cachedAt fallback uses a fresh timestamp",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-30T15:05:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "209499abacd6783ee5c98155bb2a676e3932c3f4",
          "body": "…'t untype client tools\n\nAdds a `source: 'client' | 'mcp'` discriminant to ToolExecutionResult and the\nstreaming ToolResultEvent. An MCP tool's `unknown` output no longer collapses the\nwhole result union — narrowing on source keeps client-tool results precise and\nisolates MCP results as unknown.\n\n- \n[…]\n.\n\nVerified: full monorepo build/typecheck/lint/test green (295 agent + 39 mcp tests,\nincl. type-level tests); sentrux structural gate passes (cycles 1->1, quality up,\nno complex-function regression).",
          "is_bot": false,
          "headline": "feat(agent,mcp): discriminate tool results by source so MCP tools don…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-30T15:03:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "38bf22d2b7d3a0c12e7bce70465ed68602500e5a",
          "body": "isFiniteEpoch's value>=0 tightening means serializeServer now substitutes\nDate.now() for a negative cachedAt; assert that branch produces a valid,\nnon-negative epoch that passes the read validator.",
          "is_bot": false,
          "headline": "test(mcp): cover serializeServer's negative-cachedAt write-side fallback",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T18:28:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1eb518475b25823a609327344daf21e0d43c6e99",
          "body": "An epoch (ms) is non-negative by definition; rejecting negatives validates\nthe invariant at the snapshot boundary instead of relying on downstream\nmaxAge arithmetic to fail-safe on a clock-skewed/garbage value.",
          "is_bot": false,
          "headline": "fix(mcp): reject negative cachedAt in isFiniteEpoch",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T17:57:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c638f43c486244028d2ec57c9556ba60ba3ef726",
          "body": "…ializer\n\nExtract the 'valid cachedAt' predicate into a single exported isFiniteEpoch\nin cache-types.ts, consumed by both the read-side isSerializedMCPServer\nvalidator and the write-side serializer, so the two can't drift if the rule\nis later tightened. No behavior change.",
          "is_bot": false,
          "headline": "refactor(mcp): share isFiniteEpoch between snapshot validator and ser…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T17:45:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "43ed315256dd5e8ab6177fddd77dddb5ddefea06",
          "body": "A poisoned snapshot with NaN/Infinity cachedAt passed isSerializedMCPServer\n(typeof NaN === 'number'), making tryCacheHit's staleness comparison always\nfalse so the entry never expired. Mirror serialize.ts's isExpiry guard with a\nNumber.isFinite check. Also document that includeTools/excludeTools apply to\ndiscovered MCP tools only; resource tools are gated by the resources option.",
          "is_bot": false,
          "headline": "fix(mcp): reject non-finite cachedAt in snapshot validator",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T17:31:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37e420566e5692563f430c6ed596649924a6f2ef",
          "body": "Extract the buildTools-args and serializeServer-input assembly out of\nmakeHandle into buildToolsArgs/serializeArgs helpers, dropping makeHandle\nfrom cc=17 to under the gate's threshold of 15. No behavior change.",
          "is_bot": false,
          "headline": "fix(mcp): drop makeHandle below max cyclomatic complexity",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T16:41:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "60a0ae76fc8053076c383bc291dbd49161c3f98a",
          "body": "The sentrux structural gate failed on cycle + quality + complexity\nregressions. Resolved without weakening the baseline:\n\n- extract makeHandle/freshConnect/listToolDefs into a leaf handle.ts so\n  create-mcp-tools and rehydrate no longer form a cycle\n- move MCPTransportKind to a leaf transport-types.\n[…]\nd into)\n- collapse tryCacheHit's option forwarding into a data-driven helper to\n  drop it back under the max-cyclomatic-complexity threshold\n\nGate now: cycles 1->1, quality 0.51->0.52, no degradation.",
          "is_bot": false,
          "headline": "fix(mcp): pass structural-gate by eliminating import-cycle regressions",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T16:37:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a94f9c4c25357cd63afb66d81b192186033b08c7",
          "body": "… thread clientInfo\n\nAddresses remaining Perry review threads:\n- freshConnect tears down the connection if listToolDefs or the initial\n  writeCache throws, so a failed setup never leaks the open transport\n- the tools/list_changed auto-refresh now .catch()es, so a failed refresh\n  cannot escape as an unhandled rejection; listeners keep the last good set\n- thread clientInfo through the cache-hit/rehydrate path so a warm handle\n  initializes with the same client identity as a cold one",
          "is_bot": false,
          "headline": "fix(mcp): close connection on setup failure, guard refresh rejection,…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T16:15:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c2a376c4db2084a4f5991c2cbcc23eb8524b92e6",
          "body": "…pagination\n\n- freshConnect breaks the recursive cache-fallback loop (no-auth/expired\n  snapshots no longer re-read their own cache and re-enter rehydrate)\n- reconstruct MCPAuth from a credential-bearing snapshot so reconnects\n  authenticate when no explicit auth is passed\n- thread toolNamePrefix/in\n[…]\nde/exclude/resources/emitProgress/\n  autoRefresh/cacheCredentials through cache hits so filters and prefixes\n  survive rehydration\n- follow nextCursor in tools/list, resources/list, and templates/list",
          "is_bot": false,
          "headline": "fix(mcp): correct cache/rehydrate option loss, credential reuse, and …",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T16:12:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "26336b5c44e5591b380ca4c41bf93b05f0ccdfe2",
          "body": "Expose remote MCP server tools (Streamable HTTP / SSE) as callModel tools,\nwith pluggable auth, faithful JSON-Schema->Zod conversion, and a\nserializable, rehydratable cache.",
          "is_bot": false,
          "headline": "feat(mcp): add @openrouter/mcp package",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-26T15:24:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "adc7939f4b7ed85b1a060d13433b8be6063cff73",
          "body": "chore: version packages",
          "is_bot": false,
          "headline": "Merge pull request #35 from OpenRouterTeam/changeset-release/main",
          "author_name": "Ben",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-17T13:49:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "21c2268117882ed291829fee1456b8adbf4d5135",
          "body": null,
          "is_bot": true,
          "headline": "chore: version packages",
          "author_name": "github-actions[bot]",
          "author_login": "github-actions[bot]",
          "committed_at": "2026-06-17T12:55:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4d79d358cfe7945a35f2ad4a4d7d9ff605a43dfc",
          "body": "…0.13.7\n\nchore: bump @openrouter/sdk to 0.13.7",
          "is_bot": false,
          "headline": "Merge pull request #53 from OpenRouterTeam/chore/bump-openrouter-sdk-…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-17T12:55:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a5341f21555b5d2d982484c199d7d9c3093eabe6",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: bump @openrouter/sdk to 0.13.7",
          "author_name": "Ben Heidorn",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-17T12:47:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "839b3819869cb944c00077159fc49bb209cb45a6",
          "body": "…-gh-token\n\nci: use GH_TOKEN for release-chain workflows",
          "is_bot": false,
          "headline": "Merge pull request #51 from OpenRouterTeam/sdk-bot/release-automation…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-03T17:50:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "168da4755a64b8cdcf1677fb2a3edeb1e3c2f359",
          "body": "Switch the release-chain workflows from SUBTREE_PUSH_PAT to the dedicated\nGH_TOKEN PAT now configured on this repo. Follows up the merged #49, which\nlanded before this rewire.",
          "is_bot": false,
          "headline": "ci: use GH_TOKEN secret for PR open/merge and monorepo dispatch",
          "author_name": "OpenRouter SDK Bot",
          "author_login": null,
          "committed_at": "2026-06-03T17:44:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b7ceb298dab6b1b72233bbbf3630fdc9354b7a9e",
          "body": "…-bump\n\nci: auto-bump @openrouter/sdk and dispatch monorepo on release",
          "is_bot": false,
          "headline": "Merge pull request #49 from OpenRouterTeam/sdk-bot/release-automation…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-06-03T16:33:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8917615dd47bbf9b9da7240b70921c18aaa5ada8",
          "body": "Drop the GitHub App (AGENT_BUMP_APP_*) and WEB_DISPATCH_PAT in favor of the\norg's existing SUBTREE_PUSH_PAT for opening/merging the bump PR and for the\nmonorepo dispatch. Slack alerts use the org-level CI_RELEASE_ALERT_SLACK_*\nsecrets, so no new secrets are required on this repo.",
          "is_bot": false,
          "headline": "ci: reuse SUBTREE_PUSH_PAT instead of new App/PAT secrets",
          "author_name": "OpenRouter SDK Bot",
          "author_login": null,
          "committed_at": "2026-06-03T14:39:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aa4f6b525fc518c7f29f259f2b872b73dcaff30a",
          "body": "chore(agent): bump version to 0.7.1",
          "is_bot": false,
          "headline": "Merge pull request #50 from OpenRouterTeam/release/v0.7.1",
          "author_name": "Ben",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-03T14:35:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "33a807abc07ecde1b7a4fb0f7fdf853ca7c2a4a9",
          "body": "Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(agent): bump version to 0.7.1",
          "author_name": "Ben Heidorn",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-03T14:32:45Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "faaf82cf0cf69b32719db403370cdd19686b83e3",
          "body": "fix(agent): update OutputInputImage re-export for SDK compat and persist user input",
          "is_bot": false,
          "headline": "Merge pull request #48 from OpenRouterTeam/cyb/user-input-persistence",
          "author_name": "Ben",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-03T14:13:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8c1196c700b2e8a835703ab229e4a098193215f3",
          "body": "HOP A + HOP B of the SDK release chain:\n\n- bump-openrouter-sdk.yaml: receives openrouter-sdk-published dispatch (or\n  manual workflow_dispatch), opens a PR bumping @openrouter/sdk in\n  packages/agent (caret floor + relock + changeset) via an App token so Perry\n  and CI run, then self-polls Perry + C\n[…]\non a real @openrouter/agent publish (changesets published\n  output, or npm version diff on manual publish), dispatch\n  openrouter-agent-published to openrouter-web to kick off the server-tools\n  bump.",
          "is_bot": false,
          "headline": "ci: auto-bump @openrouter/sdk and dispatch monorepo on release",
          "author_name": "OpenRouter SDK Bot",
          "author_login": null,
          "committed_at": "2026-06-03T14:08:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7b6704ae792df992df15dcf56c04a0afffe412fe",
          "body": "Add assistant-item assertion to the streaming-failure regression test,\nmaking the atomicity invariant explicit: after a successful retry, both\nexactly one user turn and one assistant turn are present in state.\n\nCo-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(agent): assert assistant message lands atomically with user input",
          "author_name": "Ben Heidorn",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-02T15:39:31Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ce00bf9bede9a68d1b38d798c906c72356cc97a2",
          "body": "Move fresh user-item persistence from initStream (after ok:true) to\nsaveResponseToState (after stream consumption). Previously, an ok:true\nresponse followed by a mid-stream failure would leave the user turn in\nstate with no assistant reply — on retry the same input was appended\nagain, producing dupl\n[…]\n a\nretry starts clean.\n\nAdds a streaming-failure regression test (ok:true + stream error + retry)\nto user-input-persistence.test.ts.\n\nCo-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(agent): persist user input atomically with assistant output",
          "author_name": "Ben Heidorn",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-02T15:14:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "76876339a98105b868cf80e1a978d16e29e48e78",
          "body": "…ist user input\n\n- Replace removed `OutputInputImage` with `OutputImage` + backward-compat\n  alias (`OutputImage as OutputInputImage`) after @openrouter/sdk dropped\n  the old export name.\n- Persist fresh user input items to state only after the API call succeeds,\n  preventing duplicates on transient retries.\n\nCo-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(agent): update OutputInputImage re-export for SDK compat and pers…",
          "author_name": "Ben Heidorn",
          "author_login": "Cybourgeoisie",
          "committed_at": "2026-06-02T11:46:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d8a8f659187994cca917f6d1e7dfef140c9e4e25",
          "body": "…ponse-470ET\n\nfeat(agent): add allowFinalResponse option to callModel",
          "is_bot": false,
          "headline": "Merge pull request #44 from OpenRouterTeam/claude/add-allow-final-res…",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-18T21:44:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7a404a4d633b562abbde4aebe1931cb7ea24084c",
          "body": "Bumps @openrouter/agent to 0.7.0 and consumes the\nallowFinalResponse changeset into the CHANGELOG.\n\nhttps://claude.ai/code/session_01Up1UhjF9mQBDq4rFwULovN",
          "is_bot": false,
          "headline": "chore: version packages (0.7.0)",
          "author_name": "Claude",
          "author_login": "claude",
          "committed_at": "2026-05-18T21:41:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8057b775f48e7db6d71aaef6c434e85a9495e2e4",
          "body": "- Save real tool outputs to state BEFORE the HITL pause check so a\n  mixed batch (HITL + regular tools) doesn't drop the regular tool's\n  output from state on resume. Matches the in-loop ordering.\n- Apply nextTurnParams from executed tools so tools that mutate\n  request params (model, temperature, e\n[…]\nath that the\n  non-streaming fixtures didn't cover).\n- Add a unit test asserting that stubs land in the request but never\n  in persisted state.\n\nhttps://claude.ai/code/session_01Up1UhjF9mQBDq4rFwULovN",
          "is_bot": false,
          "headline": "fix(agent): address adversarial review of allowFinalResponse",
          "author_name": "Claude",
          "author_login": "claude",
          "committed_at": "2026-05-18T21:37:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ad789c80235777bc65765bc7cf49bbd07b4995d4",
          "body": "…ow-up\n\nAddress review feedback on the allowFinalResponse implementation:\n\n- Execute pending tool calls when the trigger fires so they have real\n  matching outputs in the input, rather than always synthesizing stubs.\n  Stubs now only cover any manual (non-executable) tool calls in the\n  halted turn \n[…]\nbled by the falsy check).\n- Add 6 unit tests that mock betaResponsesSend and assert the second\n  request shape, tool execution, and edge cases.\n\nhttps://claude.ai/code/session_01Up1UhjF9mQBDq4rFwULovN",
          "is_bot": false,
          "headline": "fix(agent): execute pending tool calls before allowFinalResponse foll…",
          "author_name": "Claude",
          "author_login": "claude",
          "committed_at": "2026-05-18T21:25:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c33195cf89e10316fc9ddbf222d6717e831db04c",
          "body": "Sibling of stopWhen. When the loop is halted by stopWhen while the last\nresponse still contains tool calls, set allowFinalResponse: true (or a\nstring) to make one more no-tools request so the loop ends with a\nnatural-language summary instead of an unfinished tool call. A string\nvalue is appended as a final user message. The full accumulated input\narray and the original instructions are sent.\n\nhttps://claude.ai/code/session_01Up1UhjF9mQBDq4rFwULovN",
          "is_bot": false,
          "headline": "feat(agent): add allowFinalResponse option to callModel",
          "author_name": "Claude",
          "author_login": "claude",
          "committed_at": "2026-05-18T21:05:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7222108ee9bc2de1e382d4f25c4ec517029e97bf",
          "body": "chore: version packages (0.6.0)",
          "is_bot": false,
          "headline": "Merge pull request #43 from OpenRouterTeam/changeset-release-0.6.0",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-14T15:36:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "660731ecd645c28fad40f0eefed9deae857adefd",
          "body": "The 0.5.0 → 0.6.0 work (notably the HITL tooling that landed earlier)\nshifted the structural metrics far enough that the gate flags a drop\nagainst the stale baseline even on no-source-change PRs. Re-baselining\ncaptures the current state so subsequent PRs are measured from here.\n\n  quality_signal: 0.5195 → 0.5094\n  coupling_score: 0.4173 → 0.4286\n  total_import_edges: 127 → 133\n  cross_module_edges: 55 → 59",
          "is_bot": false,
          "headline": "chore: refresh sentrux baseline for 0.6.0 release",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-14T15:34:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "101f7f71d4a78a4e65c21c80e446541a9c57ba62",
          "body": "Consumes the changeset from #42 (remove implicit 5-step cap in\ncallModel). Bumps @openrouter/agent from 0.5.0 to 0.6.0 and updates\nthe CHANGELOG.",
          "is_bot": false,
          "headline": "chore: version packages (0.6.0)",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-14T15:27:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "06501a9116deb90761c6af1bce5c28f0849333ea",
          "body": "feat(agent): remove implicit 5-step cap in callModel",
          "is_bot": false,
          "headline": "Merge pull request #42 from OpenRouterTeam/remove-implicit-max-steps",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-14T15:26:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8e71f06024f41e60ccdc68577016637a31912779",
          "body": "When `stopWhen` is omitted, the tool-execution loop now runs until the\nmodel produces a turn with no tool calls instead of being silently\ncapped at 5 steps. Callers who want a bound must pass an explicit\n`stopWhen` (e.g. `stepCountIs(n)`, `maxCost(...)`).",
          "is_bot": false,
          "headline": "feat(agent): remove implicit 5-step cap in callModel",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-14T15:24:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3e5ef28746ede546293ee0b96a4143262294a0bf",
          "body": "chore(release): @openrouter/agent 0.5.0 (HITL tools)",
          "is_bot": false,
          "headline": "Merge pull request #40 from OpenRouterTeam/changeset/hitl-tools-release",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:52:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4cb0c3b4ff1f9534674cfd602213b2eddf80a0e0",
          "body": "Consumes the pre-existing `curvy-streams-watch` patch changeset (stream\ndetection regression coverage from #34) and adds a new minor changeset\ncovering the human-in-the-loop tool type merged in #38.",
          "is_bot": false,
          "headline": "chore: version packages for HITL tools release (0.5.0)",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:50:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c649c9cc99521ae9e3fbb42d109146f31f040ce1",
          "body": "feat(tools): add human-in-the-loop tool type",
          "is_bot": false,
          "headline": "Merge pull request #38 from OpenRouterTeam/feat/hitl-tools",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:48:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "396d44b3371df99ddf660288a8d53cd4fc810728",
          "body": "applyOnResponseReceivedHooks and executeToolRound grew cyclomatic\ncomplexity above 15 when HITL logic landed, tripping sentrux gate\n(complex functions 9 -> 11). Extract per-item helpers so the gate\nmatches baseline again:\n\n- applyOnResponseReceivedHooks: move per-item hook/validate logic into\n  comp\n[…]\nutForModel; describeNonRecord factors out the nested\n  typeof/Array.isArray ternary.\n\nPure refactor — no behavior change. All 280 unit tests still pass;\nsentrux gate reports \"No degradation detected\".",
          "is_bot": false,
          "headline": "refactor(tools): extract helpers to keep cc under structural gate",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:34:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "51a983d256d3594060283e6a95b3c2fac0e9cfff",
          "body": "- Preserve content-array FunctionCallOutputItem shapes in the HITL\n  onResponseReceived pipeline instead of JSON.stringify'ing them; export\n  isContentArray from conversation-state for reuse.\n- Require outputSchema on HITL tool configs and use it to validate\n  caller-supplied responses. Validation f\n[…]\nooksToFreshItems.\n- Drop the onResponseReceived call on SDK-generated outputs in\n  continueWithUnsentResults. The hook is now strictly for caller-supplied\n  outputs, matching its documented semantics.",
          "is_bot": false,
          "headline": "fix(tools): address HITL review feedback",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:19:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8ac64b5b1b9046ee3e3db9b42e19432452ccb4c5",
          "body": "chore: add sentrux architectural gating",
          "is_bot": false,
          "headline": "Merge pull request #39 from OpenRouterTeam/chore/sentrux-setup",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:16:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "74493d8889dd1b94d9b4533e803e99f0c02dd28a",
          "body": "Wire up sentrux via .mcp.json for Claude Code MCP integration and add\n.sentrux/rules.toml encoding the inner-loop -> lib -> api-shape-helpers\nlayering. CI gains a structural-gate job that installs sentrux v0.5.7 and\nruns `sentrux gate` against the saved baseline on every PR, blocking merges\nthat regress structural metrics (coupling, cycles, god files, complexity).",
          "is_bot": false,
          "headline": "chore: add sentrux architectural gating",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-06T00:14:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dc2ba48b88662be93c0ccd3e1e17ced61a2a30b8",
          "body": "- Route auto-resolvable checks through isAutoResolvableTool so pure-HITL\n  turns actually enter the execution loop and invoke onToolCalled.\n- Propagate HITL pauses out of executeToolRound; the outer loop now\n  persists pending calls under a new awaiting_hitl status and returns\n  before issuing a fol\n[…]\nput alongside error when onResponseReceived throws,\n  so the model can distinguish hook failure from tool-reported error.\n- Replace unsafe InputsUnion cast with a structurally-typed rewritten\n  array.",
          "is_bot": false,
          "headline": "fix(tools): close HITL execution gaps surfaced in review",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-05T23:47:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5155f4456ee3e0aed8734ad15a6949dbb068ee68",
          "body": null,
          "is_bot": false,
          "headline": "style: apply biome formatting and optional-chain suggestion",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-05T22:55:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8faf9bb846887c01a6d526ffc19526dfe30822d1",
          "body": "Adds a fourth client tool kind that extends manual-tool semantics with two\nasync hooks. `onToolCalled` runs when the model invokes the tool — returning\na value short-circuits like `execute`, returning `null` pauses the loop like\na manual tool. `onResponseReceived` runs on a later turn when an incomi\n[…]\non_call.name) a HITL\ntool, letting the tool post-process caller-supplied results before the\nmodel sees them.\n\nKeeps HITL control flow local to the tool definition instead of smeared\nacross the caller.",
          "is_bot": false,
          "headline": "feat(tools): add human-in-the-loop tool type",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-05-05T22:54:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ac3f82f8871caf7edb0aa0c38b2f892b73039014",
          "body": "Use getReader for stream shape checks",
          "is_bot": false,
          "headline": "Merge pull request #34 from w0nche0l/codex/getreader-stream-shape-check",
          "author_name": "robert-j-y",
          "author_login": "robert-j-y",
          "committed_at": "2026-05-01T22:38:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61aca10fd9434fe69fbe1e069e4b1858613a7da7",
          "body": null,
          "is_bot": false,
          "headline": "Add stream detection regression coverage",
          "author_name": "Dennis",
          "author_login": "w0nche0l",
          "committed_at": "2026-05-01T20:24:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "596726ca44d338ac4a8e916e3b28c50071ae1410",
          "body": null,
          "is_bot": false,
          "headline": "Use getReader for stream shape checks",
          "author_name": "Dennis",
          "author_login": "w0nche0l",
          "committed_at": "2026-05-01T08:28:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e3a868cfcc68eee699473e034f09d4efb7d6d41c",
          "body": "chore: convert to Turborepo monorepo (v2.9.6)",
          "is_bot": false,
          "headline": "Merge pull request #26 from OpenRouterTeam/turborepo-migration",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:59:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "232f6c50411eb8bce000f127a0f7655964844d74",
          "body": "# Conflicts:\n#\tpackage.json",
          "is_bot": false,
          "headline": "Merge remote-tracking branch 'origin/main' into turborepo-migration",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:59:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c3404cb0a37698590afb4bd37dbe7aa62a33a770",
          "body": "chore: version packages",
          "is_bot": false,
          "headline": "Merge pull request #29 from OpenRouterTeam/changeset-release/main",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:57:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dc6f77e83543163a0a4802853e8e7ac6cdccc42a",
          "body": "# Conflicts:\n#\tpackages/agent/tests/unit/has-approval-tools.test-d.ts\n#\tpackages/agent/tests/unit/server-tool-stream-narrowing.test-d.ts\n#\tpackages/agent/tests/unit/server-tool.test.ts",
          "is_bot": false,
          "headline": "Merge remote-tracking branch 'origin/main' into turborepo-migration",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:57:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "469aff365862127ee1460c4468b130dc9665c6ba",
          "body": null,
          "is_bot": true,
          "headline": "chore: version packages",
          "author_name": "github-actions[bot]",
          "author_login": "github-actions[bot]",
          "committed_at": "2026-04-20T18:55:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ffa0ae4b1d9bfa5f779303070c070b1da8aa494a",
          "body": "feat: add OpenRouter server tools to callModel",
          "is_bot": false,
          "headline": "Merge pull request #30 from OpenRouterTeam/add-server-tools",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:55:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "46a45f2ddd87a15669c82abb8946f67b8b10c4d9",
          "body": "Previously fell through to the default branch, which widened the runtime\nfilter by setting acceptGenericServerItem=true. The type-level\nKnownServerToolOutputs map already narrows this tool's output to the\n'openrouter:datetime' literal, so the runtime allow-list should match.",
          "is_bot": false,
          "headline": "fix: add explicit openrouter:datetime case to item stream scope",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:54:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e4e3ed5e0a4f132e8cae1c33d7831f65aa46c211",
          "body": null,
          "is_bot": false,
          "headline": "chore: add changeset for serverTool feature",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:26:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5e1db17d3c1a97316cc16efef25d4bd84366c948",
          "body": "- Defensively accept both `web_search_call` and `openrouter:web_search`\n  item literals for the `openrouter:web_search` request variant (runtime\n  filter + KnownServerToolOutputs type) so items aren't silently dropped\n  when the provider wraps results in OutputServerToolItem.\n- Replace the deny-list\n[…]\nem` / `isFileSearchCallOutputItem`\n  / `isImageGenerationCallOutputItem` branches in `handleOutputItemAdded`\n  and `handleOutputItemDone`; the `isServerToolResultItem` catch-all\n  already covers them.",
          "is_bot": false,
          "headline": "fix: tighten server tool output routing and filter correctness",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:12:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5965684ec15e2a8a4b9620e0eb933663132d8ad9",
          "body": "Adds first-class support for OpenRouter's server-executed tools\n(web_search, openrouter:datetime, image_generation, mcp, file_search,\ncode_interpreter, etc.) through a new `serverTool()` factory that lives\nalongside the existing `tool()`. Server tools are passed through to the\nAPI via the SDK's `Res\n[…]\ntions, proper typeguards\nthroughout. The `getItemsStream` scope filter is backed by a real\nruntime allow-list derived from `TTools`, so the type-level narrowing\ncorresponds to actual runtime behavior.",
          "is_bot": false,
          "headline": "feat: add OpenRouter server tools to callModel",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T18:09:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "54475e856547b690a175ecd36b6bb58c3e166097",
          "body": "# Conflicts:\n#\tpackage.json\n#\tpnpm-lock.yaml",
          "is_bot": false,
          "headline": "Merge remote-tracking branch 'origin/main' into turborepo-migration",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:52:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3baff61ca7e90871ccb65c7a826be64fc800e570",
          "body": "chore: version packages",
          "is_bot": false,
          "headline": "Merge pull request #28 from OpenRouterTeam/changeset-release/main",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:39:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ec4ff3f4d97c576aec4669ec8271b1d39cbd8a85",
          "body": "chore: bump @openrouter/sdk from 0.11.2 to 0.12.12",
          "is_bot": false,
          "headline": "Merge pull request #25 from OpenRouterTeam/chore/bump-sdk-0.12.12",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:39:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e23eb4256098b3ed21e9a13fbf8145e3691eb936",
          "body": null,
          "is_bot": false,
          "headline": "Merge remote-tracking branch 'origin/main' into turborepo-migration",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:36:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d3cff5f023d3c19a44083baa40c7e14a924f39cc",
          "body": "- Remove dead \"Check for pending changesets\" step in publish.yaml;\n  nothing reads steps.changesets.outputs.pending anymore.\n- Add \"globalDependencies\": [\"tsconfig.base.json\"] to turbo.json so\n  edits to the shared base tsconfig invalidate build/typecheck/test\n  caches instead of serving stale output.",
          "is_bot": false,
          "headline": "chore: address review feedback",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:35:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c41af68aa9c42103aa342c4787a68e5724a5e066",
          "body": null,
          "is_bot": true,
          "headline": "chore: version packages",
          "author_name": "github-actions[bot]",
          "author_login": "github-actions[bot]",
          "committed_at": "2026-04-20T17:28:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8e516726cad9139975e572c0ac448e012d6672d7",
          "body": "fix(openrouter): normalize hooks constructor option so shorthand input isn't silently dropped",
          "is_bot": false,
          "headline": "Merge pull request #27 from OpenRouterTeam/fix-e2e-tests",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:28:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ef157612ca213d23ef1bfbfec012db09144315bf",
          "body": "The underlying SDK only wires `hooks` into the request pipeline when it's an\n`SDKHooks` instance; passing a plain hook object (e.g. `{ beforeRequest }`) was\nsilently ignored, causing `hooks` to no-op for every caller who used the\nnatural shorthand.\n\n`new OpenRouter({ hooks })` now accepts an `SDKHoo\n[…]\nng from `@openrouter/sdk`, which hit the SDK's older `ModelResult` that\ndoesn't yield manual-tool `function_call` items from `finalResponse`) and\nbumps a 6s timeout on a Sonnet-with-tools test to 20s.",
          "is_bot": false,
          "headline": "fix(openrouter): normalize hooks constructor option",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:27:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b5743a1c3a25a7c815bae969bdcebb9bacf02248",
          "body": "Move @openrouter/agent into packages/agent/ under a pnpm workspace with\nTurborepo managing build/test/lint/typecheck task graphs at the root.\n\nRelease flow now uses `changeset publish`, which walks the workspace and\npublishes only packages whose on-disk version differs from npm — the\npipeline genera\n[…]\nEADME, CHANGELOG,\n.npmignore, vitest.config.ts, and tsconfig move into packages/agent/.\nBiome + grit plugins stay at the root; scope globs widened to cover\npackages/*. Vitest loads a shared root .env.",
          "is_bot": false,
          "headline": "chore: convert to Turborepo monorepo (v2.9.6)",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-20T17:01:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e2e61ceb20c248bfd5615abd35c9c38236a72f51",
          "body": "ci: split CI into parallel jobs",
          "is_bot": false,
          "headline": "Merge pull request #9 from OpenRouterTeam/ci-parallel-checks",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-17T14:26:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "36eda074cb0616122892f9e79c60c06977d94750",
          "body": "- Apply Biome formatter to tests/unit/conversation-state.test.ts to\n  break long array literals across multiple lines.\n- Have the e2e-tests job short-circuit with a warning when\n  OPENROUTER_API_KEY is not set instead of hard-failing, so the check\n  passes in contexts where the secret is unavailable (e.g. forks).",
          "is_bot": false,
          "headline": "ci: fix lint formatting and skip e2e tests when API key is absent",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-16T21:03:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "66f85cf1a54ff188c2eb11d333aa2020e82a2a8b",
          "body": "Combines the parallel-job split from this branch with the named-step\nstyle added on main. Also passes the OPENROUTER_API_KEY secret into\nthe e2e-tests job so it can actually run.",
          "is_bot": false,
          "headline": "ci: resolve merge conflict and wire OPENROUTER_API_KEY for e2e job",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-16T20:48:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8faa32c4d1069dfbdbf590ee73f78823bb024fdb",
          "body": null,
          "is_bot": false,
          "headline": "style: fix biome formatting in conversation-state test",
          "author_name": "Jakob Castro",
          "author_login": "jakobdylanc",
          "committed_at": "2026-04-16T17:05:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ec94de8c16fa114ba1e6369db25b4a2cd4ebc359",
          "body": "Co-Authored-By: jakob <jakob.castro@openrouter.ai>",
          "is_bot": true,
          "headline": "chore: bump @openrouter/sdk from 0.11.2 to 0.12.12",
          "author_name": "Devin AI",
          "author_login": "devin-ai-integration[bot]",
          "committed_at": "2026-04-16T16:45:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bc686d2249e577efb0f47b8e92d8574d66467e4a",
          "body": "Add support for passing input_text, input_image, and input_file content\nblocks directly as tool output without JSON.stringify. This enables tools\nthat produce rich outputs like images to pass them through efficiently.\n\nBump version to 0.3.2.",
          "is_bot": false,
          "headline": "feat: support content arrays in tool outputs",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-14T20:25:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "358253d5d3b6b36465afbf1c76f5445e963cdf2b",
          "body": "feat(tools): add toModelOutput for custom tool result formatting",
          "is_bot": false,
          "headline": "Merge pull request #24 from OpenRouterTeam/feature/to-model-output",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-14T20:21:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1e43375cc85a774afdd3faad92ec4be4f8ca73cc",
          "body": "Adds optional toModelOutput function to tool definitions that converts\nexecution output to model-facing content blocks (Responses API format).\nThis decouples what calling code receives from what the model sees.",
          "is_bot": false,
          "headline": "feat(tools): add toModelOutput for custom tool result formatting",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-14T19:52:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1572b88ba8c2f2b4bcb38a2526a894a3ea193917",
          "body": "chore: version packages",
          "is_bot": false,
          "headline": "Merge pull request #23 from OpenRouterTeam/changeset-release/main",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-12T14:43:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f7a90634fb25ccfb0794dd50c77aa1e6ea61d567",
          "body": null,
          "is_bot": true,
          "headline": "chore: version packages",
          "author_name": "github-actions[bot]",
          "author_login": "github-actions[bot]",
          "committed_at": "2026-04-12T12:10:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f697969678f86da1ef04952d78625bca659eb043",
          "body": "chore: add changeset for type fixes (patch)",
          "is_bot": false,
          "headline": "Merge pull request #22 from OpenRouterTeam/changelog/fix-types-patch",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-12T12:10:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8535b77d462fc82c8c52019948cb9de7852688f0",
          "body": "Ensures code quality checks run before pushing to remote.",
          "is_bot": false,
          "headline": "feat: add husky pre-push hooks for lint and typecheck",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-12T12:08:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5d947e6ad5ba291d43322168b11782c19b71b8e2",
          "body": "- Use `import type` for Item import in async-params.ts\n- Organize imports per biome rules\n- Remove extra blank line in item-types.ts",
          "is_bot": false,
          "headline": "fix: resolve biome lint errors",
          "author_name": "Matt Apperson",
          "author_login": "mattapperson",
          "committed_at": "2026-04-12T12:07:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 7,
      "commits_last_year": 152,
      "latest_release_at": "2026-07-22T20:50:38Z",
      "latest_release_tag": "@openrouter/agent@0.8.0",
      "releases_from_tags": false,
      "days_since_last_push": 2,
      "active_weeks_last_year": 16,
      "days_since_latest_release": 11,
      "mean_days_between_releases": 13
    },
    "artifacts": {
      "collected": true,
      "structure": [],
      "declarations": [
        {
          "name": null,
          "path": "package.json",
          "tokens": [
            "npm.private"
          ],
          "ecosystem": "npm"
        },
        {
          "name": "@openrouter/agent",
          "path": "packages/agent/package.json",
          "tokens": [
            "npm.entry"
          ],
          "ecosystem": "npm"
        },
        {
          "name": "@openrouter/mcp",
          "path": "packages/mcp/package.json",
          "tokens": [
            "npm.entry"
          ],
          "ecosystem": "npm"
        }
      ]
    },
    "community": {
      "has_readme": true,
      "has_license": false,
      "readme_badges": {
        "hosts": [],
        "total": 0,
        "header": 0,
        "collected": true,
        "has_inspect_badge": false
      },
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 25,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "@openrouter/mcp",
          "exists": true,
          "license": "Apache-2.0",
          "keywords": [
            "openrouter",
            "mcp",
            "model-context-protocol",
            "agent",
            "typescript",
            "ai",
            "tools",
            "llm"
          ],
          "ecosystem": "npm",
          "categories": [],
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@openrouter/mcp",
          "declared_type": null,
          "is_deprecated": false,
          "latest_version": "0.0.1",
          "repository_url": "https://github.com/OpenRouterTeam/typescript-agent",
          "versions_count": 1,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 8,
          "monthly_downloads": 133,
          "first_published_at": "2026-07-24T20:05:28.313000Z",
          "latest_published_at": "2026-07-24T20:05:28.695000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 9
        },
        {
          "name": "@openrouter/agent",
          "exists": true,
          "license": "Apache-2.0",
          "keywords": [
            "openrouter",
            "agent",
            "typescript",
            "ai",
            "tools",
            "streaming",
            "llm"
          ],
          "ecosystem": "npm",
          "categories": [],
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@openrouter/agent",
          "declared_type": null,
          "is_deprecated": false,
          "latest_version": "0.8.0",
          "repository_url": "https://github.com/OpenRouterTeam/typescript-agent",
          "versions_count": 13,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 8,
          "monthly_downloads": 382454,
          "first_published_at": "2026-04-01T16:43:16.521000Z",
          "latest_published_at": "2026-07-22T20:50:29.987000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 11
        }
      ]
    },
    "popularity": {
      "forks": 11,
      "stars": 19,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2026-04-10",
            "count": 1
          },
          {
            "date": "2026-05-01",
            "count": 1
          },
          {
            "date": "2026-05-02",
            "count": 1
          },
          {
            "date": "2026-05-10",
            "count": 1
          },
          {
            "date": "2026-05-25",
            "count": 1
          },
          {
            "date": "2026-05-30",
            "count": 1
          },
          {
            "date": "2026-06-15",
            "count": 1
          },
          {
            "date": "2026-06-17",
            "count": 1
          },
          {
            "date": "2026-06-26",
            "count": 1
          },
          {
            "date": "2026-07-16",
            "count": 1
          },
          {
            "date": "2026-07-29",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 11,
        "total_forks": 11
      },
      "star_history": null,
      "open_issues_and_prs": 17
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": true,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "packages/agent/tsconfig.json",
        "packages/mcp/tsconfig.json"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 181323,
      "source_files_sampled": 120,
      "oversized_source_files": 2,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "package.json"
      ],
      "advisories": {
        "error": null,
        "scope": "published_package",
        "source": "osv",
        "findings": [],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 95,
        "malicious_count": 0,
        "assessed_package": "npm:@openrouter/mcp@0.0.1",
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm"
      ],
      "dependencies": [
        {
          "name": "@openrouter/sdk",
          "manifest": "packages/agent/package.json",
          "ecosystem": "npm",
          "version_constraint": "^0.13.7"
        },
        {
          "name": "zod",
          "manifest": "packages/agent/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.0.0"
        },
        {
          "name": "@modelcontextprotocol/sdk",
          "manifest": "packages/mcp/package.json",
          "ecosystem": "npm",
          "version_constraint": "^1.29.0"
        },
        {
          "name": "@openrouter/agent",
          "manifest": "packages/mcp/package.json",
          "ecosystem": "npm",
          "version_constraint": "workspace:*"
        },
        {
          "name": "zod",
          "manifest": "packages/mcp/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.0.0"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 11,
        "merged_prs": 58,
        "open_issues": 6,
        "closed_ratio": 0.143,
        "closed_issues": 1,
        "closed_unmerged_prs": 15
      },
      "bus_factor": 1,
      "bot_contributors": 2,
      "top_contributors": [
        {
          "type": "User",
          "login": "mattapperson",
          "commits": 101,
          "avatar_url": "https://avatars.githubusercontent.com/u/192433?v=4"
        },
        {
          "type": "User",
          "login": "LukasParke",
          "commits": 12,
          "avatar_url": "https://avatars.githubusercontent.com/u/5702154?v=4"
        },
        {
          "type": "User",
          "login": "Cybourgeoisie",
          "commits": 8,
          "avatar_url": "https://avatars.githubusercontent.com/u/301326?v=4"
        },
        {
          "type": "User",
          "login": "claude",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/81847?v=4"
        },
        {
          "type": "User",
          "login": "robert-j-y",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/212159665?v=4"
        },
        {
          "type": "User",
          "login": "w0nche0l",
          "commits": 3,
          "avatar_url": "https://avatars.githubusercontent.com/u/3719829?v=4"
        },
        {
          "type": "User",
          "login": "okacodes",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/193345390?v=4"
        },
        {
          "type": "User",
          "login": "jakobdylanc",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/38699060?v=4"
        },
        {
          "type": "User",
          "login": "ayush-or",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/291842981?v=4"
        }
      ],
      "contributors_sampled": 9,
      "top_contributor_share": 0.743
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "bump-openrouter-sdk.yaml",
        "ci.yaml",
        "publish.yaml"
      ],
      "has_docs_dir": false,
      "linter_configs": [
        "biome.json"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "pnpm-lock.yaml"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 3,
            "reason": "branch protection is not maximal on development and all release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 9,
            "reason": "18 out of 19 merged PRs checked by a CI test -- score normalized to 9",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 4,
            "reason": "Found 8/18 approved changesets -- score normalized to 4",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 10,
            "reason": "project has 5 contributing companies or organizations",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 0,
            "reason": "license file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 0,
            "reason": "16 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "d03060256c4c7d55432e508544d8d35d2f65d4bb",
        "ran_at": "2026-08-03T04:51:18Z",
        "aggregate_score": 3.8,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "recent_prs": {
        "merged_7d": 3,
        "decided_7d": 3,
        "merged_30d": 17,
        "authors_30d": 6,
        "decided_30d": 29,
        "sample_size": 60,
        "window_days": 30,
        "sample_exhausted": false,
        "authors_probed_30d": 6,
        "newcomer_merged_30d": 15,
        "bot_prs_excluded_30d": 1,
        "newcomer_authors_30d": 3,
        "newcomer_decided_30d": 26
      },
      "ci_last_run_at": "2026-07-30T15:59:39Z",
      "oldest_open_prs": [
        {
          "number": 31,
          "created_at": "2026-04-20T18:44:41Z",
          "last_comment_at": "2026-07-29T19:17:56Z",
          "last_comment_author": "cortex-github-agent"
        },
        {
          "number": 32,
          "created_at": "2026-04-21T16:45:08Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 55,
          "created_at": "2026-06-26T02:55:58Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 74,
          "created_at": "2026-07-22T01:49:06Z",
          "last_comment_at": "2026-07-29T19:40:38Z",
          "last_comment_author": "devin-ai-integration"
        },
        {
          "number": 85,
          "created_at": "2026-07-28T19:31:30Z",
          "last_comment_at": "2026-07-29T19:40:37Z",
          "last_comment_author": "devin-ai-integration"
        },
        {
          "number": 86,
          "created_at": "2026-07-28T19:53:53Z",
          "last_comment_at": "2026-07-30T18:48:38Z",
          "last_comment_author": "LukasParke"
        },
        {
          "number": 88,
          "created_at": "2026-07-29T17:44:01Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 89,
          "created_at": "2026-07-29T19:59:32Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 90,
          "created_at": "2026-07-31T02:45:53Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 91,
          "created_at": "2026-07-31T05:03:44Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 92,
          "created_at": "2026-07-31T21:05:35Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-07-30T15:58:38Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 10,
          "created_at": "2026-04-03T21:18:01Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 11,
          "created_at": "2026-04-03T21:21:09Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 41,
          "created_at": "2026-05-10T20:13:12Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 45,
          "created_at": "2026-05-27T17:40:50Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 54,
          "created_at": "2026-06-26T02:55:21Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 58,
          "created_at": "2026-07-08T04:30:26Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/OpenRouterTeam/typescript-agent",
    "host": "github.com",
    "name": "typescript-agent",
    "owner": "OpenRouterTeam"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": "The weighted overall 64 is calibrated to 73 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 64,
            "calibrated": 73,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 73,
      "inputs": {
        "security": 50,
        "vitality": 85,
        "community": 44,
        "governance": 60,
        "calibration": "2026-08-02",
        "engineering": 75,
        "ai_readiness": 55,
        "weighted_overall_raw": 64
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 85,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "commits_last_year": 152,
              "human_commit_share": 0.94,
              "days_since_last_push": 2,
              "active_weeks_last_year": 16
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 2 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 2
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "16/52 weeks with commits",
                "points": 11.1,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 16
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "152 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 152
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "exceptional",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 7,
              "latest_release_tag": "@openrouter/agent@0.8.0",
              "releases_from_tags": false,
              "days_since_latest_release": 11,
              "mean_days_between_releases": 13
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "7 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 11 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~13 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 13
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "weak",
        "name": "Community & Adoption",
        "value": 44,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "at_risk",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 29,
            "inputs": {
              "forks": 11,
              "stars": 19,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "19 stars",
                "points": 20.4,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 19
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "11 forks",
                "points": 8.3,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 11
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "at_risk",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 25,
            "inputs": {
              "has_readme": true,
              "has_license": false,
              "readme_badges": 0,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "readme_badge_services": [],
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "no license file detected",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "license_absent",
                    "params": {}
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "exceptional",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 93,
            "inputs": {
              "packages": [
                "@openrouter/mcp",
                "@openrouter/agent"
              ],
              "dependents": null,
              "ecosystems": "npm",
              "total_downloads": null,
              "monthly_downloads": 382587
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "382,587 downloads/month across npm",
                "points": 74.4,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 382587,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 60,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "weak",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 37,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 9,
              "top_contributor_share": 0.743
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 74% of commits",
                "points": 5.8,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 74
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "9 contributors",
                "points": 12.2,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 9
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 5 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "weak",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 43,
            "inputs": {
              "merged_prs": 58,
              "open_issues": 6,
              "closed_issues": 1,
              "prs_merged_7d": 3,
              "prs_decided_7d": 3,
              "prs_merged_30d": 17,
              "prs_decided_30d": 29,
              "issue_closed_ratio": 0.143,
              "closed_unmerged_prs": 15,
              "first_time_authors_30d": 3,
              "first_time_prs_merged_30d": 15,
              "first_time_prs_decided_30d": 26
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "14% of issues closed",
                "points": 6,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 14
                    }
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "58/73 decided PRs merged",
                "points": 23.8,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 58,
                      "decided": 73
                    }
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "15/26 first-time contributors' PRs merged in 30d",
                "points": 7.5,
                "status": "partial",
                "details": [
                  {
                    "code": "newcomer_prs_merged",
                    "params": {
                      "days": 30,
                      "merged": 15,
                      "decided": 26
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 8/18 approved changesets -- score normalized to 4",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "good",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "followers": 3536,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "OpenRouterTeam",
              "public_repos": 36,
              "account_age_days": 1117
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "3,536 followers of OpenRouterTeam",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 3536,
                      "login": "OpenRouterTeam"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "36 public repos, account ~3 yr old",
                "points": 17.5,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 36
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 3
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "exceptional",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "@openrouter/mcp",
                "@openrouter/agent"
              ],
              "ecosystems": "npm",
              "any_deprecated": false,
              "min_days_since_publish": 9
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "2 package(s) on npm",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 2,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 9 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 9
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "13 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 13
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 75,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "biome.json",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "biome.json"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "18 out of 19 merged PRs checked by a CI test -- score normalized to 9",
                "points": 18,
                "status": "partial",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 65,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": "https://openrouter.ai/docs/sdks/typescript/call-model",
              "has_readme": true,
              "has_docs_dir": false,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://openrouter.ai/docs/sdks/typescript/call-model",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 50,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "weak",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 38,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.8
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection is not maximal on development and all release branches",
                "points": 2.2,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "18 out of 19 merged PRs checked by a CI test -- score normalized to 9",
                "points": 2.2,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 8/18 approved changesets -- score normalized to 4",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 5 contributing companies or organizations",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "16 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "exceptional",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the npm:@openrouter/mcp@0.0.1 runtime dependency closure — what installing the published package pulls in — 95 packages. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_published",
                "params": {
                  "package": "npm:@openrouter/mcp@0.0.1",
                  "assessed": 95
                }
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 0,
              "affected_packages": 0,
              "assessed_packages": 95,
              "unassessed_packages": 0,
              "affected_by_severity": "none",
              "direct_affected_packages": 0
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "no direct dependency carries a known advisory",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "no_direct_advisories",
                    "params": {}
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "no indirect dependency carries a known advisory",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "no_indirect_advisories",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory carries a publication date",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_no_publication_date",
                    "params": {}
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "exceptional",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 95,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "exceptional",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "commit_weight_rule": {
                "min_commits": 50,
                "min_commit_share": 0.1
              },
              "review_only_matches": 0,
              "below_threshold_exposures": [],
              "assessed_self_published_locations": 5
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 55,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "weak",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.947,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "89 of 94 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 89,
                      "sampled": 94
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "pnpm-lock.yaml"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "packages/agent/tsconfig.json",
                "packages/mcp/tsconfig.json"
              ],
              "agent_commit_share": 0.12,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": "biome.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "biome.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "packages/agent/tsconfig.json, packages/mcp/tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "packages/agent/tsconfig.json, packages/mcp/tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "12 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 12,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "exceptional",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 99,
            "inputs": {
              "primary_language": "TypeScript",
              "largest_source_bytes": 181323,
              "source_files_sampled": 120,
              "oversized_source_files": 2
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "TypeScript (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "TypeScript"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "2/120 source files over 60KB",
                "points": 54.1,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 120,
                      "oversized": 2
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 20,
            "inputs": {
              "example_dirs": [],
              "has_mcp_signal": true,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "labels": [
        "mcp-server",
        "library"
      ],
      "scores": {
        "sdk": 2,
        "library": 4.8,
        "mcp-server": 7
      },
      "primary": "mcp-server",
      "evidence": [
        {
          "tier": "declared",
          "label": "library",
          "source": "npm.private",
          "weight": -6
        },
        {
          "tier": "distribution",
          "label": "library",
          "source": "registry:npm",
          "weight": 6
        },
        {
          "tier": "declared",
          "label": "library",
          "source": "npm.entry",
          "weight": 4.8
        },
        {
          "tier": "dependencies",
          "label": "mcp-server",
          "source": "dep:@modelcontextprotocol/sdk",
          "weight": 4
        },
        {
          "tier": "structure",
          "label": "mcp-server",
          "source": "mcp_signal",
          "weight": 3
        },
        {
          "tier": "description",
          "label": "sdk",
          "source": "description:sdk",
          "weight": 2
        }
      ],
      "artifacts": [
        {
          "path": "package.json",
          "labels": [],
          "ecosystem": "npm"
        },
        {
          "path": "packages/agent/package.json",
          "labels": [
            "library"
          ],
          "ecosystem": "npm"
        },
        {
          "path": "packages/mcp/package.json",
          "labels": [
            "library"
          ],
          "ecosystem": "npm"
        }
      ],
      "confidence": "medium",
      "host_extension": false,
      "runs_as_process": true,
      "consumed_by_code": true
    },
    "metrics_version": "2.3.1"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-08-03T04:51:38.340415Z",
  "schema_version": "0.30.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/o/OpenRouterTeam/typescript-agent.svg",
  "full_name": "OpenRouterTeam/typescript-agent",
  "license_state": "absent",
  "license_spdx": null
}

Scores are signals, not warranties. They reflect publicly visible practices on GitHub — not a code audit, and not a security guarantee.

Missing data is excluded and weights renormalized, never scored as zero. Methodology is versioned and open: metrics v2.3.1, schema v0.30.0 — full methodology · metrics wiki.

How one result sits in the wider record: aggregate statisticsnpm.