Commit Graph

33 Commits

Author SHA1 Message Date
Mark Tkachenko
872e93f11f feat: add the MCP server and tool to AIR tool calls (#1359)
AIR gets `_meta.jetbrains.air.mcp = {server, tool}` on MCP tool calls,
so the MCP card does not depend on the title format.

The adapter splits the `mcp__<server>__<tool>` tool name the way Claude
Code does. `server` is the normalized name. Clients other than AIR get
nothing new.

Split from IJAI-872. The SDK does not expose MCP progress yet, so this
PR does not forward it.
2026-10-15 17:04:17 +02:00
nikita-ashihmin
78a29f58ba feat: publish the predicted follow-up prompt as _meta.promptSuggestions (#1356)
## What

The Claude Code CLI can predict the prompt the user is likely to send
next. This adapter asked for nothing and dropped the `prompt_suggestion`
message. It now asks for the prediction (`promptSuggestions: true`) and
forwards the one prediction of a turn.

ACP has no field for a composer prefill, so the value travels as

```json
{
  "sessionUpdate": "session_info_update",
  "_meta": { "promptSuggestions": { "suggestions": [{ "text": "Run the tests" }] } }
}
```

The key names no vendor, so another client can read it. AIR already
renders the first non-blank text as gray text in an empty composer,
where Tab fills the composer and sends nothing.

## Behavior

- `suggestions: []` clears the prediction the client holds: at the next
`session/prompt`, because that turn's prediction is spent, and on a
`conversation_reset`, because the transcript it was drawn from is gone.
- A prediction the next prompt outran — the user sent the next prompt
before the adapter read the prediction off the SDK stream — is dropped
instead of published mid-turn.
- A blank prediction never goes on the wire.
- The update is sent only to an AIR client, like the other extension
keys, so non-AIR traffic is unchanged (the key is added to
`AIR_ONLY_META_KEYS`; the `origin/main` scenario baselines stay
byte-identical).
- The CLI predicts nothing on the first turn, after an API error, in
plan mode, and at the usage limit, so a turn without a prediction is
normal. A caller can opt out with
`_meta.claudeCode.options.promptSuggestions: false`.

Documented in `docs/air-extensions.md`.

## Tests

`src/tests/prompt-suggestions.test.ts` covers the wire shape, the
publish after the turn's result, the clear at the next prompt, the
dropped stale prediction, the blank prediction, and a non-AIR client.
`src/tests/create-session-options.test.ts` covers the SDK option and the
caller opt-out.

`npm run check`, `npm run build` and the touched suites are green; the
full `test:run` has pre-existing flakiness on this machine
(session-index watchers, providers) unrelated to this change.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
2026-10-15 17:55:44 +04:00
nikita-ashihmin
1a1f1a0e80 feat: mark session list rows of worktrees Claude Code made (agentWorktree) (#1352)
## What

Marks `session/list` and `_session/list/changes` rows whose session runs
in a linked Git worktree that Claude Code itself created (`claude
--worktree`, the `EnterWorktree` tool), as
`_meta.jetbrains.air.agentWorktree: true`. A new agent capability
`sessionWorktrees`, advertised together with `sessionIndex`, tells the
client that rows carry the field, so a missing field means "not made by
the agent" rather than "unknown".

## Why

With `includeWorktrees`, a client gets sessions from every linked
worktree with nothing to tell them apart. A worktree Claude Code made
for a session belongs to the project the user started it from; a
worktree the user made by hand is a separate checkout. Only the agent
knows which is which.

## How

- The rule reads the transcript's `worktree-state` records: the latest
one wins; the session counts only when `worktreeSession` is non-null and
`enteredExisting` is not `true`. `environment.isWorktree` is not used,
since plain git detection is also true for user-made worktrees.
- The record is parsed in the existing head/tail transcript scan, so no
extra file reads. The row is marked only when its `cwd` is in a linked
worktree (`<common-dir>/worktrees/<name>`), never for the main checkout
or a submodule.
- The row's `cwd` stays the session's own directory. The field is `true`
or absent.

Proposed as `SessionInfo.agentWorktree` plus a `agentWorktrees`
capability marker in the session list extensions RFD:
agentclientprotocol/agent-client-protocol#2323. Docs:
`docs/air-extensions.md`.

## Tests

`src/tests/session-index.test.ts`: marked worktree, `enteredExisting`,
no record (user worktree), a later `null` record, main checkout,
`_session/list/changes` rows, capability negotiation, and scan windows.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:15:52 +04:00
Ben Brandt
fadbe1881c fix: let session/delete succeed for a session without a transcript (#1351)
Found by running the ACP TCK's v2 suite against the adapter
(ACP-DELETE-201 and ACP-DELETE-203). The code is shared, so v1 clients
get the fix too.

## The bug

ACP says: "Deleting an already-deleted session, or a session that never
existed, **SHOULD** succeed silently." For a client that is neither AIR
nor a `sessionIndex` client, `session/delete` instead failed with
`-32603` "Internal error" in three cases:

- **A session this connection ran but never prompted.** Claude Code
writes the transcript at the first prompt. So the adapter closed the
live session, then the SDK delete threw "not found in any project
directory".
- **An unknown session.** The SDK delete throws the same "not found"
error.
- **An id that isn't a UUID.** The SDK delete throws "Invalid
sessionId".

## The fix

A new `deleteSessionIfStored` in `src/session-index/service.ts` handles
every other client's delete:
- An id that isn't a session id can't name a transcript, so it returns
`{}` without calling the SDK.
- The SDK's own "not found" means nothing is on disk, so that also
returns `{}`. It's recognised by the existing `isSdkNotFound`.
- Any other SDK failure, such as a permission error, still fails the
request.

The two AIR paths are unchanged, because `docs/air-extensions.md`
documents their errors: `-32002` for an unknown session on a
`sessionIndex` client, and the SDK's error for AIR without
`sessionIndex`. The doc now describes the plain client's behavior.

## Checks

- **ACP TCK v2** (`agentclientprotocol/acp-tck` at `fa00ccd`) against
this build: ACP-DELETE-201 and ACP-DELETE-203 now pass, where both
failed before. ACP-DELETE-202 is still skipped, because `session/list`
doesn't show a session that was never prompted.
- **Tests:** three new tests: a never-prompted live session, an unknown
session, and an id that isn't a UUID. Removing either rule, or
swallowing every error, fails a test. The existing delete tests now use
UUID ids, which is what real sessions have.
- **Suite:** `npm run check`, build and all 3068 tests pass, with no
golden changes.
2026-10-14 23:14:54 +02:00
Sergey Shkredov
e8eb9d340f feat: narrow models to what the providers/set gateway lists (#1340)
### Summary

Addresses [IJAI-1783](https://youtrack.jetbrains.com/issue/IJAI-1783).

An organization can restrict the models its JetBrains gateway serves,
but a session routed through `providers/set` offered the whole Claude
Code catalogue, so the picker showed models the gateway then refused.
`providers/set` now lists the gateway's models before it responds, and
the model picker offers only the SDK models the gateway lists.

- `anthropic`: `GET {baseUrl}/v1/models?limit=1000`, paginated by
`has_more`/`last_id`, with the provider headers and `anthropic-version`.
- `bedrock`: `GET {baseUrl}/foundation-models` (`ListFoundationModels`,
`modelSummaries[].modelId`), the listing the JetBrains gateway answers
on its Bedrock route. Only `anthropic.` ids count. The cross-region
prefix `global` when listed, else that of the first listed id, becomes
the session's `ANTHROPIC_BEDROCK_REGION_PREFIX`, so the CLI calls the
models under the prefix the gateway serves; ids of other prefixes, and
SDK rows resolved to another prefix, are dropped.
- Rows match by `resolvedModel` (or `value`) without dates, `-vN:M`
suffixes, `[1m]` hints and provider prefixes; the narrowing runs after
the `availableModels` allowlist. When the gateway does not list the
model a session runs (the dropped default, or a resumed or recreated
session's last model), the session switches to the first listed one with
`setModel`, so the CLI does not keep running an unlisted model.
- The listing runs in the provider-update queue, so `providers/set` and
`providers/disable` apply in the order they arrive; logout also clears a
`providers/set` that is still listing.
- A 4xx answer means the gateway has no listing: `providers/set`
succeeds and nothing is narrowed. Any other failed listing (network
error, 5xx, unexpected body, 10 s timeout) fails `providers/set` with
`internal_error`, and the previous provider stays in effect. An invalid
header or a base URL with credentials fails without repeating the value.
A listing that matches no SDK model leaves the session with no model to
offer: there is no model config option.
- `vertex`, gateways set through `authenticate`, and native routing are
not listed. Setting the same gateway again reuses its listing;
`providers/disable` and logout drop it.
- `docs/model-configuration.md` describes the narrowing.

### Not covered

Background haiku requests, subagents, and a `/model` typed into the
prompt choose their own model and are not checked against the listing.

### Verification

```bash
npm run check
npm run build
npm run test:run -- --no-file-parallelism   # 2964 passed, 5 failed, 30 skipped; all 5 in session-index.test.ts, flaky on main too
```

Tests stub `fetch` (`src/tests/setup-no-network.ts` answers an empty
listing by default) and cover the listing requests and pagination, the
Bedrock region prefix and its env, region-aware matching, the dropped
default, the failing `providers/set`, and the empty picker. The
JetBrains Bedrock gateway's `/foundation-models` answers
`global.anthropic.claude-{haiku,opus,sonnet}-5-5` (seen through Junie,
which lists the same route); this adapter has not yet been run end to
end against it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:03:27 +02:00
Ben Brandt
764f339c3e fix: end the terminal of a command whose tool call is closed without a result (#1335)
## Problem

A tool call that ends without a tool result is closed by a report that
only sets its status (`AcpToolCallRenderer.unfinished`). That happens
after `session/cancel`, for a subagent's command (Claude Code sends no
result for it) or a call that Claude abandoned, and when a turn ends
with a call that has no result (`closeUnfinished` in `settleActive`).

A client that renders terminals (the `terminal_output` /
`terminal_output_delta` extension, and the display terminals of ACP v2)
holds a terminal for a Bash command, which its `tool_call` started with
`_meta.terminal_info`. The closing report did not end it, so the call
shows as ended next to a terminal that never exits.

Example, live (Claude Code 2.1.296, Sonnet): a foreground Agent call
whose subagent runs `python3 -c "import time; time.sleep(45)"`, and
`session/cancel` while it runs. Claude Code answers the Agent call and
sends nothing for the subagent's command, so the adapter closes that
call itself:

- v1 client with `_meta.terminal_output`: `tool_call_update` `failed`,
with no `terminal_exit`.
- v2 client: `terminal_update` with the command, then `tool_call_update`
`cancelled`, and no `terminal_update` that carries an exit.

## Change

- `closeToolCall` is the one function that every closing report goes
through (the cancelled-turn closing and the end-of-turn closing today,
and any closing added later). It now also ends the call's terminal, in
the same update, as the result of a command does. It reads the tool from
the tool use cache before it drops the entry.
- The closings that #1336 and #1339 add go through `closeToolCall` too,
so they end the terminal as well once merged.
- `AcpToolCallRenderer.terminalExit` decides whether the call has a
terminal. It uses the rule that `toolCall` uses to start one, a command
on a client that renders terminals, which is now one private method
(`hasTerminal`) that `toolCall`, `toolUseContent` and `terminalExit`
share.
- The exit code is unknown, since the command ended without a result:
`exit_code: null`, as `endBackgroundTerminal` sends for a background
command. AIR always gets a number and reads 1 for a command that did not
succeed (#1252), so it gets 1. A v2 client gets `terminal_update` with
an empty `exitStatus`.
- `docs/air-extensions.md`: one line in the terminal conventions.

## Evidence

**Tests** (8 new):

- `incomplete-tools.test.ts`: the cancel's closing of a subagent's
command ends its terminal, still names the subagent, and sends
`exit_code: null`; AIR gets `exit_code: 1`; a client without terminals
gets no exit; a subagent's `Read` gets none; a command that ran but got
no result by the end of the turn gets the exit; so does one that Claude
abandoned before it ran.
- `tools.test.ts`: `terminalExit` for Bash, PowerShell, v2, AIR, a
client without terminals, and tools that are not commands.
- `acp-v2.test.ts`: the stopped subagent's terminal exits in the child's
session, after the command's terminal started and before the root's
`idle cancelled`, and the call ends `cancelled`.

**Removal checks.** Each of these fails at least one test: no exit in
`closeToolCall` (5), AIR gets null (2), an exit for a client without
terminals (2), an exit for a tool that is not a command (3), another
terminal id (5), the exit replacing the parent meta (1), the tool read
after the cache entry is dropped (5).

**Live**, each update validated against the SDK's JSON schema (v1
`SessionNotification`, v2 `session/update`), 0 violations. The Agent
call and the subagent's command from the example, cancelled 4 s after
the command's permission request was answered:

| client | `main` | this PR |
| --- | --- | --- |
| v1, `_meta.terminal_output` | closing report `failed`, no
`terminal_exit` | the same update carries `terminal_exit {exit_code:
null, signal: null}` |
| v1, AIR with `terminal_output` | not run on `main` (same code path) |
`terminal_exit` with `exit_code: 1` |
| v2 | `cancelled`, no terminal update | `terminal_update` with
`exitStatus: {}`, then `cancelled`, before the root's `idle cancelled` |

`npm run check`, `npm run build` and `npm run test:run` (2932 tests)
pass. No golden file changes. With #1336 and #1339 merged into it, in
any of the six orders, the branch merges without conflicts and all 2948
tests pass.

## Not covered

- A call that only a permission request announced, whose tool use never
streamed before it was closed, has no entry in the tool use cache. Its
tool is unknown here, so its terminal stays open. This is the lookup
that `AsyncTaskRuntime` uses too (`toolUseCache`, then
`resolvedToolNames`). Covering it needs the tool name where the
permission request emits the call.
- The fallback for a tool result without a tool use
(`toAcpNotifications`) still reports the status only, with no exit.
- The end-of-turn closing is covered by a test only: Claude Code does
not end a turn with an unanswered call on demand.
2026-10-14 11:44:15 +02:00
Ben Brandt
5758ac84a4 feat: report subagents as the subagents RFD defines, for clients that are not AIR (#1320)
The first of three stacked PRs that replace #1257, split for review.
Together they bring v1 clients the [subagents
RFD](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/subagents.mdx),
as revised by agentclientprotocol/agent-client-protocol#2281 and #2308.
**AIR is unchanged**: its golden files don't change.

1. **This PR:** the RFD form for a v1 client that declares `subagents`
and isn't AIR.
2. #1321: a child keeps its session across SendMessage resumes.
3. #1322: a child reports `requires_action` while its permission or
question request is open.

Merge them in that order. Each later PR is based on the previous branch,
so its diff shows only its own part.

## Who gets what

- **AIR** (any client that declares `_meta.jetbrains.air`): the earlier
draft it implements, exactly as before. That's `subagent_spawned`,
`subagent_state_update`, a session per generation, the hidden Agent/Task
tool call, and draft replay. AIR keeps it whichever signal it declares.
- **A v1 client with `clientCapabilities.subagents` that isn't AIR:**
the RFD form below. SDK 1.8.0 types it (`subagent_update`,
`session_message`, the v1 `StateUpdate`).
- **Without either signal:** nothing changes.

`subagentForm` (`src/acp-subagents.ts`) picks the form.
`NativeSubagentRuntime` keeps the routing, buffering and nested lineage,
and renders either form.

## The RFD form

| RFD | Before | Now |
| --- | --- | --- |
| Announce the child before any of its traffic | `subagent_spawned {
name, task, prompt }` | `subagent_update { sessionId, title,
description, state: { state: "running" } }` |
| `description` is the role or purpose, "not a transcript of
instructions" | `task` was the prompt | `title`: the agent's `name`,
with its role (`subagent_type`) after it, else the role. `description`:
the Agent input's short `description` |
| The parent's message to the child is an incoming `session_message` |
`prompt` on the announcement | a `session_message` in the child's
transcript, with the Agent/Task tool use's id and the session that made
the call as sender |
| Delegation tools stay tool calls | the Agent/Task tool call was hidden
| it stays a tool call of the session that made it, with the report its
model got. Its heartbeats and result stay there too |
| Work-state snapshots, not a terminal lifecycle | `completed`,
`failed`, `cancelled`, `disconnected` | `running`; `idle` with
`end_turn`, `cancelled`, or `error` with the SDK's error text as its
JSON-RPC `error`; `idle` with no stop reason for work that ended without
a known reason (a worker restart, or still running when the stream ends)
|
| `idle` isn't terminal | later updates were dropped | an idle child
still gets its updates, such as its background work's |
| A cancel stops the work it waits on; other descendants are up to the
Agent (#2308) | `session/cancel` ended every child as cancelled | the
adapter ends no child itself. Claude Code's interrupt stops the
foreground children, and currently background ones too; each reports
`idle`/`cancelled` from the SDK's events, and the foreground ones'
requests are withdrawn |
| Never guess a parent | a child that ended before its spawning frame
was announced under the root | it stays unexposed. A nested child waits
for its parent's announcement |
| Never move a tool call | | a tool call that a permission request
emitted on the root before its child was announced stays on the root
after a cancel. AIR keeps the cancel's cleanup, since its draft ends
every child there |
| No agent-side capability | `sessionCapabilities.subagents` for every
client | for AIR only |

## Not in this PR

- **SendMessage resumes** (#1321). Until then a resumed child stays
`idle`. Requests find a child by its agent id, so only the permission
and question requests of the resumed run appear in its session. A resume
never opens a second session for the child, which the RFD forbids, and a
test pins that.
- **`requires_action`** (#1322).
- **Replay** doesn't expose children yet. Today's replay invents
`<session>:replay-subagent:<id>` ids, and the RFD requires the original
ones. The RFD makes child replay a SHOULD, so an RFD client's replay
leaves children out until the replay PR.

## Settled during the review of #1257

- **Cancel cascade:** the RFD leaves background children to the Agent
(#2308). An agent-level test pins #2308's validation scenario.
- **Session ids across forks:** the raw SDK agent id stays the session
id. The SDK's fork copies no subagent history, so a fork can't repeat an
id under another root. The reasoning is on `nextChildSessionId`.
- **Held turns:** the adapter holds a v1 prompt open while a background
subagent the turn started runs. A cancel still settles the prompt.

## Live run

Against real Claude Code (Sonnet), with a v1 client that declares
`subagents` and checks every update and permission request against the
SDK's v1 schema, there were no schema violations:

- **Launch:** `running` with the title (`scout (general-purpose)`) and
description, the prompt as a `session_message` from the root, and the
child's tool calls in its session. Then `idle`/`end_turn` before the
Agent call completes on the root.
- **A 40-second child:** the Agent call's heartbeat and result stay on
the root. Before the fix, they moved to the child, because the heartbeat
named the Agent call as its own parent.
- **Cancel:** a foreground child is `idle`/`cancelled` 15 ms after
`session/cancel`, and the prompt returns `cancelled`. Claude Code
stopped a background child too (its metadata says `stoppedByUser`).
- **Found on `main`, for every client, and fixed separately:** after a
cancel, a tool call that was running never gets a final status.

## Tests

- **AIR:** the end-to-end tests of the earlier draft run with an AIR
client that declares `nativeSubagentSessions` (`AIR_NATIVE_SUBAGENTS`).
The AIR golden files are unchanged.
- **RFD, end to end:**
  - announcement and prompt
- a failure with the SDK's error, also when the error arrives after the
failure
  - a stop, and a worker-restart orphan
  - no draft updates
  - the cancel per #2308
- the raced tool call after a cancel, kept on the root, with AIR's
cleanup kept
  - the Agent call's heartbeat

  Every update is checked against the SDK's v1 schema.
- **RFD, runtime:**
  - the visible control tool call, also nested
  - labels, also from a late frame
- an unparented child staying unexposed, and a nested child announced
only after its parent
  - nothing reported at the parent's cancel
  - the end of the stream, and `unknown`
  - a resume opening no second session
- **Plain/Zed comparison with origin/main:** it now allows the missing
agent capability, which `compare.ts` documents.
- **Mutation checks during #1257's reviews:** removing any of these
rules fails tests.
- **Checks:** `npm run check`, build and all 2863 tests pass.

## Release

Titled `feat:`, because it changes what a v1 client that declares
`subagents` receives.
2026-10-13 16:11:24 +02:00
Sergey Shkredov
d6c371704f fix: display active Central helpers as JetBrains Air Gateway (#1266)
### Summary

Addresses [IJAI-2956](https://youtrack.jetbrains.com/issue/IJAI-2956).

- Recognize Central only when the active key source is `apiKeyHelper`
and its effective helper command contains the whole word `central` and
the word sequence `proxy start`, case-insensitively, allowing multiple
whitespace characters.
- Read the effective command through the existing `SettingsManager`/SDK
settings resolution, including project/managed overrides, rather than
reimplementing configuration merging or executing helpers.
- Return `kind: gateway` and the exact label `JetBrains Air Gateway`,
without `detail` or invented account identity. URL, flags, routes, and
endpoint ownership are intentionally not checked: this is an agreed weak
display heuristic, not proof of provenance or entitlement.
- Render ordinary helpers as `API key helper` instead of raw
`apiKeyHelper`; do not expose unknown key-source identifiers. Preserve
external backend and client-owned configuration priority, CLI/SDK
merging, stale-probe protection, and duplicate suppression.
- Add mapper and ACP-agent regression coverage for positive/negative
commands, overrides, CLI/SDK results, and source changes; update
`docs/air-extensions.md`.

Examples matching the heuristic include `central proxy start` and
`/opt/bin/CeNtRaL PrOxY StArT --arbitrary-flag`. `decentralized proxy
start`, `central start`, and an inactive helper do not match.

### Sanitized auth status

```json
{
  "kind": "gateway",
  "label": "JetBrains Air Gateway"
}
```

No helper command, key, header, private route, or invented account
information is sent.

### Verification

Based on fresh upstream `main` at `0724b17`, rechecked immediately
before publication. Changes and rebuilt `dist` are available in the main
local clone; the user confirmed the local adapters work.

```bash
npm run build
npm run lint
npm run test:run
```

- Build and lint passed.
- Full test suite: **2484 passed, 30 existing skipped** across 44 passed
test files and 4 skipped files.
- No new skip flags or disabled tests were used.

### Scope

No AIR UI, provisioning, personal configuration migration, effort/mode
changes, or new ACP protocol. Plan files and personal configurations are
not committed.

---------

Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Andrey Bragin <andrey.bragin@jetbrains.com>
2026-10-13 14:29:48 +02:00
Ben Brandt
383bc7dcf0 fix: start a forked session so the client can prompt it at once (#1317)
## Problem

The v1 schema says a forked session is active once the agent responds,
and can be prompted without `session/load` or `session/resume`
(agentclientprotocol/agent-client-protocol#2359). `session/fork` only
copied the transcript, so prompting the new id failed with "Session not
found" until the client loaded or resumed it.

## Change

- **The fork starts the new session the way a resume does.** It starts
from the copied transcript, with the model and the permission mode that
transcript records, and with the request's `cwd`, MCP servers,
additional directories and `_meta`. The response carries the session's
`modes` and `configOptions`, and the available commands follow it, as
for `session/new`.
- **A client that loads or resumes after a fork keeps working.** A later
`session/load` replays the history into the running session, and a later
`session/resume` returns it. Neither starts a second Claude Code
process. A load or resume with other settings restarts the session, as
for any loaded session. The fingerprint that decides this ignores the
AIR fork point in `_meta`.
- **Cost:** each fork now starts a Claude Code process. That added about
0.65 s to the fork response in the live run.
- **v2:** the v2 fork used to resume the forked session itself. It now
uses the session that the v1 fork starts, so the v2 bridge drops its own
resume.
- **AIR docs:** `docs/air-extensions.md` now says, under the session
fork point, what a load or resume after a fork does.

## Evidence

**Live, CLI 2.1.293 on Haiku, over v1:**
1. A new session answered "Remember the codeword PELICAN" with `OK`.
2. `session/fork` returned the fork with mode `auto` and the `mode`,
`model` and `effort` config options.
3. Prompting the fork right away returned `PELICAN`, with no load or
resume in between.
4. A `session/load` of the fork then replayed its history and was ready
in 4 ms. The timing log shows one session start for the fork and none
for the load, and a further prompt still answered.

**Tests:**
- New: the fork can be prompted at once, and its response carries modes
and config options; a later resume and load reuse the session; and the
fork fails when its session can't start.
- The existing fork tests now also check that the session starts from
the fork, with the request's additional directories.
- The v2 fork tests pass unchanged.
- **Mutation checks:** removing the start, the response's `modes`, or
the additional directories each fails a test.
- `npm run check`, build and all 2813 tests pass, with no golden
changes.
2026-10-13 05:28:48 +00:00
Maksim Zheltoukhov
fe8b69edd4 feat: accept IDE session hooks without dropping model settings (#1265)
IDEs can pass their own Claude hooks through
`_meta.claudeCode.options.settings.hooks` on `session/new` and
`session/load`. Before this change, any session settings replaced
`CLAUDE_MODEL_CONFIG` entirely, so adding a single hook dropped the
deployment's `modelOverrides` / `availableModels`. The two are now
merged, and the session settings win per key.

The wrapper does not merge hooks itself: Claude Code already runs the
hooks of every settings tier, so user and project hooks keep running
next to the session ones. Hooks stay scoped to their session, and a
`session/load` with different settings recreates the query, which
replaces or removes them.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:00:07 +02:00
nikita-ashihmin
03d84f6663 feat: add the AIR sessionIndex extension for fast session lists (#1268)
## Summary

Adds the opt-in AIR `sessionIndex` extension. With it, an AIR client can
show the latest sessions of a project quickly, with their state, and can
rename, archive and unarchive them over ACP. The full contract is in
`docs/air-extensions.md` ("Session index").

The extension follows ACP RFDs. The transport differs: it uses
`_meta.jetbrains.air` request options and row fields, `_session/*`
methods, and AIR capabilities that are not advertised under ACP v2. The
`archived` list filter also differs (see below).

- **Session list extensions**
(agentclientprotocol/agent-client-protocol#2323):
- `limit`, ordered by last user activity (`lastPromptAt ?? updatedAt`),
and `includeWorktrees`.
  - Row fields `lastPromptAt`, `model` and `forkedFrom`.
- `state`: `running`, `idle`, `requires_action`, and `error`, which the
RFD lacks.
  - `lastTurnEndedAt`.
- `cost`: the SDK's `total_cost_usd` for a session that runs on this
connection only. No cost is read from transcripts.
  - Live updates come from an AIR list subscription (below).
- **Session archive** (agentclientprotocol/agent-client-protocol#2161):
- `archived` is on every row, `session_info_update` is sent on a change,
and an unknown session is `-32002`.
- The `archived` list filter has three values: `"unarchived"` (the
default), `"archived"` (archived sessions only) and `"all"` (both, in
one order). #2161 has a boolean here. Its `false` is `"unarchived"`, its
`true` is `"all"`, and `"archived"` has no counterpart.
- **Capabilities**: an AIR client that declares `sessionIndex` gets
these in `_meta.jetbrains.air.capabilities`. The client declares only
`sessionIndex`.
  - `sessionIndex`.
- `sessionArchive`: `_session/archive` and `_session/unarchive`. The RFD
capability is `archive` (#2161).
- `sessionRename`: `_session/rename`. The RFD has capability `setTitle`
and method `session/set_title`
(agentclientprotocol/agent-client-protocol#1987).
- `sessionListSubscribe`: `_session/list/subscribe`,
`_session/list/unsubscribe` and `_session/list/changes`. No RFD
counterpart.

### Clients without `sessionIndex`

Clients that don't declare `sessionIndex`, which means non-AIR clients
and released AIR builds, keep the current behaviour. They get the same
SDK `listSessions` path, page size, cursor and initialize capabilities,
with no watchers and no notifications. There are two deliberate
deviations, both strictly safer:

1. **`session/delete` from an AIR client without `sessionIndex`.**
Released AIR builds use delete as "Done". Instead of deleting the
transcript (data loss), the adapter archives the session in AIR's own
Claude format (below). Archived sessions are hidden from that client's
list, so "Done" looks the same and nothing is lost. An unknown session
keeps the SDK's error. Other clients get the SDK delete, as before.
2. **`session/close`** of a session that is not loaded here returns `{}`
instead of throwing.

### Sessions open in another process

`session/load`, `session/resume` and `session/prompt` work on a session
that another live Claude Code process has open, for every client, as the
Claude CLI does. Rename, archive, unarchive and delete also go ahead,
writing records or deleting transcripts as the CLI's `/rename` and
delete do. The other process keeps running.

### Archive format and rule

The archive state uses AIR's native Claude archive format
(`ClaudeStoreThreadBackend.appendTitleRecords`, ADR 0140), so AIR's
native path and the adapter read and write the same state.

- **Archive** appends `{"type":"custom-title","customTitle":"[archived]
<title>",…}` and `{"type":"agent-name","agentName":"[archived]
<title>",…}` to every transcript copy. **Unarchive** appends the same
pair without the prefix. Titles are cut to the CLI's 200 characters.
- **Archived is decided by the agent name first**, as in AIR's native
Claude provider. A session's name is its last top-level `agentName`,
else the custom title the SDK reports. The session is archived exactly
when that name starts with `[archived] `. The listed title comes from
the same source, shown without the prefix.
- Title records are metadata: `updatedAt` and the list order do not
move.

### Rename, archive and delete

- **Rename** writes the same title pair. An archived session keeps its
prefix. A session running here is renamed through its CLI's
`rename_session` control request, serialised with auto-titling. Other
sessions get the records plus the `custom-title.json` sidecar the CLI
writes. No generated title replaces a client title, but a title stored
later by someone else (a `/rename`) is still adopted.
- **Archiving stops the running session.** `_session/archive` of a
session loaded on this connection does four things:
  1. It interrupts the running turn.
2. It retitles the session through its CLI. The adapter also appends the
matching `agent-name` record to the CLI's transcript, because the CLI
writes that record only later.
  3. It closes the session as `session/close` does.
4. It archives the transcripts once more if the closed CLI left them
unarchived.

The client then gets `session_info_update` with `archived: true`.
Unarchive does not reopen the session. A session that is not loaded here
is archived by writing the title records.
- **Delete** for a `sessionIndex` client deletes every copy of the
session and its `<sessionId>/` directories. A session running here is
closed first.

### Implementation (`src/session-index/`)

- **Index**:
- Reads the project dirs with `readdir` + `stat`, and keeps a 2000-entry
metadata LRU keyed by (path, mtime, size).
- Cache misses are read in parallel through `getSessionInfo`, plus a
growing tail read (64 KB → 4 MB) for the last prompt, last message, turn
end and agent name.
- SDK semantics are kept: sidecar titles, `continued-in`, long-path
hashes, darwin case, and first-prompt extraction (ported, and
fuzz-compared with the SDK).
- The `cwd` scope follows the SDK `listSessions`. Worktrees expand to
the same relative subdirectory, and a session copied to several project
directories is listed from its newest copy.
- The keyset cursor is bound to `cwd`, `archived` and
`includeWorktrees`. Session ids match exactly.
- **State**:
  - For sessions of this process, the SDK state.
- For other sessions, the Claude Code live registry
(`~/.claude/sessions/<pid>.json`, with pid + procStart liveness and
Claude Desktop's rules) or the transcript tail. The registry is read
only for the row state.
- `error` means the session is not running and its last turn ended with
an error, not a cancel.

Documented in `docs/air-extensions.md`, with an extension → RFD mapping
table and the remaining differences.

### Measured on a real `~/.claude` (233 transcripts, read-only)

| Case | New path | Old SDK path |
|---|---|---|
| cwd, limit 50, cold process | ~80 ms | 78–156 ms (all rows) |
| cwd, limit 50, warm | 1.3–4 ms | — |

## Session list subscription

Replaces polling the first page. It is only for a client that declared
`sessionIndex`; the agent advertises `sessionListSubscribe` with it,
never under ACP v2.

- **Subscribe**: `_session/list/subscribe { cwd }` returns `{
subscriptionId }`. `cwd` must be absolute, else `-32602`; other params
are ignored.
- The scope is `cwd` and the same subdirectory in every linked worktree,
in any archive state, without sidechains.
- **Changes**: `_session/list/changes { subscriptionId, sessions,
removed }`.
- `sessions` holds the full `session/list` row of each session that
appeared, or whose title, `lastPromptAt`, `state`, `lastTurnEndedAt`,
`cost`, `model`, `forkedFrom` or `archived` changed since the last row
sent.
- The first change of a session after subscribe is sent in full. After
that, a change of `updatedAt` alone sends nothing.
  - `removed` lists sessions that were deleted or moved out of scope.
  - Archive and unarchive are row changes.
- A session is sent at most once a second, with one notification per
subscription per pass.
- **Unsubscribe**: `_session/list/unsubscribe { subscriptionId }`
returns `{}` and is idempotent.
- A subscription lives until unsubscribe or until the connection closes.
- A connection has at most 128 subscriptions. The 129th is `-32602` with
`data.reason: "too_many_subscriptions"`.
- Subscriptions of the same `cwd` share one watch and are notified
independently.
- **Delivery** is best effort, with no resync and no sequence numbers. A
rescan every 10 s (and 1 s after a project directory watch opens) sends
whatever differs.
- **Implementation** (`list-subscriptions.ts`, `session-info.ts`):
- A non-recursive `fs.watch` on the scope's project dirs, the registry
and the projects root.
- A transcript event re-reads only that transcript, and a registry event
reads only that `<pid>.json`. Sessions running here push their SDK state
at once.
- Subscribe reads no session row. The rescan reads only transcripts
whose stat changed or whose read failed.

### Measured: one subscription on the 5,200-session project (Docker
`node:22`, 2 CPU, 2 GB, shared host)

300 s of churn: 20 sessions appending 10/s each (updatedAt-only),
registry status flips every 200 ms, and a session created every 2 s and
deleted 10 s later.

| | Old `list_changed` watcher | Subscription |
|---|---|---|
| Agent CPU p50 / max | 19% / — | 12.9% / 49% |
| Event loop p99 (p50 / max of 1 s windows) | — | 16 / 31 ms; worst
single delay 87 ms |
| Change latency p50 / p95 | 950 ms (hint) | create 291 / 570 ms, delete
279 / 506 ms; registry state 902 / 990 ms* |
| Notifications / min | — | 90 (896 rows, 0 rows for updatedAt-only
appends) |
| RSS | flat ~160 MB | 159 → 198 MB (heap flat ~50 MB) |

\* The registry flips the same 5 sessions every 200 ms, so each is held
to the once-a-second limit. Latency is measured from the first unsent
change.

These numbers were measured with an earlier subscribe that read every
row up front. Subscribe now reads no rows, so that one-off cost no
longer occurs. The lazy subscription has not been re-measured yet.

## Load test: 10,000 sessions (Docker, 2 CPU, 2 GB)

Calibrated data: 10k transcripts, 15 GB, 36k subagent files, and one
5,200-session project. Sizes match a real profile (p50 311K / p90 2.67M
/ p99 20.5M).

| Scenario (limit 50) | Before | After |
|---|---|---|
| Big project, first page cold / warm p50 | out of memory | 225 / 19 ms
|
| No cwd, cold / warm p50 | 1,722 / 178 ms | 209 / 45 ms |
| Page through 4,637 rows | 457 s | 11.4 s cold, 2.9 s warm |
| Delete p50 / 100 concurrent | 23.8 ms / 4.4 s | 7.8 ms / 0.8 s |
| 300 s of churn (60k changes) | out of memory after 3 s | list p50 54
ms, 0 stalls >50 ms, RSS flat ~160 MB |

## Streaming: keep the event loop responsive under a backlog

Under a backlog of buffered SDK messages, the adapter could freeze its
event loop for seconds. Every `query.next()` on a non-empty SDK queue
resolves in a microtask, so `runConsumer` forwarded the whole backlog
without reaching a macrotask boundary. Incoming JSON-RPC, including
`session/cancel`, waited until the backlog drained.

1. **Consumer yields to the event loop.** A shared `EventLoopYielder`
(`src/utils.ts`) gives the event loop a turn via `setImmediate` once the
current iteration has run for 8 ms. Order, content and cancel semantics
are unchanged.
2. **No per-message copy in `nodeToWebWritable`** for byte streams.
Object-mode streams keep the copy.
3. **Diff memo.** The approval preview and the PostToolUse patch no
longer diff the same texts twice. Timed-out diffs are not remembered.
4. **Prefix guards** in `parseMcpCommand` and `sanitizeTitle`. Both give
identical output.
5. **Replay yields between history messages**, with the same budget.

With 10 sessions × 1 MB, the max event-loop delay went from 4205 ms to
1626 ms, and ping p95 from 2793 ms to 262 ms (interleaved A/B, Docker
`node:22`, 2 CPU). Single-stream throughput is unchanged. Remaining
stalls are inside the Claude SDK and GC.

## Test plan

- [x] Rebased on `origin/main`. `npm run check`, `npm run build` and
`npm run test:run` pass.
- [x] Regression tests for the unchanged non-`sessionIndex` paths.
- [x] Regression tests for every review finding.
- [x] Reviews: Codex (gpt-6-astra) and a Claude review subagent, run as
independent passes against `origin/main` until a round found nothing of
normal or higher severity.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:59:47 +04:00
nikita-ashihmin
0e83a06c4a fix: async task routing, Monitor tasks, structured task ids, and abandoned tool calls (#1249)
It affects AIR clients with the `asyncTasks` capability.

## Problem

- A subagent can start a background Bash or Monitor and then end its
turn. The task ends after the subagent finished. The route of the task
dropped every update of a finished child generation, so the client never
got the terminal `async_task_state_update`. The task stayed running in
the client forever. One AIR log held 440 such dropped updates.
- The `Monitor` tool streams the stdout of a background command to the
model only. The SDK reports its task as `local_bash`, so the client
showed it as a background shell. In one real session, 18 of 18 Monitor
tasks had no output file, no progress and no result. The Stop button
only broke the agent's wait.
- The adapter read a task id from the text of a Bash tool result. A
foreground `sed`, `strings` or `python3` that printed adapter source,
test fixtures or CLI strings became a background task, for example with
the id `";\n const start = text` or `${e}`. The client got a spawn and a
`backgrounded` marker on a foreground call, and the task never ended.
- Claude Code 2.1.287 reports a background command in four texts, and
only one gave an output path. A subagent tool result almost never has
`tool_use_result` (0 of 48 in one session). So a subagent command that
hit its timeout got no output path (9 of 10).

## Fix

- `NativeSubagentRuntime.routeOfToolCall` still sends
`async_task_spawned`, `async_task_progress` and
`async_task_state_update` to the child generation that owned the tool
call after that child finished. It never sends them to a later
generation. Every other late update is dropped as before. The spawn is
in the list because a held task can get its tool call id after the
subagent finished.
- `AsyncTaskRuntime` takes a `toolNameOf` lookup. A task of a `Monitor`
tool call is ignored, like a subagent task: no spawn, no progress, no
state, and no stop. A held task that gets its tool call id later goes
through the same check. A `local_monitor` task is ignored too. The
`Monitor` tool call stays in the transcript.
- A task comes only from structured data:
`tool_use_result.backgroundTaskId` or the SDK task events. The text id
parser is gone.
- The output path of a known task can come from the result of its own
tool call. The path ends with `tasks/<id>.output` and starts after the
nearest `": "` on the same line, so a path with spaces works, for
example under `C:\Users\John Doe\AppData\Local\Temp`. It must be
absolute (POSIX, a Windows drive or UNC), and it never holds the end of
another path. The parse is linear in the text length. All four texts
give the path in both event orders: the tool result before or after the
SDK names the task. The memory is bounded. A structured path still wins,
and the terminal `output_file` reaches the client also after a level
close.
- The `backgrounded` marker comes only from structured data: the
structured task, a known background task of the tool call, or a
`run_in_background` input. The "both event orders" above holds for the
path, not for this marker. The marker rides the tool result update, so a
command that the SDK names only after its tool result gets the marker
only from a `run_in_background` input. A command that the user moved to
the background by hand gets no marker in that order.
- The replay behaves the same in each case.

## Tests

- Native subagent and agent tests: a task update and a late spawn after
the child finished go to `worker-1`; a held task of a subagent gets its
spawn and state in the child session after the subagent finished; a late
message chunk is still dropped; a subagent's background Bash ends after
the subagent and the client gets the terminal state in the child
session.
- Monitor: runtime, live and replay tests, also a task that names its
tool call after the tool result. A Monitor task cannot be stopped.
- Structured ids: foreground output with each marker text creates no
task; each of the four texts gives the path in both orders, also a
Windows and a POSIX path with spaces and a UNC path; no path of another
task, another tool call, `.outputs`, a relative path, a path without a
`": "` boundary, or a path across a line; two paths on one line are not
glued; a 2 MB text without `": "` parses in milliseconds; a structured
path wins. An agent test covers a subagent without `tool_use_result`,
and a replay test takes the path from the tool result.
- The new tests fail without the change. Build, lint, format and all
tests pass.




## Abandoned streamed tool calls

This part affects every client, Zed included.

**Problem.** The adapter opens a tool call at the `content_block_start`
of a streamed `tool_use`. Claude can abandon that block before it is
complete, for example for a steering message. The tool then never runs.
At end_turn the settle check reported the session failure "Claude ended
the turn without returning results for tool calls"
(`incomplete_tool_call`). A real AIR session showed a red service error
after 27 minutes of normal work, for a call that never had input.

**Fix.**
- The session records the tool calls that Claude Code sent to the tool
runner: the `tool_use` blocks of a complete assistant message, and the
tool calls of a permission request. The record is bounded and is dropped
when the call ends.
- At end_turn, an open foreground call without that record is abandoned.
The adapter closes it as failed with "Claude stopped this tool call
before it ran." and reports no session failure. The turn ends as
`end_turn`.
- An open call with that record keeps the old behaviour: the old failure
text and the `incomplete_tool_call` session failure.
- The cleanup and the cancellation checks are the same for both groups.
- The replay does not change, because the history holds no partial
`tool_use`. No scenario golden file changes, for AIR, plain or Zed.

**Tests.** A partial `tool_use` and then text ends as `end_turn` with
the neutral close. A mixed turn still fails for the unfinished call. A
steering message during a partial `tool_use` gives no failure. The
existing incomplete-tool tests now send the complete `tool_use` and keep
their failure. The new tests fail without the change.
2026-10-05 18:59:32 +04:00
nikita-ashihmin
190a00f68f feat: answer /mcp in chat with the MCP server status and a reconnect (#1218)
## Summary

In an ACP client, `/mcp` showed the CLI text "… Use `/mcp` in the
terminal for details." The user of an ACP client has no terminal. In SDK
mode, Claude Code also refuses `/mcp reconnect|enable|disable` with
"Reconnect, enable, and disable aren't available in this session." and
changes nothing.

- `/mcp` and `/mcp reconnect|enable|disable [<server>|all]` still run as
a normal Claude Code turn. The adapter replaces the text of the local
command, through the same hook that `/usage` uses. Prompt order, cancel,
and the provider update stay with the turn.
- For an action, the adapter runs it in that turn through the SDK
control API (`reconnectMcpServer`, `toggleMcpServer`). It shows one
result line for each server, then the status list.
- The status list comes from `query.mcpServerStatus()`. It has a summary
line and groups by status: failed, needs authentication, connecting,
connected, and disabled on one line. A failed server shows a short
cleaned error. A list is used and not a table, because a table breaks
words in narrow columns.
- Useful CLI sentences stay, for example "There's no MCP server named
…". Sentences that point to the terminal are removed.
- After an explicit reconnect, a server from the ACP session that needs
authentication starts the existing MCP OAuth flow, when the client
supports URL elicitation.
- `/mcp` is advertised with the input hint `[reconnect|enable|disable
[<server>|all]]` and is hidden on history replay.

## Other commits in this branch

This branch also carries commits of other work: skill command metadata
for AIR, a faster `session/load` replay, and the resumed permission
mode. They can move to separate PRs if preferred.

## Tests

- `src/tests/mcp-command.test.ts` (61 tests). The fake CLI answers with
the real SDK-mode texts and message shapes (synthetic assistant +
result) and does not change any status.
- A live run through the adapter with a broken and a working stdio
server: `/mcp`, `/mcp disable good`, `/mcp enable good`, `/mcp reconnect
broken`, `/mcp reconnect nosuch`.
- `compare.ts` allows one new difference against the origin/main
baseline: the `mcp` command in `available_commands_update`. The baseline
files are unchanged.
- `npm run check` and `npm run test:run` pass.
2026-10-02 17:37:59 +04:00
Kirill Alekhnovich
af0b43de94 fix: send the legacy custom answer key to AIR (#1210)
Released AIR versions, such as 262.air/release-09, read the custom
answer companion only from the root key
_meta._askUserQuestionCustomAnswer. The adapter sent only
_meta.jetbrains.air.customAnswer, so these versions showed the companion
as a separate question.

For an AIR client the companion now carries both keys with the same
value. Other clients get no key, as before. Remove the root key when the
ACP registry no longer serves AIR versions that read only that key.

IJAI-2892
2026-10-01 13:25:01 +02:00
nikita-ashihmin
875d75f2a4 feat: AIR tool call contract, exact git patches, plan file, and fixes for every client (#1153)
## Summary

One PR with separate commits (the earlier stack #1168 and #1169 is
folded back in).

### Exact git patches
- `src/diff.ts` builds exact git patches for Edit and Write: real line
numbers, file modes, CRLF, the final-newline marker, a size limit
(including a bound for `replace_all`), binary detection, and an
asynchronous diff with a time budget. The approval preview mirrors the
input normalisation of the bundled CLI.
- No creation patch over an existing file or for an Edit of an existing
empty file, and no patch for a Write that changed nothing.

### For every client
Clients that do not declare `_meta.jetbrains.air` (Zed, plain ACP
clients) get the same information as on `main`.
- A `tool_call_update` omits top-level fields that did not change since
the last report of the same tool call (ACP merge semantics). The `_meta`
of each report stays complete.
- Bug fixes: a failed approval preview no longer blanks the approval
diff; a permission denial replaces a pinned approval patch; the
compaction summary is not repeated when the streamed text is identical;
streamed subagent text is not sent twice; async task fields are recorded
only after a successful send; late updates of a finished native subagent
are dropped; the updates of a fresh Claude context after a clear-context
restart reach the ACP session.

### For AIR only
- One fact in one field: input in `rawInput`, the file text of an edit
only in the diff (`diffPatch`), output once, no display copies of the
input (`rawInputRendering`), `_meta.claudeCode` reduced to `toolName`,
`parentToolUseId` and the `toolResponse` markers, per-key `_meta`
filtering.
- Plan file mode (`planFile`): ExitPlanMode carries only
`rawInput.planFilePath`; AIR follows the file that Claude writes.
- AIR-only keys move under `_meta.jetbrains.air.*` and are no longer
sent to other clients.

### Structure and docs
- `src/tool-calls/`: one reporter per tool, one `ClientCapabilities`,
one `AcpToolCallRenderer`, the field tracker and the changed-meta
filter.
- `docs/air-extensions.md` replaces the per-extension AIR docs.

## Tests
- `npm run build`, `npm run check`, `npm run test:run`: 1995 passed.
- Scenario tests for plain, Zed, and AIR clients: the SDK mock honours
the adapter's session id, every update must reach its ACP session, every
message is validated against the ACP JSON schema with all formats
checked, plain and Zed are compared with a recorded `main` baseline, AIR
keeps compact golden recordings.
2026-09-28 17:58:00 +04:00
Ben Brandt
79851b8668 feat: Send experimental session notices for live advisories (#1155) 2026-09-22 13:46:49 +00:00
hwisu
bbae5c2c09 fix: show shell commands in permission prompts (#1070)
## Summary
- use the canonical tool-call title for permission prompts, so shell
approvals show the exact command instead of the model-authored
description
- map `PowerShell` through the same shell presentation path as `Bash`
- complete the PowerShell terminal lifecycle with matching
`terminal_info`, `terminal_output`, and `terminal_exit` metadata,
including failed commands
- add focused regression coverage without changing the existing Bash
lifecycle tests

Fixes #1068

## PowerShell input and result contract
`PowerShell` is a distinct tool name, but its documented input fields
match `Bash`: the executable command is in `command`, while
`description` is an optional human-readable summary. The Claude Code
hooks reference documents the shared `command`, `description`,
`timeout`, and `run_in_background` fields, and the tools reference
likewise states that PowerShell hooks receive the command in
`tool_input.command`.

Anthropic closed
[claude-code#83647](https://github.com/anthropics/claude-code/issues/83647#issuecomment-5310033500)
after documenting this equivalence. Reusing the SDK's exported
`BashInput` shape for the common input fields is therefore intentional;
the SDK currently does not export a separate `PowerShellInput` type.

PowerShell results do not need a PowerShell-specific output assumption
here. The result mapper first shape-checks structured stdout/stderr and
otherwise falls back to the standard raw `tool_result` string or
content-block array. Routing PowerShell through that existing mapper
preserves this fallback while completing the adapter's capability-gated
terminal sequence:

1. initial `tool_call` with `terminal_info`
2. `tool_call_update` with `terminal_output`
3. final `tool_call_update` with `terminal_exit`

All three events use the original tool-use ID, so the client can
reconcile and close the terminal. Failed PowerShell results also bypass
the generic error renderer and finish the terminal with a non-zero
display exit code.

The permission presentation introduced in #1004 already handled `Bash`
and `PowerShell` together when choosing a description-based permission
title, but the canonical tool presentation and terminal result paths
only handled `Bash`. This PR completes both paths.

References:
- [Claude Code hooks: PowerShell
input](https://code.claude.com/docs/en/hooks#powershell)
- [Claude Code tools reference: PowerShell
tool](https://code.claude.com/docs/en/tools-reference#powershell-tool)
- [Anthropic schema
confirmation](https://github.com/anthropics/claude-code/issues/83647#issuecomment-5310033500)

## Verification
- `npm run check`
- `npm run build`
- `npm run test:run -- src/tests/tools.test.ts -t "PowerShell terminal
output"` (1 passed)
- full suite with an empty temporary Claude config:
`CLAUDE_CONFIG_DIR=<empty temporary directory> npm run test:run` (1070
passed, 27 skipped)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-09-17 13:34:05 +00:00
nikita-ashihmin
735950a83e perf: supply AIR diff counts from Claude structured patches (#1122)
Claude supplies structured patches for Edit and Write, but ACP diff
blocks previously carried only the reconstructed texts. AIR had to
compare those texts again to display added and removed counts.

Count patch operations in the existing conversion loop and publish
`_meta.jetbrains.air.diffStats` on each diff block. This uses the same
contract as https://github.com/agentclientprotocol/codex-acp/pull/501.
Each block has its own counts. No extra traversal, full file comparison,
or navigation calculation is needed.

Omit statistics when patches are absent or inconsistent, so clients
retain their existing fallback. Exclude EOF markers from the displayed
texts. Document the extension and cover per-block counts, Edit and Write
hooks, EOF changes, and fallback behavior.

Validation:
- `npm run build`
- `npm run check`
- `env -u ANTHROPIC_BASE_URL npm run test:run` (the local endpoint
override affects unrelated provider tests)
2026-09-15 12:39:39 +04:00
Ben Brandt
3d99cafe4a fix: honor host opt-out for allowDangerouslySkipPermissions (#1129)
## Summary

Closes #1119. Supersedes #1120 by @amansk (their commits are included on
this branch; the fork does not allow maintainer edits, so review fixes
landed here instead).

Hosts that never use `bypassPermissions` can opt a session out by
sending `_meta.claudeCode.options.allowDangerouslySkipPermissions:
false` on `session/new` (and again on `session/load`). When omitted or
`true`, behavior is unchanged: bypass is available on non-root sessions.

## Design

Bypass availability is decided once per session in `createSession`:

```ts
const allowBypass =
  ALLOW_BYPASS && sessionMeta?.claudeCode?.options?.allowDangerouslySkipPermissions !== false;
```

and threaded to the three places that must agree:

- the SDK `allowDangerouslySkipPermissions` flag (the actual
enforcement; the CLI refuses bypass without it)
- the spawn-time mode clamp in `resolvePermissionMode`, so a
`permissions.defaultMode` of `bypassPermissions` (or a mode carried
across a clear-context restart) becomes `default` before `query()`
rather than failing at spawn
- the session mode catalog in `SessionModeManager.initialize`, so the
client is never offered a mode the CLI would reject

## Changes from #1120

- Fold the host opt-out into `resolvePermissionMode` via an
`allowBypass` parameter instead of a second inline clamp; drop the
separate `resolveAllowDangerouslySkipPermissions` helper
- Make `allowBypass` a required `initialize` parameter and remove the
module-global `ALLOW_BYPASS` fallback from `session-mode.ts`
- Fix `tsc` errors in the new tests (`modes` is optional on
`NewSessionResponse`), which broke the CI build job
- Move the settings-driven clamp test onto the shared
`acp-agent-settings` fixture (isolated cwd, no double module reload,
passes as root)
- Document the opt-out in `docs/permission-extension.md`

## Testing

- `npm run check`, `npm run build` — pass
- `npm run test:run` — pass (the branch is based before #1123, so the
SDK version guard in `model-presentation.test.ts` only passes with the
pinned 0.3.257; it clears on rebase)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Amandeep Khurana <amansk@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 15:11:30 +02:00
dependabot[bot]
d3205ab11f feat(deps): Bump @anthropic-ai/claude-agent-sdk to 0.3.270 (#1123)
Bumps the minor group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.257` | `0.3.263` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.122.0` | `0.124.0` |
|
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
| `26.4.0` | `26.5.0` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.69.0` | `8.70.0` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.69.0` | `8.70.0` |
| [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` |
| [hono](https://github.com/honojs/hono) | `4.13.5` | `4.13.7` |
| [jose](https://github.com/panva/jose) | `6.2.10` | `6.2.12` |


Updates `@anthropic-ai/claude-agent-sdk` from 0.3.257 to 0.3.263
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases">@​anthropic-ai/claude-agent-sdk's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.263</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.263</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.263
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.263
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.263
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.263
</code></pre>
<h2>v0.3.261</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>pluginDelivery: 'initialize'</code> to send
<code>plugins</code> over stdin so the launch command line no longer
grows with the plugin count (fixes Windows start failures with many
plugins)</li>
<li>Fixed <code>query()</code> throwing &quot;Object not
disposable&quot; in runtimes without a native
<code>Symbol.dispose</code>, such as Node ≤22 <code>vm</code> contexts
(Jest's <code>node</code> environment, vitest
<code>vmThreads</code>/<code>vmForks</code>) and Node &lt;18.18</li>
<li>Updated to parity with Claude Code v2.1.261</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.261
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.261
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.261
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.261
</code></pre>
<h2>v0.3.260</h2>
<h2>What's changed</h2>
<ul>
<li>Added optional <code>user_message_uuid</code> to
<code>thinking_tokens</code> system messages, linking thinking progress
to the user message that triggered the turn</li>
<li>Added optional <code>first_content_frame_ms</code>,
<code>first_stream_post_ms</code>, <code>first_stream_post_ack_ms</code>
and <code>first_stream_post_wall_ms</code> fields to the success result
message for remote-session latency breakdowns</li>
<li>Fixed <code>managedSettings</code> <code>disableAutoMode:
&quot;disable&quot;</code> (either spelling) being dropped by the
restrictive-only filter instead of turning auto mode off for the spawned
session</li>
<li>Fixed <code>rewindFiles()</code> reporting success when no files
could be restored (for example when checkpoint backups are missing); it
now fails</li>
<li>Changed <code>error_max_structured_output_retries</code> results to
append the last StructuredOutput tool error; validation errors now name
the offending key, allowed values, and actual length or count</li>
<li>Changed <code>rate_limit_event</code> to also re-emit during an
exceeded window on repeat 429s (about once per 30 seconds per limit
window), so stream consumers can refresh stale rate-limit state</li>
<li>Updated to parity with Claude Code v2.1.260</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/claude-agent-sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.3.263</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.263</li>
</ul>
<h2>0.3.262</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.262</li>
</ul>
<h2>0.3.261</h2>
<ul>
<li>Added <code>pluginDelivery: 'initialize'</code> to send
<code>plugins</code> over stdin so the launch command line no longer
grows with the plugin count (fixes Windows start failures with many
plugins)</li>
<li>Fixed <code>query()</code> throwing &quot;Object not
disposable&quot; in runtimes without a native
<code>Symbol.dispose</code>, such as Node ≤22 <code>vm</code> contexts
(Jest's <code>node</code> environment, vitest
<code>vmThreads</code>/<code>vmForks</code>) and Node &lt;18.18</li>
<li>Updated to parity with Claude Code v2.1.261</li>
</ul>
<h2>0.3.260</h2>
<ul>
<li>Added optional <code>user_message_uuid</code> to
<code>thinking_tokens</code> system messages, linking thinking progress
to the user message that triggered the turn</li>
<li>Added optional <code>first_content_frame_ms</code>,
<code>first_stream_post_ms</code>, <code>first_stream_post_ack_ms</code>
and <code>first_stream_post_wall_ms</code> fields to the success result
message for remote-session latency breakdowns</li>
<li>Fixed <code>managedSettings</code> <code>disableAutoMode:
&quot;disable&quot;</code> (either spelling) being dropped by the
restrictive-only filter instead of turning auto mode off for the spawned
session</li>
<li>Fixed <code>rewindFiles()</code> reporting success when no files
could be restored (for example when checkpoint backups are missing); it
now fails</li>
<li>Changed <code>error_max_structured_output_retries</code> results to
append the last StructuredOutput tool error; validation errors now name
the offending key, allowed values, and actual length or count</li>
<li>Changed <code>rate_limit_event</code> to also re-emit during an
exceeded window on repeat 429s (about once per 30 seconds per limit
window), so stream consumers can refresh stale rate-limit state</li>
<li>Updated to parity with Claude Code v2.1.260</li>
</ul>
<h2>0.3.259</h2>
<ul>
<li>Added <code>user_message_uuids</code> beside
<code>user_message_uuid</code> on a turn's first reply frame and result:
every user message the turn answered, so a reply to several merged
messages can be matched to each</li>
<li>Added <code>permissionPrompts: 'none'</code> option to auto-deny
permission prompts in sessions with nobody to answer them, without
disabling auto mode's classifier</li>
<li>Updated to parity with Claude Code v2.1.259</li>
</ul>
<h2>0.3.258</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.258</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="69f318f92f"><code>69f318f</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="a96f6b5cbe"><code>a96f6b5</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="a79d677cbd"><code>a79d677</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="e3d162bdf0"><code>e3d162b</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="eefd0df145"><code>eefd0df</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.257...v0.3.263">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.122.0 to 0.124.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases">@​anthropic-ai/sdk's
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.124.0</h2>
<h2>0.124.0 (2026-09-04)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.123.0...sdk-v0.124.0">sdk-v0.123.0...sdk-v0.124.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add Claude Tag category and user breakdowns to
usage reports (<a
href="669ff22431">669ff22</a>)</li>
<li><strong>api:</strong> add named types for organization compliance
settings state (<a
href="54be8da717">54be8da</a>)</li>
<li><strong>api:</strong> add support for sending a workspace ID on more
endpoints (<a
href="a11e6f1d3b">a11e6f1</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>repair custom-code merge in messages resources (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/455">#455</a>)
(<a
href="7ac43b92e7">7ac43b9</a>)</li>
<li><strong>tools:</strong> create agent-toolset files and directories
owner-only (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/485">#485</a>)
(<a
href="a9c2298873">a9c2298</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> bundle the mock server spec and update
breaking-change detection (<a
href="8752d02422">8752d02</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="a63afbc47d">a63afbc</a>)</li>
<li><strong>tests:</strong> reword the skip reason on the path-level
query param tests (<a
href="999bee5bb8">999bee5</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> update a few doc strings (<a
href="2ef3d59b81">2ef3d59</a>)</li>
</ul>
<h2>sdk: v0.123.0</h2>
<h2>0.123.0 (2026-09-01)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.122.0...sdk-v0.123.0">sdk-v0.122.0...sdk-v0.123.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> beta user profiles: add
external_user_onboarded_at, remove relationship in favor of access_type
(<a
href="3efb1a1a81">3efb1a1</a>)</li>
<li><strong>api:</strong> manual updates (<a
href="c6f0bdaff6">c6f0bda</a>)</li>
<li><strong>api:</strong> organization compliance settings, user-profile
order_by, memory-store and toolset schema updates (<a
href="8e2f0c2f7e">8e2f0c2</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>keep credential file access out of non-Node bundles (<a
href="ab6a4b2814">ab6a4b2</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="788ea8bdaa">788ea8b</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.124.0 (2026-09-04)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.123.0...sdk-v0.124.0">sdk-v0.123.0...sdk-v0.124.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add Claude Tag category and user breakdowns to
usage reports (<a
href="669ff22431">669ff22</a>)</li>
<li><strong>api:</strong> add named types for organization compliance
settings state (<a
href="54be8da717">54be8da</a>)</li>
<li><strong>api:</strong> add support for sending a workspace ID on more
endpoints (<a
href="a11e6f1d3b">a11e6f1</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>repair custom-code merge in messages resources (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/455">#455</a>)
(<a
href="7ac43b92e7">7ac43b9</a>)</li>
<li><strong>tools:</strong> create agent-toolset files and directories
owner-only (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/485">#485</a>)
(<a
href="a9c2298873">a9c2298</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> bundle the mock server spec and update
breaking-change detection (<a
href="8752d02422">8752d02</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="a63afbc47d">a63afbc</a>)</li>
<li><strong>tests:</strong> reword the skip reason on the path-level
query param tests (<a
href="999bee5bb8">999bee5</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> update a few doc strings (<a
href="2ef3d59b81">2ef3d59</a>)</li>
</ul>
<h2>0.123.0 (2026-09-01)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.122.0...sdk-v0.123.0">sdk-v0.122.0...sdk-v0.123.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> beta user profiles: add
external_user_onboarded_at, remove relationship in favor of access_type
(<a
href="3efb1a1a81">3efb1a1</a>)</li>
<li><strong>api:</strong> manual updates (<a
href="c6f0bdaff6">c6f0bda</a>)</li>
<li><strong>api:</strong> organization compliance settings, user-profile
order_by, memory-store and toolset schema updates (<a
href="8e2f0c2f7e">8e2f0c2</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>keep credential file access out of non-Node bundles (<a
href="ab6a4b2814">ab6a4b2</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="788ea8bdaa">788ea8b</a>)</li>
</ul>
<h3>Documentation</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ba14b1f4fd"><code>ba14b1f</code></a>
Merge pull request <a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1177">#1177</a>
from anthropics/release-please--branches--main--chan...</li>
<li><a
href="f99c1a7942"><code>f99c1a7</code></a>
chore: release main</li>
<li><a
href="54be8da717"><code>54be8da</code></a>
feat(api): add named types for organization compliance settings
state</li>
<li><a
href="999bee5bb8"><code>999bee5</code></a>
chore(tests): reword the skip reason on the path-level query param
tests</li>
<li><a
href="a9c2298873"><code>a9c2298</code></a>
fix(tools): create agent-toolset files and directories owner-only (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/485">#485</a>)</li>
<li><a
href="2637d1198b"><code>2637d11</code></a>
chore(examples): refresh platform model IDs (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/376">#376</a>)</li>
<li><a
href="669ff22431"><code>669ff22</code></a>
feat(api): add Claude Tag category and user breakdowns to usage
reports</li>
<li><a
href="a9090aff65"><code>a9090af</code></a>
Build SDK</li>
<li><a
href="8752d02422"><code>8752d02</code></a>
chore(internal): bundle the mock server spec and update breaking-change
detec...</li>
<li><a
href="a63afbc47d"><code>a63afbc</code></a>
chore(internal): codegen related update</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.122.0...sdk-v0.124.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 26.4.0 to 26.5.0
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/eslint-plugin's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md">@​typescript-eslint/eslint-plugin's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li><a
href="f66bdcac30"><code>f66bdca</code></a>
test(eslint-plugin): [member-ordering] use <code>RuleTester</code>
directly in `optional...</li>
<li><a
href="4586535ab2"><code>4586535</code></a>
fix(eslint-plugin): [no-deprecated] report deprecated imported values
used in...</li>
<li><a
href="f8e1e4e2f7"><code>f8e1e4e</code></a>
fix(eslint-plugin): [no-unnecessary-condition] no false positive on RHS
of a ...</li>
<li><a
href="889cece8bb"><code>889cece</code></a>
fix(eslint-plugin): [member-ordering] don't report fields that read
fields de...</li>
<li><a
href="1a5a8b124b"><code>1a5a8b1</code></a>
feat(eslint-plugin): [no-generated-empty-object-type] add rule (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12730">#12730</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/parser's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md">@​typescript-eslint/parser's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.9.1 to 10.10.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.10.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="264b4346d1"><code>264b434</code></a>
feat: add <code>d</code> and <code>v</code> flags to
<code>no-unexpected-multiline</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21305">#21305</a>)
(Gihyeon Jeong / 정기현)</li>
<li><a
href="c6cc6c592f"><code>c6cc6c5</code></a>
feat: check <code>Object.prototype</code> property names in
<code>new-cap</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21269">#21269</a>)
(crimsonjay0)</li>
<li><a
href="5661fa65fd"><code>5661fa6</code></a>
feat: no-extra-bind false negatives with class fields and static blocks
(<a
href="https://redirect.github.com/eslint/eslint/issues/21260">#21260</a>)
(synthex-byte)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="bb47dc6da2"><code>bb47dc6</code></a>
fix: update dependency file-entry-cache to v11 (<a
href="https://redirect.github.com/eslint/eslint/issues/20801">#20801</a>)
(Milos Djermanovic)</li>
<li><a
href="427ac0a014"><code>427ac0a</code></a>
fix: use format strings in debug calls (<a
href="https://redirect.github.com/eslint/eslint/issues/21247">#21247</a>)
(Francesco Trotta)</li>
<li><a
href="9d8153223d"><code>9d81532</code></a>
fix: support <code>__proto__</code> in <code>/* exported */</code>
comments (<a
href="https://redirect.github.com/eslint/eslint/issues/21261">#21261</a>)
(sethamus)</li>
<li><a
href="87e0a08243"><code>87e0a08</code></a>
fix: prefer-object-has-own autofix breaks when Object is shadowed (<a
href="https://redirect.github.com/eslint/eslint/issues/21282">#21282</a>)
(김채영)</li>
<li><a
href="8e2cb14221"><code>8e2cb14</code></a>
fix: <code>new-cap</code> false positive for <code>UTC</code> calls with
<code>properties: false</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21275">#21275</a>)
(Pixel)</li>
<li><a
href="9f4a364ab0"><code>9f4a364</code></a>
fix: Ignore static imports in no-unreachable (<a
href="https://redirect.github.com/eslint/eslint/issues/21276">#21276</a>)
(Taha Kotil)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="2417cad57d"><code>2417cad</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="9cecb8a0a2"><code>9cecb8a</code></a>
docs: document <code>\c</code> control letter escapes in
no-control-regex (<a
href="https://redirect.github.com/eslint/eslint/issues/21286">#21286</a>)
(한국)</li>
<li><a
href="8724829f69"><code>8724829</code></a>
docs: update compat table links (<a
href="https://redirect.github.com/eslint/eslint/issues/21263">#21263</a>)
(fnx)</li>
<li><a
href="5634542be5"><code>5634542</code></a>
docs: Clarify eqeqeq suggestion behavior (<a
href="https://redirect.github.com/eslint/eslint/issues/21256">#21256</a>)
(Müslüm Yılmaz)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="b3d876b460"><code>b3d876b</code></a>
chore: disable npm audit in ecosystem tests (<a
href="https://redirect.github.com/eslint/eslint/issues/21306">#21306</a>)
(Francesco Trotta)</li>
<li><a
href="1696682791"><code>1696682</code></a>
ci: restore EMFILE test on Node.js 26 (<a
href="https://redirect.github.com/eslint/eslint/issues/21297">#21297</a>)
(Marry (Subin Yang))</li>
<li><a
href="2c7f5d6f47"><code>2c7f5d6</code></a>
chore: update github/codeql-action action to v4.37.9 (<a
href="https://redirect.github.com/eslint/eslint/issues/21296">#21296</a>)
(renovate[bot])</li>
<li><a
href="3c753f18b4"><code>3c753f1</code></a>
chore: update eslint (<a
href="https://redirect.github.com/eslint/eslint/issues/21289">#21289</a>)
(renovate[bot])</li>
<li><a
href="1c734690bf"><code>1c73469</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21280">#21280</a>)
(ESLint Bot)</li>
<li><a
href="08a02be429"><code>08a02be</code></a>
test: add error locations to <code>no-extra-boolean-cast</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21266">#21266</a>)
(lumir)</li>
<li><a
href="77bb1db8e7"><code>77bb1db</code></a>
chore: update github/codeql-action action to v4.37.8 (<a
href="https://redirect.github.com/eslint/eslint/issues/21270">#21270</a>)
(renovate[bot])</li>
<li><a
href="007e81ac0a"><code>007e81a</code></a>
ci: skip EMFILE test on Node.js 26 (<a
href="https://redirect.github.com/eslint/eslint/issues/21265">#21265</a>)
(lumir)</li>
<li><a
href="0430280e7c"><code>0430280</code></a>
chore: improve ecosystem tests compatibility on Windows (<a
href="https://redirect.github.com/eslint/eslint/issues/21178">#21178</a>)
(crimsonjay0)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3f20a57c62"><code>3f20a57</code></a>
10.10.0</li>
<li><a
href="f4e5284803"><code>f4e5284</code></a>
Build: changelog update for 10.10.0</li>
<li><a
href="bb47dc6da2"><code>bb47dc6</code></a>
fix: update dependency file-entry-cache to v11 (<a
href="https://redirect.github.com/eslint/eslint/issues/20801">#20801</a>)</li>
<li><a
href="427ac0a014"><code>427ac0a</code></a>
fix: use format strings in debug calls (<a
href="https://redirect.github.com/eslint/eslint/issues/21247">#21247</a>)</li>
<li><a
href="b3d876b460"><code>b3d876b</code></a>
chore: disable npm audit in ecosystem tests (<a
href="https://redirect.github.com/eslint/eslint/issues/21306">#21306</a>)</li>
<li><a
href="9d8153223d"><code>9d81532</code></a>
fix: support <code>__proto__</code> in <code>/* exported */</code>
comments (<a
href="https://redirect.github.com/eslint/eslint/issues/21261">#21261</a>)</li>
<li><a
href="264b4346d1"><code>264b434</code></a>
feat: add <code>d</code> and <code>v</code> flags to
<code>no-unexpected-multiline</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21305">#21305</a>)</li>
<li><a
href="1696682791"><code>1696682</code></a>
ci: restore EMFILE test on Node.js 26 (<a
href="https://redirect.github.com/eslint/eslint/issues/21297">#21297</a>)</li>
<li><a
href="2c7f5d6f47"><code>2c7f5d6</code></a>
chore: update github/codeql-action action to v4.37.9 (<a
href="https://redirect.github.com/eslint/eslint/issues/21296">#21296</a>)</li>
<li><a
href="87e0a08243"><code>87e0a08</code></a>
fix: prefer-object-has-own autofix breaks when Object is shadowed (<a
href="https://redirect.github.com/eslint/eslint/issues/21282">#21282</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.257
to 0.3.263

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.257 to
0.3.263

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.257 to
0.3.263

Updates `@eslint/plugin-kit` from 0.7.2 to 0.7.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/releases">@​eslint/plugin-kit's
releases</a>.</em></p>
<blockquote>
<h2>plugin-kit: v0.7.3</h2>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.7.2...plugin-kit-v0.7.3">0.7.3</a>
(2026-09-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>preserve <code>__proto__</code> in <code>ConfigCommentParser</code>
results (<a
href="https://redirect.github.com/eslint/rewrite/issues/501">#501</a>)
(<a
href="18773d9e41">18773d9</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/rewrite/blob/main/packages/plugin-kit/CHANGELOG.md">@​eslint/plugin-kit's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/eslint/rewrite/compare/plugin-kit-v0.7.2...plugin-kit-v0.7.3">0.7.3</a>
(2026-09-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>preserve <code>__proto__</code> in <code>ConfigCommentParser</code>
results (<a
href="https://redirect.github.com/eslint/rewrite/issues/501">#501</a>)
(<a
href="18773d9e41">18773d9</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a4466ca3f8"><code>a4466ca</code></a>
chore: release main (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/504">#504</a>)</li>
<li><a
href="f83339de1a"><code>f83339d</code></a>
docs: clarify parameter description in <code>getText</code> (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/505">#505</a>)</li>
<li><a
href="714ffde559"><code>714ffde</code></a>
docs: Update README sponsors</li>
<li><a
href="18773d9e41"><code>18773d9</code></a>
fix: preserve <code>__proto__</code> in <code>ConfigCommentParser</code>
results (<a
href="https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit/issues/501">#501</a>)</li>
<li><a
href="caffae71f6"><code>caffae7</code></a>
docs: Update README sponsors</li>
<li><a
href="ae1ba2585b"><code>ae1ba25</code></a>
docs: Update README sponsors</li>
<li><a
href="50051c603e"><code>50051c6</code></a>
docs: Update README sponsors</li>
<li><a
href="ea835245e2"><code>ea83524</code></a>
docs: Update README sponsors</li>
<li><a
href="29956ae008"><code>29956ae</code></a>
docs: Update README sponsors</li>
<li><a
href="f6d5bdff38"><code>f6d5bdf</code></a>
docs: Update README sponsors</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/rewrite/commits/plugin-kit-v0.7.3/packages/plugin-kit">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/project-service` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/project-service's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/project-service/CHANGELOG.md">@​typescript-eslint/project-service's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li><a
href="d2d07eb2ac"><code>d2d07eb</code></a>
fix(project-service): avoid discarded tsserver logs (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/project-service/issues/12748">#12748</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/project-service">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/scope-manager` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/scope-manager's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/scope-manager/CHANGELOG.md">@​typescript-eslint/scope-manager's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<p>This was a version bump only for scope-manager to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/scope-manager">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/tsconfig-utils` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/tsconfig-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/tsconfig-utils/CHANGELOG.md">@​typescript-eslint/tsconfig-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<p>This was a version bump only for tsconfig-utils to align it with
other projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/tsconfig-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/type-utils` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/type-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/type-utils/CHANGELOG.md">@​typescript-eslint/type-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<p>This was a version bump only for type-utils to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/type-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/types` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/types's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Ulrich Stark <a
href="https://github.com/ulrichstark"><code>@​ulrichstark</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/types/CHANGELOG.md">@​typescript-eslint/types's
changelog</a>.</em></p>
<blockquote>
<h2>8.70.0 (2026-09-07)</h2>
<p>This was a version bump only for types to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ee76085c2"><code>7ee7608</code></a>
chore(release): publish 8.70.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/typescript-estree` from 8.69.0 to 8.70.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/typescript-estree's
releases</a>.</em></p>
<blockquote>
<h2>v8.70.0</h2>
<h2>8.70.0 (2026-09-07)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-generated-empty-object-type] add
rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12730">#12730</a>)</li>
<li><strong>website:</strong> generate per-page social preview cards (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12734">#12734</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li>use stable release of pnpm 12 (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12808">#12808</a>)</li>
<li>update pnpm to 12.3.4 and dedupe Docusaurus packages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12829">#12829</a>)</li>
<li><strong>eslint-plugin:</strong> [member-ordering] don't report
fields that read fields declared before them (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12729">#12729</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] no false
positive on RHS of a nested logical expression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12728">#12728</a>)</li>
<li><strong>eslint-plugin:</strong> [no-deprecated] report deprecated
imported values used in object shorthand properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780">#12780</a>)</li>
<li><strong>project-service:</strong> avoid discarded tsserver logs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12748">#12748</a>)</li>
<li><strong>typescript-estree:</strong> clarify the
parserOptions.project error message (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12817">#12817</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Barry <a
href="https://github.com/barry166"><code>@​barry166</code></a></li>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg</li>
<li>Josh Goldberg ✨ <a
href="https://github.com/JoshuaKGoldberg"><code>@​JoshuaKGoldberg</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaibling...

_Description has been truncated_

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 13:14:44 +02:00
Ben Brandt
67c7c9b117 feat: Add tool names to tool calls (#1128)
Supports the new tool call name RFD
2026-09-14 11:11:52 +00:00
Andrey Bragin
4deace4037 ci: poll for package availability after publish before triggering registry update (#1121) 2026-09-11 11:46:03 +02:00
nikita-ashihmin
2b666c3d35 feat: advertise recommended config values (#1111)
Clients that negotiate the AIR `recommendedValue` capability receive
concrete model and effort choices instead of ambiguous default rows. The
SDK's default model is mapped only to an exact selectable model
(including equivalent context suffix spellings); unavailable
recommendations retain the legacy default. Recommendations remain
independent of explicit selections.

The adapter applies the displayed effort to the SDK at startup and on
model switches, preserving supported user pins and re-reading per-model
settings for automatic choices. Permission modes and clients without the
capability retain their existing configuration behavior. Model and
effort configuration logic is extracted into dedicated modules.

Terse SDK model labels are enriched with concrete versions from model
metadata: for example, `Fable` becomes `Fable 5.1`, `Sonnet` becomes
`Sonnet 5`, and `Opus (1M context)` becomes `Opus 5`. Custom names are
preserved, and collisions fall back to the original labels. An SDK
version guard requires review on upgrades, and an opt-in live
initialization test verifies the upstream model contract without sending
a prompt.

Validation:
- `npm run check`
- `npm run build`
- `env -u ANTHROPIC_BASE_URL npm run test:run` — 1253 passed, 28 skipped
- `RUN_INTEGRATION_TESTS=true npx vitest run
src/tests/model-presentation.test.ts` — 4 passed
- Regression coverage for unavailable model generations/context windows,
startup effort, per-model settings, unsupported effort pins, and
duplicate Opus labels
2026-09-10 01:10:31 +04:00
Andrey Bragin
45f8b34020 ci: don't wait for CI workflow when publishing preview releases, replace --dry-run with real publication (#1113) 2026-09-09 13:08:08 +02:00
Andrey Bragin
0dd58685b1 ci: set up preview publishing (#1084)
Today nothing reaches npm between two release-please releases, so trying
a merged
but unreleased change means building from source. This adds a
continuously
published preview channel.

Every CI-green push to `main` that is not a release merge publishes
`<package.json version, patch bumped>-preview.N` under the `preview`
dist-tag,
tags the commit `v<version>`, and dispatches the same agent registry
update a
stable release does. With `main` at 0.73.0 that gives
`0.73.1-preview.1`,
`0.73.1-preview.2`, and so on; `N` restarts at 1 whenever a release
moves the
base. `latest` is untouched.

## Approach

- **Added to `publish.yml` rather than a new workflow.** npm binds a
trusted
publisher to one repo + workflow filename, and a package gets only one.
A
  separate file would fail OIDC and cost the stable path its publisher.
- **Gated on the `CI` workflow succeeding** (`workflow_run`), so a red
commit is
never published. release-please's release merge is excluded by the head
commit's author and subject — its `releases_created` output is not
reachable
  from a different workflow run.
- **`N` is the max of the npm registry and local git tags.** npm
reserves
versions forever, even after `unpublish`, so a reused `N` is a hard
failure;
but the registry is CDN-served and lags, which the tags cover. A
registry read
  failure aborts rather than guessing.
- **Base is patch+1, not release-please's prediction**, which moves
mid-flight
  and would reset `N` under already-published previews.
- **The version is applied to the CI working tree only**, never
committed —
`package.json` and the release-please manifest stay release-please's to
own.
- **`--tag preview` is mandatory:** `npm publish` defaults to `latest`
even for a
  semver prerelease.
- **Three jobs.** `publish-npm-preview` mirrors `publish-npm` and only
publishes
(no repo write access); `publish-tag-preview` only tags, so a failed tag
is
retryable with _Re-run failed jobs_ without re-publishing an immutable
version;
`trigger-registry-update` is shared with the stable path. Both
downstream jobs
gate on a `published` output the publish step sets, not on job status —
the tag
push happens after the publish, so a red job can still mean npm has the
  version.

## Next step

The publish is `npm publish --dry-run` for now. The `workflow_run`
trigger and
the `release` environment's protected-branch policy mean this job cannot
run off
the default branch at all, so merging this is the only way to exercise
the
trigger, the gate and the version calculation — and with `published`
unset, the
rehearsal neither tags nor touches the registry.

Once one run looks right, a follow-up drops `--dry-run` and adds
`echo "published=true" >> "$GITHUB_OUTPUT"` to that step. Nothing else
changes.
2026-09-08 14:16:37 +02:00
nikita-ashihmin
996d488589 feat: align Claude modes and clear-context planning (#1004)
## Summary
- translate Claude SDK permission suggestions and tool-specific
decisions into ACP v1 presentation and editable option metadata
- align Claude session permission modes and clear-context plan handling
- split permission effects, modes, normalization, option construction,
presentation, and response handling into focused modules
- document the permission extension and add unit plus live-audit
coverage for native edge cases

## Tests
- `npm run check`
- `npm run build`
- `env -u ANTHROPIC_BASE_URL npm run test:run` (931 passed, 27 skipped)
2026-08-22 14:59:03 +04:00
nikita-ashihmin
0581b9cf39 feat: align typed session failures with AIR protocol (#992)
## Summary

- align `_meta.jetbrains.air.sessionFailure` with AIR's
transcript-record protocol: `id`, `revision`, broad `category`, explicit
`severity`, `title`, optional `details`, and `actions`
- forward Claude's actual user-facing assistant/result text as `title`;
use adapter-authored fallback text only when Claude provides no message
(transport failure, worker EOF, or an internal invariant)
- reduce categories to `connection`, `access`, `limit`, `request`,
`service`, and `unknown`
- limit suggested actions to `retry`, `login`, and `new_session`
- update one transcript record through `id + revision`, keep resolved
entries in history, and remove clear/tombstone updates
- suppress duplicate assistant failure prose only for clients that
negotiated typed failures; preserve legacy ACP behavior otherwise
- restore synthetic usage-limit failures from session history with their
exact stored Claude text
- document negotiation, delivery carriers, identity/revision behavior,
mappings, recovery, and compatibility in the extension spec

## Validation

- `npm run check`
- `npm run build`
- `npm run test:run` — 777 passed, 26 skipped
- `git diff --check`

## Tracking

[IJAI-993](https://youtrack.jetbrains.com/issue/IJAI-993)
2026-08-14 17:29:39 +04:00
Mark Tkachenko
8aaf608b4e ci: fix release flow (#971) 2026-08-07 15:16:50 +02:00
nikita-ashihmin
f8fd3ab822 fix: publish and replace Claude goals reliably (#967)
## Summary

- publish optimistic provider-neutral goal snapshots for submitted and
steered goal commands
- advertise and implement `set`, using steering for an active turn and
an ordinary prompt while idle
- suppress stale old-goal runtime updates while replacement/clear is
pending and roll back failed commands
- keep cancellation turn-scoped and orphan-account pending steers so
stale results cannot settle the next prompt
- reject unsupported pause/resume controls without host-side fallbacks

## Validation

- `npm test -- --run` (702 passed, 20 skipped)
- `npm run check`
- `npm run build`
- regression coverage for idle/active set and clear, stale runtime
updates, command failure rollback, cancellation, and stale steer
isolation
2026-08-07 17:01:18 +04:00
Mark Tkachenko
133337ffe5 ci: simplify the release flow and make it agent-friendly (#965) 2026-08-07 13:46:42 +02:00
nikita-ashihmin
8b31dea11b feat: expose provider-neutral ACP goal extension (#964)
## Summary

- advertise a provider-neutral goal extension in
`InitializeResponse._meta.goal`
- map Claude SDK `active_goal` messages to
`session_info_update._meta.goal`
- expose the shared `_session/goal` control method with the truthfully
supported `clear` action
- document the extension as a candidate shape for a future first-class
ACP goal API

## Architecture

A goal is persistent **session state**, not the lifecycle of one
`session/prompt` request. `goal.status === "active"` says that the
objective may drive more work; it does not say that a prompt is
executing now.

This keeps two independent axes:

1. **Goal state** is published asynchronously as session metadata and
may survive multiple prompt turns.
2. **Prompt activity** follows the backend's actual running/quiescent
boundaries. While a turn runs, clients use advertised steering or prompt
queueing. Once the backend is quiescent, an ordinary `session/prompt` is
valid even if the goal remains active.

The metadata is deliberately provider-neutral:

```json
{
  "_meta": {
    "goal": {
      "version": 1,
      "controlMethod": "_session/goal",
      "actions": ["clear"]
    }
  }
}
```

State is emitted as `session_info_update._meta.goal`; `null` clears it.
The common snapshot vocabulary supports `objective`, common statuses,
timestamps in Unix milliseconds, budgets/usage, iteration count, and a
continuation reason. Each implementation emits only fields it actually
knows. This shape is intentionally close to a future typed ACP
capability/update so promotion out of `_meta` would not require a
provider-specific migration.

Control actions are capability-negotiated. The shared vocabulary
reserves `set`, `pause`, `resume`, and `clear`, but an agent advertises
only the subset it can implement with correct semantics.

## Claude mapping

The Claude runtime already emits internal `active_goal` messages,
although that message is not currently included in the public
`SDKMessage` union. The adapter handles it before the exhaustive switch:

- `condition` → `objective`
- `iterations` → `iterations`
- `set_at` → `createdAt`
- `last_reason` → `lastReason`
- `value: null` → `goal: null`

Provider bookkeeping such as `tokens_at_start` is intentionally not
exposed. Goal updates do not settle, extend, or otherwise mutate ACP
prompt lifecycle. Claude's existing Stop hook remains authoritative for
whether the backend itself continues working.

Only `clear` is advertised for out-of-band control. It submits `/goal
clear` through the existing session prompt queue, preserving the
adapter's established ordering and turn ownership. Setting a goal
remains available through `/goal <condition>` but is not advertised as a
control action because it starts the long-running Stop-hook workflow.

The companion Codex implementation is agentclientprotocol/codex-acp#371.

## Verification

- `npm run test:run` — 692 passed, 20 skipped
- `npm run build`
- `npm run check`
2026-08-06 17:01:56 +04:00
Anna Zhdan
1e15f3d8b0 feat: allow configuring models through env vars (#586)
Allow providing Claude model settings via env var on startup. Providing
model settings is required when using Claude with alternative llm
provider, e.g. Bedrock as models have different IDs there
2026-04-24 15:50:28 +02:00
Bennet Bo Fenner
9e18a4e6fd Add docs for release process (#135) 2025-10-31 18:12:11 +01:00