mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-04 16:31:56 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5de5d4a2c8 | ||
|
|
0581b9cf39 | ||
|
|
11dd73b871 | ||
|
|
e4dba808ea | ||
|
|
de0d0e2b7d | ||
|
|
35aaddb3d5 | ||
|
|
1f09e9a3ca | ||
|
|
ec73cd8560 | ||
|
|
8157ee113e | ||
|
|
3df1ede89f | ||
|
|
1afa940a2c |
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.66.0"
|
||||
".": "0.68.0"
|
||||
}
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## [0.68.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.67.0...v0.68.0) (2026-08-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* align typed session failures with AIR protocol ([#992](https://github.com/agentclientprotocol/claude-agent-acp/issues/992)) ([0581b9c](https://github.com/agentclientprotocol/claude-agent-acp/commit/0581b9cf397ffd88f2830db721c2d8e3689045e4))
|
||||
|
||||
## [0.67.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.66.0...v0.67.0) (2026-08-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **deps:** Update to @anthropic-ai/claude-agent-sdk v0.3.232 ([#993](https://github.com/agentclientprotocol/claude-agent-acp/issues/993)) ([de0d0e2](https://github.com/agentclientprotocol/claude-agent-acp/commit/de0d0e2b7d185c521c3d1ac7b86e4312c919abfe))
|
||||
* expose typed session failures for AIR ([#979](https://github.com/agentclientprotocol/claude-agent-acp/issues/979)) ([8157ee1](https://github.com/agentclientprotocol/claude-agent-acp/commit/8157ee113e705750be4eb6cc787bdd12f1db84ff))
|
||||
* publish the model fallback as a warning advisory ([#990](https://github.com/agentclientprotocol/claude-agent-acp/issues/990)) ([35aaddb](https://github.com/agentclientprotocol/claude-agent-acp/commit/35aaddb3d5b14eca316582ab285af8a660150dae))
|
||||
* surface resolved model name in default model option description ([#982](https://github.com/agentclientprotocol/claude-agent-acp/issues/982)) ([ec73cd8](https://github.com/agentclientprotocol/claude-agent-acp/commit/ec73cd8560be7d5e8b9741e404d7d45e17336996))
|
||||
* surface Skill tool calls with name and kind in _meta ([#986](https://github.com/agentclientprotocol/claude-agent-acp/issues/986)) ([1f09e9a](https://github.com/agentclientprotocol/claude-agent-acp/commit/1f09e9a3cae787b0effdfae0e7205ef7fe22b9dc))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* preserve task plans across prompts ([#974](https://github.com/agentclientprotocol/claude-agent-acp/issues/974)) ([1afa940](https://github.com/agentclientprotocol/claude-agent-acp/commit/1afa940a2c8c0f4c610f4f64d30c0961642907b0))
|
||||
* show a pending title while Claude prepares a file ([#978](https://github.com/agentclientprotocol/claude-agent-acp/issues/978)) ([3df1ede](https://github.com/agentclientprotocol/claude-agent-acp/commit/3df1ede89f217312bc237124dc1eccc10c860f99))
|
||||
|
||||
## [0.66.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.65.0...v0.66.0) (2026-08-07)
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ This tool implements an ACP agent by using the official [Claude Agent SDK](https
|
||||
- Custom [Slash commands](https://docs.anthropic.com/en/docs/claude-code/slash-commands)
|
||||
- Client MCP servers
|
||||
- Session-scoped long-running goals through the provider-neutral [goal extension](docs/goal-extension.md)
|
||||
- Structured errors, recovery, and warnings through the opt-in [session failure extension](docs/session-failure-extension.md)
|
||||
|
||||
Learn more about the [Agent Client Protocol](https://agentclientprotocol.com/).
|
||||
|
||||
|
||||
208
docs/session-failure-extension.md
Normal file
208
docs/session-failure-extension.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# Session failure extension
|
||||
|
||||
This document defines the provider-neutral experimental `sessionFailure` extension implemented by
|
||||
`claude-agent-acp`. Warnings and errors are durable transcript entries shown in order beside user,
|
||||
agent, and tool messages. They are not assistant-authored text and not ephemeral chat banners.
|
||||
|
||||
## Capability negotiation
|
||||
|
||||
The extension is opt-in. A client advertises it in `initialize`:
|
||||
|
||||
```json
|
||||
{
|
||||
"clientCapabilities": {
|
||||
"_meta": {
|
||||
"jetbrains": {
|
||||
"air": {
|
||||
"version": 1,
|
||||
"capabilities": ["sessionFailure"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The adapter enables the extension only when `version` is a finite integer greater than or equal to
|
||||
`1` and `capabilities` contains `sessionFailure`. Otherwise it preserves the legacy ACP behavior.
|
||||
|
||||
## Wire record
|
||||
|
||||
The record is placed under `_meta.jetbrains.air.sessionFailure`:
|
||||
|
||||
```json
|
||||
{
|
||||
"_meta": {
|
||||
"jetbrains": {
|
||||
"air": {
|
||||
"version": 1,
|
||||
"sessionFailure": {
|
||||
"id": "prompt-uuid:error",
|
||||
"revision": 1,
|
||||
"category": "limit",
|
||||
"severity": "error",
|
||||
"title": "You've hit your individual spend limit · run /usage-credits to ask your admin for a higher limit",
|
||||
"actions": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Type | Meaning |
|
||||
| ---------- | -------: | -------------------- | ----------------------------------------------------------------------- |
|
||||
| `id` | yes | non-empty string | Stable identity of one logical incident. |
|
||||
| `revision` | yes | positive integer | Monotonically increasing version of that incident. |
|
||||
| `category` | yes | category enum | Broad machine-readable visual group. |
|
||||
| `severity` | yes | `warning` or `error` | Inline warning or error presentation. |
|
||||
| `title` | yes | string | Complete normal user-facing presentation. |
|
||||
| `details` | no | string | Long explanation used only when required text is too large for `title`. |
|
||||
| `actions` | yes | ordered string array | Recovery actions recommended by the adapter. |
|
||||
|
||||
There are no `phase`, `source`, `safeMessage`, `retryable`, `retryAfterMs`, `turnId`, retry-counter,
|
||||
or provider-code fields. Retry progress belongs in `title` when Claude supplies it. Transcript order
|
||||
and persistence time come from the receiving ACP event.
|
||||
|
||||
## Identity, revisions, and history
|
||||
|
||||
`id` identifies one occurrence, not an error type.
|
||||
|
||||
- The first record for an incident creates one transcript entry at the current stream position.
|
||||
- The same `id` with a higher `revision` updates that entry in place without moving it.
|
||||
- The same or a lower revision is idempotently ignored by the client.
|
||||
- A later independent occurrence uses a new `id`, even when its category and text are identical.
|
||||
- Consecutive updates for the same notice may reuse one id and increment its revision.
|
||||
- Different ids are never deduplicated by comparing title or category.
|
||||
|
||||
Live turn failures use `<turnId>:error`. Session-scoped incidents use an adapter-session epoch plus an
|
||||
incident sequence. Notices have their own `:notice:` namespace. Replayed usage-limit failures recover
|
||||
the persisted user-message UUID used as the live turn id, so the same occurrence keeps the same id.
|
||||
Malformed history without a preceding user message falls back to
|
||||
`<sessionId>:history-error:<messageUuid>`.
|
||||
|
||||
Resolved records remain in transcript history. Recovery removes only the adapter's internal active
|
||||
state; it does not publish a clear tombstone and does not delete or rewrite the historical entry. A
|
||||
producer may publish a higher revision only when it has a real new user-facing status to show.
|
||||
|
||||
## Delivery surfaces
|
||||
|
||||
A turn-terminal failure is attached to the successful ACP `PromptResponse` in `_meta`; the response
|
||||
uses `stopReason: end_turn`. Session-scoped, replay-restored, warning, and background incidents use a
|
||||
`session_info_update`. The schema is identical on both carriers.
|
||||
|
||||
A warning does not end a turn. An error record does not independently invent ACP lifecycle state;
|
||||
the prompt response or transport lifecycle remains authoritative. If no capable client negotiated
|
||||
the extension, existing JSON-RPC errors and transcript text remain unchanged.
|
||||
|
||||
## Categories
|
||||
|
||||
Categories are deliberately broad and drive only iconography and generic accessibility labels.
|
||||
|
||||
| Category | Covers |
|
||||
| ------------ | --------------------------------------------------------------------- |
|
||||
| `connection` | Lost transport, stopped or shutting-down worker, unavailable runtime. |
|
||||
| `access` | Authentication required, rejected credentials, denied access. |
|
||||
| `limit` | Rate, quota, token, context, turn, or configured budget limit. |
|
||||
| `request` | Invalid input, unsupported model or operation, rejected request. |
|
||||
| `service` | Provider overload, provider failure, adapter/internal failure. |
|
||||
| `unknown` | A warning or failure that does not fit another group. |
|
||||
|
||||
Claude SDK conditions map to these groups:
|
||||
|
||||
| Claude condition | Category |
|
||||
| ---------------------------------------------------------------------------------------- | ---------------------------------- |
|
||||
| `authentication_failed`, `oauth_org_not_allowed`, synthetic login message | `access` |
|
||||
| `billing_error`, `rate_limit`, `max_output_tokens`, usage/spend limit, budget/turn limit | `limit` |
|
||||
| `invalid_request`, `model_not_found` | `request` |
|
||||
| `overloaded`, `server_error`, unknown provider error, adapter internal error | `service` |
|
||||
| API retry without an HTTP response, query transport loss, worker shutdown | `connection` |
|
||||
| model fallback notice | `unknown` with `severity: warning` |
|
||||
|
||||
Unknown SDK error kinds degrade to `service`; they never become success. Category does not determine
|
||||
message text or client behavior beyond presentation.
|
||||
|
||||
## Severity
|
||||
|
||||
- `warning` means the operation may still succeed and normally has no actions while recovery is in
|
||||
progress. It does not terminate the turn. SDK `api_retry` progress reuses the active turn failure id;
|
||||
a later terminal failure updates that record with a higher revision.
|
||||
- `error` means the operation cannot continue without user action or another request.
|
||||
|
||||
Severity is always explicit.
|
||||
|
||||
## Title and details
|
||||
|
||||
`title` is the complete normal presentation: what happened, retry progress when present, and a short
|
||||
next step. The adapter must not replace a real Claude message with category-specific canned wording.
|
||||
|
||||
For Claude SDK failures the title is copied from the user-facing text Claude already emitted:
|
||||
|
||||
- top-level assistant error text, including synthetic usage-limit and login messages;
|
||||
- otherwise the terminal SDK result or error text;
|
||||
- the exact model-fallback notice for warning records.
|
||||
|
||||
Adapter-authored fallback text is allowed only when no Claude user-facing message exists, such as a
|
||||
query iterator throwing, worker-shutdown EOF, or an internal missing-result invariant. Raw thrown
|
||||
exceptions, stack traces, transport URLs, tokens, headers, environment values, and private paths are
|
||||
never used as titles.
|
||||
|
||||
`details` is reserved exclusively for required explanations too large to fit reasonably in `title`.
|
||||
If the complete message fits in `title`, `details` must be omitted. It is not a place for status text,
|
||||
retry counters, provider payloads, or diagnostics.
|
||||
|
||||
## Actions
|
||||
|
||||
Version 1 defines only:
|
||||
|
||||
| Action | Meaning |
|
||||
| ------------- | -------------------------------------------------- |
|
||||
| `retry` | Retry the operation associated with this incident. |
|
||||
| `login` | Start authentication. |
|
||||
| `new_session` | Start a fresh agent session/runtime. |
|
||||
|
||||
The adapter supplies action ordering. The client filters actions it cannot safely execute and ignores
|
||||
unknown or duplicate values. The client must not infer actions from category.
|
||||
|
||||
Current Claude policies are:
|
||||
|
||||
- authentication: `login`;
|
||||
- provider overload/error and recoverable internal error: `retry`;
|
||||
- transport loss or worker shutdown: `new_session`;
|
||||
- context or configured session-budget exhaustion: `new_session`;
|
||||
- account quota and invalid request: no action.
|
||||
|
||||
## Internal recovery
|
||||
|
||||
Recovery policy is internal adapter state and is not serialized:
|
||||
|
||||
- restored quota remains active until a real model answer;
|
||||
- authentication remains active until successful `auth_status`;
|
||||
- transport loss and worker shutdown remain active until runtime replacement;
|
||||
- other turn-scoped failures stop being active at a later confirmed attempt boundary;
|
||||
- notices are not cleared merely by generic success.
|
||||
|
||||
Recovery never removes the transcript record. Publishing another incident is not evidence that an
|
||||
older one recovered.
|
||||
|
||||
## History replay
|
||||
|
||||
`session/load` scans top-level assistant history for SDK synthetic usage-limit messages. It recognizes
|
||||
only `<synthetic>` messages beginning with SDK-owned stable prefixes, not arbitrary model prose. Every
|
||||
recognized occurrence is replayed as a typed record at its original transcript position. The latest
|
||||
matching message stays active until a later real-model answer proves recovery; older and recovered
|
||||
records remain historical typed entries.
|
||||
|
||||
For a capable client, replay suppresses duplicate assistant prose and restores typed errors using the
|
||||
exact stored Claude text as `title`. Legacy clients receive the original transcript and no typed record.
|
||||
|
||||
## Verification
|
||||
|
||||
The executable contract lives primarily in `src/tests/acp-agent.test.ts`, under `usage-limit failure
|
||||
replay`, `stop reason propagation`, and `model refusal fallback handling`.
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run check
|
||||
npm run test:run
|
||||
```
|
||||
406
package-lock.json
generated
406
package-lock.json
generated
@@ -1,31 +1,31 @@
|
||||
{
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.66.0",
|
||||
"version": "0.68.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.66.0",
|
||||
"version": "0.68.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.3.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.232",
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"claude-agent-acp": "dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.115.0",
|
||||
"@anthropic-ai/sdk": "0.117.1",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.5",
|
||||
"@types/node": "26.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.65.0",
|
||||
"@typescript-eslint/parser": "8.65.0",
|
||||
"eslint": "10.8.0",
|
||||
"@types/node": "26.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.67.0",
|
||||
"@typescript-eslint/parser": "8.67.0",
|
||||
"eslint": "10.8.1",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.9.0",
|
||||
"globals": "17.11.0",
|
||||
"prettier": "3.9.6",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
@@ -45,22 +45,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.220.tgz",
|
||||
"integrity": "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.232.tgz",
|
||||
"integrity": "sha512-8od7hJk9fZnF1/oYYiR9PvroGbZRQrpmNgKirjHNGoj5ur5YcAZLohI70XVUAUe3KvjB1msLxtkvmlAT9sqFAg==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220"
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.232",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.232"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": ">=0.93.0",
|
||||
@@ -69,9 +69,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.220.tgz",
|
||||
"integrity": "sha512-7VxlbEosK7DODiOnsjoVd0DSJzbnaPrM2jelMHI0y8zx1UnLS3WC6EFUXbvy74F2sXqEznh2tzn7EKWInaRN6Q==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.232.tgz",
|
||||
"integrity": "sha512-+/4PX+dwmQAjlOlooocwa3kClulZfMo133xQH3LYDlK7D5bzze16lwlDGPVAYGEarpvXg7G5JK8QjfWAJ2HYbg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -82,9 +82,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.220.tgz",
|
||||
"integrity": "sha512-X9RwDsSmbF6ultKZroaip+DL8WRgC64gHbrAwrRlAFSPNZV7zmJyP2ur8rW7KrxqmtuehdMMkw8+SAC/6hD2PA==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.232.tgz",
|
||||
"integrity": "sha512-EHZ1Y3aGyZ2mFZ6QLR1bM3/HiIn2cLrPjU+k3/oCIW6omJFodfzf410aWjDPSnMj7CE4d6t7MSjBWekMUbcv0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -95,9 +95,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.220.tgz",
|
||||
"integrity": "sha512-WkROPwWskqhKR9XgnmseHQ6rLi9zM9qt57IWoToIjL/eXOqDWipp7JXZ1L5ud+LrA42dunHPZfBwD/vXZ+A7LA==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.232.tgz",
|
||||
"integrity": "sha512-wW2opwA5s7gLghjU6B2ADMAtoc7bAZMevUzi4g+1PXMJ8MGcPvbnx92EDYJrVDcZmAl1+fz19XyPsaShlisTzw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -111,9 +111,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.220.tgz",
|
||||
"integrity": "sha512-OHoZOZ8Cf2TBr6oXIXPwyvUxj9jrq2w8E4poA8dMpacXszcPSPiCQCMuuOh4aWJzfeJE1+TtWxhKMVb2csXyZQ==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.232.tgz",
|
||||
"integrity": "sha512-XkLcb9UT/l42Rtw7KBApzgxUe/kwoWJ9KCPcVEnYojzIvVR+AwBCl/QReNU5+6c72w48dYBMmLebI64gQbN0tg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -127,9 +127,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.220.tgz",
|
||||
"integrity": "sha512-tkTJFnpR9VifvWX2fmkCAPkT6+8Wk/gVu8B5jsVekKZPiZoWRHmMXO30BnZn+f0TZhgYP+82PSX3S8crH1kn+w==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.232.tgz",
|
||||
"integrity": "sha512-6Px1xDiwQyLkSxwRQ34/kPA8WMXQ2rHYGkwockND7+9yMw+ShI3AfLkyi9G7JtJHObWFT6B82eSHjDS/Fy+9hQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -143,9 +143,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.220.tgz",
|
||||
"integrity": "sha512-K+FWj+LcGhC1Z7wqeWoLxm1iemcba5xKpLLFVwYm4V6HyMx3ruYd/2r2TiQtjT+JWeNFWIys0ScHiItR6vWAiA==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.232.tgz",
|
||||
"integrity": "sha512-L1x2ge9NpXMLTczmT44TKPQ88PHE+gsCakQMVEOa8rXFvfBoqvcpxM0DT8wrqYWUHhQEYR8NXxQTP9a1NVRj8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -159,9 +159,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.220.tgz",
|
||||
"integrity": "sha512-rIwgq0UwQExWl6KrHUyC4w5KwpL9l6nd95aUTx6RitexaAuEw//xtfTVLnuE4hDDQZFkzEwpdKc3nxDWoGcUbA==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.232.tgz",
|
||||
"integrity": "sha512-fDiuwL5dm1elOy7fNp4Qmdor8so4R8npj2pUGQA1G/xKfJhaK236aTWezvZid3L/O7cRdFeN9IVofOAlWLQcsw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -172,9 +172,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
|
||||
"version": "0.3.220",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.220.tgz",
|
||||
"integrity": "sha512-MuOuXhbr66HlGaWXD2f3w0k2PsvmnbkwcUZ0dAe2poFLdl72GC2dapwwOBefxm9QmoNqk9+jmv/dSKGOVWyvLw==",
|
||||
"version": "0.3.232",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.232.tgz",
|
||||
"integrity": "sha512-Hc/9uy1BI9mqKVyB1b/zoUnm3MFgtVNzQY6p5zgaq9DaIjCKDpR4a4L1aDZM4lMqUcWFMZM+u7juOhh+m39NBQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -185,9 +185,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/sdk": {
|
||||
"version": "0.115.0",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.115.0.tgz",
|
||||
"integrity": "sha512-BJrFIVyjNuU8lfDyIJTvlRYzgQg+zEl78BxE7fq8esULsGz9IRQvGtW5spq3tydmtjQb/GFdooKGdGsetpx+lQ==",
|
||||
"version": "0.117.1",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.117.1.tgz",
|
||||
"integrity": "sha512-Yn2QlXfyCiKJ5YGCOOay7ZE78ISvII2XY621WMCiflmG8IYgwx59IBwPExxki3Xk9jKUtnD/Sj6UvplWr0rZxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json-schema-to-ts": "^3.1.1",
|
||||
@@ -491,9 +491,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.142.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz",
|
||||
"integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==",
|
||||
"version": "0.144.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz",
|
||||
"integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -501,9 +501,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz",
|
||||
"integrity": "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -518,9 +518,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz",
|
||||
"integrity": "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -535,9 +535,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz",
|
||||
"integrity": "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -552,9 +552,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz",
|
||||
"integrity": "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -569,9 +569,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz",
|
||||
"integrity": "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz",
|
||||
"integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -586,9 +586,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz",
|
||||
"integrity": "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz",
|
||||
"integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -606,9 +606,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz",
|
||||
"integrity": "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz",
|
||||
"integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -626,9 +626,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz",
|
||||
"integrity": "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz",
|
||||
"integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -646,9 +646,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz",
|
||||
"integrity": "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz",
|
||||
"integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -666,9 +666,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz",
|
||||
"integrity": "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz",
|
||||
"integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -686,9 +686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz",
|
||||
"integrity": "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz",
|
||||
"integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -706,9 +706,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz",
|
||||
"integrity": "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -723,9 +723,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz",
|
||||
"integrity": "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz",
|
||||
"integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -740,9 +740,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz",
|
||||
"integrity": "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz",
|
||||
"integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -851,9 +851,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz",
|
||||
"integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==",
|
||||
"version": "26.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
|
||||
"integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -861,17 +861,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz",
|
||||
"integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz",
|
||||
"integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/type-utils": "8.65.0",
|
||||
"@typescript-eslint/utils": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"@typescript-eslint/scope-manager": "8.67.0",
|
||||
"@typescript-eslint/type-utils": "8.67.0",
|
||||
"@typescript-eslint/utils": "8.67.0",
|
||||
"@typescript-eslint/visitor-keys": "8.67.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -884,22 +884,22 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.65.0",
|
||||
"@typescript-eslint/parser": "^8.67.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz",
|
||||
"integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz",
|
||||
"integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"@typescript-eslint/scope-manager": "8.67.0",
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"@typescript-eslint/typescript-estree": "8.67.0",
|
||||
"@typescript-eslint/visitor-keys": "8.67.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -915,14 +915,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
|
||||
"integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz",
|
||||
"integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.65.0",
|
||||
"@typescript-eslint/types": "^8.65.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.67.0",
|
||||
"@typescript-eslint/types": "^8.67.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -937,14 +937,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
|
||||
"integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz",
|
||||
"integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0"
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"@typescript-eslint/visitor-keys": "8.67.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -955,9 +955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
|
||||
"integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz",
|
||||
"integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -972,15 +972,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz",
|
||||
"integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz",
|
||||
"integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0",
|
||||
"@typescript-eslint/utils": "8.65.0",
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"@typescript-eslint/typescript-estree": "8.67.0",
|
||||
"@typescript-eslint/utils": "8.67.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -997,9 +997,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
|
||||
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz",
|
||||
"integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1011,16 +1011,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
|
||||
"integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz",
|
||||
"integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.65.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"@typescript-eslint/project-service": "8.67.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.67.0",
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"@typescript-eslint/visitor-keys": "8.67.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -1039,16 +1039,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
|
||||
"integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz",
|
||||
"integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0"
|
||||
"@typescript-eslint/scope-manager": "8.67.0",
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"@typescript-eslint/typescript-estree": "8.67.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1063,13 +1063,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
|
||||
"integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
|
||||
"version": "8.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz",
|
||||
"integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/types": "8.67.0",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1344,9 +1344,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/content-type": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
|
||||
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -1658,9 +1658,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz",
|
||||
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
|
||||
"version": "10.8.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz",
|
||||
"integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -1922,9 +1922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz",
|
||||
"integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz",
|
||||
"integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -1986,9 +1986,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "8.6.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.1.tgz",
|
||||
"integrity": "sha512-0D493aP61w0TJ2A0wy27riRsO7FMQ7FK+KUHOKCSfPvYo0R55aiC6emCVgFUeShH0fq0ICPVzNcgoS+BsbXQCA==",
|
||||
"version": "8.6.2",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz",
|
||||
"integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -2237,9 +2237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "17.9.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz",
|
||||
"integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==",
|
||||
"version": "17.11.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz",
|
||||
"integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2289,9 +2289,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.0.tgz",
|
||||
"integrity": "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ==",
|
||||
"version": "4.13.2",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.2.tgz",
|
||||
"integrity": "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -2364,9 +2364,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz",
|
||||
"integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz",
|
||||
"integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -2887,9 +2887,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.17",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
|
||||
"integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",
|
||||
"version": "3.3.18",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3110,9 +3110,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.25",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz",
|
||||
"integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
|
||||
"version": "8.5.26",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
||||
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3130,7 +3130,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.16",
|
||||
"nanoid": "^3.3.17",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
@@ -3246,13 +3246,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.2.tgz",
|
||||
"integrity": "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz",
|
||||
"integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.142.0",
|
||||
"@oxc-project/types": "=0.144.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3262,20 +3262,20 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm64": "1.2.2",
|
||||
"@rolldown/binding-darwin-arm64": "1.2.2",
|
||||
"@rolldown/binding-darwin-x64": "1.2.2",
|
||||
"@rolldown/binding-freebsd-x64": "1.2.2",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.2",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.2.2",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.2.2",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.2",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.2.2",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.2.2",
|
||||
"@rolldown/binding-linux-x64-musl": "1.2.2",
|
||||
"@rolldown/binding-openharmony-arm64": "1.2.2",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.2.2",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.2.2"
|
||||
"@rolldown/binding-android-arm64": "1.2.4",
|
||||
"@rolldown/binding-darwin-arm64": "1.2.4",
|
||||
"@rolldown/binding-darwin-x64": "1.2.4",
|
||||
"@rolldown/binding-freebsd-x64": "1.2.4",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.4",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.2.4",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.2.4",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.4",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.2.4",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.2.4",
|
||||
"@rolldown/binding-linux-x64-musl": "1.2.4",
|
||||
"@rolldown/binding-openharmony-arm64": "1.2.4",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.2.4",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
@@ -3667,9 +3667,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-is/node_modules/content-type": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
|
||||
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
@@ -3739,16 +3739,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz",
|
||||
"integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
|
||||
"integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.33.0",
|
||||
"picomatch": "^4.0.5",
|
||||
"postcss": "^8.5.23",
|
||||
"rolldown": "~1.2.0",
|
||||
"postcss": "^8.5.25",
|
||||
"rolldown": "~1.2.1",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
16
package.json
16
package.json
@@ -3,7 +3,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "0.66.0",
|
||||
"version": "0.68.0",
|
||||
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
@@ -66,19 +66,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.3.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.220",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.232",
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.115.0",
|
||||
"@anthropic-ai/sdk": "0.117.1",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.5",
|
||||
"@types/node": "26.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.65.0",
|
||||
"@typescript-eslint/parser": "8.65.0",
|
||||
"eslint": "10.8.0",
|
||||
"@types/node": "26.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.67.0",
|
||||
"@typescript-eslint/parser": "8.67.0",
|
||||
"eslint": "10.8.1",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.9.0",
|
||||
"globals": "17.11.0",
|
||||
"prettier": "3.9.6",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
|
||||
693
src/acp-agent.ts
693
src/acp-agent.ts
File diff suppressed because it is too large
Load Diff
463
src/session-failure-extension.ts
Normal file
463
src/session-failure-extension.ts
Normal file
@@ -0,0 +1,463 @@
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
|
||||
import {
|
||||
getSessionMessages,
|
||||
type SDKAssistantMessageError,
|
||||
USAGE_LIMIT_ERROR_PREFIXES,
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
const JETBRAINS_META_KEY = "jetbrains";
|
||||
const AIR_META_KEY = "air";
|
||||
const AIR_EXTENSION_VERSION_KEY = "version";
|
||||
const AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
|
||||
const AIR_SESSION_FAILURE_KEY = "sessionFailure";
|
||||
const AIR_EXTENSION_VERSION = 1;
|
||||
|
||||
export function airSessionFailureCapabilityMeta() {
|
||||
return {
|
||||
[JETBRAINS_META_KEY]: {
|
||||
[AIR_META_KEY]: {
|
||||
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
|
||||
[AIR_EXTENSION_CAPABILITIES_KEY]: [AIR_SESSION_FAILURE_KEY],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type ClaudeFailureKind =
|
||||
| "advisory"
|
||||
| "auth_required"
|
||||
| "bad_request"
|
||||
| "budget_exhausted"
|
||||
| "context_exhausted"
|
||||
| "internal_error"
|
||||
| "overloaded"
|
||||
| "provider_error"
|
||||
| "quota_exhausted"
|
||||
| "rate_limited"
|
||||
| "transport_lost"
|
||||
| "worker_shutdown";
|
||||
|
||||
type AirSessionFailureCategory =
|
||||
"connection" | "access" | "limit" | "request" | "service" | "unknown";
|
||||
|
||||
type AirSessionFailureSeverity = "warning" | "error";
|
||||
type AirSessionFailureAction = "retry" | "login" | "new_session";
|
||||
|
||||
type SessionFailureRecoveryPolicy =
|
||||
"never" | "next_attempt" | "real_model_success" | "auth_status" | "runtime_rebind";
|
||||
|
||||
export type PublishedSessionFailure = {
|
||||
id: string;
|
||||
revision: number;
|
||||
kind: ClaudeFailureKind;
|
||||
category: AirSessionFailureCategory;
|
||||
turnId?: string;
|
||||
severity: AirSessionFailureSeverity;
|
||||
title: string;
|
||||
details?: string;
|
||||
actions: AirSessionFailureAction[];
|
||||
recoveryPolicy: SessionFailureRecoveryPolicy;
|
||||
};
|
||||
|
||||
type SessionFailureOptions = {
|
||||
turnId?: string;
|
||||
sessionScoped?: boolean;
|
||||
title?: string;
|
||||
details?: string;
|
||||
severity?: AirSessionFailureSeverity;
|
||||
};
|
||||
|
||||
export type SessionFailureState = {
|
||||
epoch: string;
|
||||
revisions: Map<string, number>;
|
||||
active: Map<string, PublishedSessionFailure>;
|
||||
nextIncident?: number;
|
||||
lastNotice?: { title: string; id: string };
|
||||
};
|
||||
|
||||
type Logger = {
|
||||
error: (...args: unknown[]) => void;
|
||||
};
|
||||
|
||||
export function createSessionFailureState(): SessionFailureState {
|
||||
return {
|
||||
epoch: randomUUID(),
|
||||
revisions: new Map(),
|
||||
active: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
const AIR_FAILURE_POLICY: Record<
|
||||
ClaudeFailureKind,
|
||||
{
|
||||
category: AirSessionFailureCategory;
|
||||
actions: AirSessionFailureAction[];
|
||||
fallbackTitle: string;
|
||||
}
|
||||
> = {
|
||||
advisory: {
|
||||
category: "unknown",
|
||||
actions: [],
|
||||
fallbackTitle: "Claude reported a notice.",
|
||||
},
|
||||
auth_required: {
|
||||
category: "access",
|
||||
actions: ["login"],
|
||||
fallbackTitle: "Sign in to continue using Claude.",
|
||||
},
|
||||
bad_request: {
|
||||
category: "request",
|
||||
actions: [],
|
||||
fallbackTitle: "Claude could not process this request.",
|
||||
},
|
||||
budget_exhausted: {
|
||||
category: "limit",
|
||||
actions: ["new_session"],
|
||||
fallbackTitle: "This Claude session reached its configured budget.",
|
||||
},
|
||||
context_exhausted: {
|
||||
category: "limit",
|
||||
actions: ["new_session"],
|
||||
fallbackTitle: "This Claude turn reached its configured limit.",
|
||||
},
|
||||
internal_error: {
|
||||
category: "service",
|
||||
actions: ["retry", "new_session"],
|
||||
fallbackTitle: "Claude Agent encountered an internal error.",
|
||||
},
|
||||
overloaded: {
|
||||
category: "service",
|
||||
actions: ["retry"],
|
||||
fallbackTitle: "Claude is temporarily overloaded.",
|
||||
},
|
||||
provider_error: {
|
||||
category: "service",
|
||||
actions: ["retry"],
|
||||
fallbackTitle: "The model provider reported an error.",
|
||||
},
|
||||
quota_exhausted: {
|
||||
category: "limit",
|
||||
actions: [],
|
||||
fallbackTitle: "The Claude account has no available quota.",
|
||||
},
|
||||
rate_limited: {
|
||||
category: "limit",
|
||||
actions: ["retry"],
|
||||
fallbackTitle: "Claude is temporarily rate limited.",
|
||||
},
|
||||
transport_lost: {
|
||||
category: "connection",
|
||||
actions: ["new_session"],
|
||||
fallbackTitle: "The connection to Claude was lost.",
|
||||
},
|
||||
worker_shutdown: {
|
||||
category: "connection",
|
||||
actions: ["new_session"],
|
||||
fallbackTitle: "The Claude worker is shutting down.",
|
||||
},
|
||||
};
|
||||
|
||||
export function sessionFailureMeta(failure: PublishedSessionFailure) {
|
||||
return {
|
||||
[JETBRAINS_META_KEY]: {
|
||||
[AIR_META_KEY]: {
|
||||
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
|
||||
[AIR_SESSION_FAILURE_KEY]: {
|
||||
id: failure.id,
|
||||
revision: failure.revision,
|
||||
category: failure.category,
|
||||
severity: failure.severity,
|
||||
title: failure.title,
|
||||
...(failure.details ? { details: failure.details } : {}),
|
||||
actions: failure.actions,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** `getSessionMessages` deliberately exposes only the API message and strips
|
||||
* transcript-level `error` / `isApiErrorMessage` fields. The SDK exports the
|
||||
* exact stable prefixes used by its synthetic usage-limit errors, so replay
|
||||
* can recover this one typed failure without matching arbitrary model prose. */
|
||||
export function assistantMessageText(apiMessage: unknown): string | undefined {
|
||||
if (!apiMessage || typeof apiMessage !== "object") return undefined;
|
||||
const { content } = apiMessage as { content?: unknown };
|
||||
if (typeof content === "string") return content || undefined;
|
||||
if (!Array.isArray(content)) return undefined;
|
||||
const text = content
|
||||
.map((block) =>
|
||||
block &&
|
||||
typeof block === "object" &&
|
||||
"type" in block &&
|
||||
block.type === "text" &&
|
||||
"text" in block &&
|
||||
typeof block.text === "string"
|
||||
? block.text
|
||||
: "",
|
||||
)
|
||||
.join("");
|
||||
return text || undefined;
|
||||
}
|
||||
|
||||
export function isSyntheticUsageLimitMessage(apiMessage: unknown): boolean {
|
||||
if (!apiMessage || typeof apiMessage !== "object") return false;
|
||||
const { model } = apiMessage as { model?: unknown };
|
||||
if (model !== "<synthetic>") return false;
|
||||
const text = assistantMessageText(apiMessage);
|
||||
if (!text) return false;
|
||||
return USAGE_LIMIT_ERROR_PREFIXES.some((prefix) => text.startsWith(prefix));
|
||||
}
|
||||
|
||||
export function activeUsageLimitMessage(
|
||||
messages: Awaited<ReturnType<typeof getSessionMessages>>,
|
||||
): { uuid: string; title: string } | undefined {
|
||||
let active: { uuid: string; title: string } | undefined;
|
||||
for (const message of messages) {
|
||||
if (message.type !== "assistant" || message.parent_tool_use_id !== null) continue;
|
||||
if (isSyntheticUsageLimitMessage(message.message)) {
|
||||
const title = assistantMessageText(message.message);
|
||||
if (title) active = { uuid: message.uuid, title };
|
||||
continue;
|
||||
}
|
||||
const model =
|
||||
message.message && typeof message.message === "object" && "model" in message.message
|
||||
? message.message.model
|
||||
: undefined;
|
||||
// A later real model answer proves the account recovered. Synthetic
|
||||
// local-command/interruption messages do not: they can be appended while
|
||||
// the provider remains unavailable.
|
||||
if (model !== undefined && model !== "<synthetic>") active = undefined;
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
export function supportsAirSessionFailures(capabilities?: ClientCapabilities): boolean {
|
||||
const jetbrains = capabilities?._meta?.[JETBRAINS_META_KEY] as
|
||||
Record<string, unknown> | undefined;
|
||||
const air = jetbrains?.[AIR_META_KEY] as Record<string, unknown> | undefined;
|
||||
const version = air?.[AIR_EXTENSION_VERSION_KEY];
|
||||
const advertised = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
|
||||
return (
|
||||
typeof version === "number" &&
|
||||
Number.isFinite(version) &&
|
||||
Number.isInteger(version) &&
|
||||
version >= AIR_EXTENSION_VERSION &&
|
||||
Array.isArray(advertised) &&
|
||||
advertised.some((capability) => capability === AIR_SESSION_FAILURE_KEY)
|
||||
);
|
||||
}
|
||||
|
||||
function sessionFailureRecoveryPolicy(
|
||||
kind: ClaudeFailureKind,
|
||||
turnId?: string,
|
||||
): SessionFailureRecoveryPolicy {
|
||||
switch (kind) {
|
||||
case "advisory":
|
||||
return "never";
|
||||
case "auth_required":
|
||||
return "auth_status";
|
||||
case "transport_lost":
|
||||
case "worker_shutdown":
|
||||
return "runtime_rebind";
|
||||
case "quota_exhausted":
|
||||
return "real_model_success";
|
||||
default:
|
||||
return turnId ? "next_attempt" : "real_model_success";
|
||||
}
|
||||
}
|
||||
|
||||
/** Owns the AIR failure lifecycle for both the live consumer and history replay.
|
||||
* Published revisions become active only after delivery succeeds; recovery only
|
||||
* removes internal active state because transcript records are historical. */
|
||||
export class SessionFailureController {
|
||||
private readonly sessionId: string;
|
||||
private readonly state: SessionFailureState;
|
||||
private readonly capabilities?: ClientCapabilities;
|
||||
private readonly isCurrent: () => boolean;
|
||||
private readonly sendUpdate: (notification: SessionNotification) => Promise<void>;
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(options: {
|
||||
sessionId: string;
|
||||
state: SessionFailureState;
|
||||
capabilities?: ClientCapabilities;
|
||||
isCurrent: () => boolean;
|
||||
sendUpdate: (notification: SessionNotification) => Promise<void>;
|
||||
logger: Logger;
|
||||
}) {
|
||||
this.sessionId = options.sessionId;
|
||||
this.state = options.state;
|
||||
this.capabilities = options.capabilities;
|
||||
this.isCurrent = options.isCurrent;
|
||||
this.sendUpdate = options.sendUpdate;
|
||||
this.logger = options.logger;
|
||||
}
|
||||
|
||||
private isSupported(): boolean {
|
||||
return supportsAirSessionFailures(this.capabilities);
|
||||
}
|
||||
|
||||
private async emit(failure: PublishedSessionFailure): Promise<boolean> {
|
||||
if (!this.isCurrent()) {
|
||||
this.logger.error(
|
||||
`Session ${this.sessionId}: ignored AIR session failure from a stale consumer`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await this.sendUpdate({
|
||||
sessionId: this.sessionId,
|
||||
update: {
|
||||
sessionUpdate: "session_info_update",
|
||||
_meta: sessionFailureMeta(failure),
|
||||
},
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Session ${this.sessionId}: failed to publish AIR session failure: ${error}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
recordActive(failure: PublishedSessionFailure): void {
|
||||
this.state.revisions.set(failure.id, failure.revision);
|
||||
this.state.active.set(failure.id, failure);
|
||||
this.state.lastNotice =
|
||||
failure.kind === "advisory" ? { title: failure.title, id: failure.id } : undefined;
|
||||
}
|
||||
|
||||
async clear(
|
||||
shouldClear: (failure: PublishedSessionFailure) => boolean = () => true,
|
||||
): Promise<boolean> {
|
||||
if (!this.isSupported()) return true;
|
||||
this.state.lastNotice = undefined;
|
||||
for (const failure of [...this.state.active.values()]) {
|
||||
if (!shouldClear(failure)) continue;
|
||||
this.state.active.delete(failure.id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async prepare(
|
||||
kind: ClaudeFailureKind,
|
||||
failureOptions: SessionFailureOptions = {},
|
||||
): Promise<PublishedSessionFailure | undefined> {
|
||||
if (!this.isSupported() || !this.isCurrent()) return undefined;
|
||||
const policy = AIR_FAILURE_POLICY[kind];
|
||||
const title = failureOptions.title || policy.fallbackTitle;
|
||||
if (kind === "advisory") {
|
||||
const id =
|
||||
this.state.lastNotice?.title === title
|
||||
? this.state.lastNotice.id
|
||||
: `${this.sessionId}:notice:${this.state.epoch}:${this.state.nextIncident ?? 1}`;
|
||||
if (this.state.lastNotice?.title !== title) {
|
||||
this.state.nextIncident = (this.state.nextIncident ?? 1) + 1;
|
||||
}
|
||||
return {
|
||||
id,
|
||||
revision: (this.state.revisions.get(id) ?? 0) + 1,
|
||||
kind,
|
||||
category: policy.category,
|
||||
severity: "warning",
|
||||
title,
|
||||
...(failureOptions.details ? { details: failureOptions.details } : {}),
|
||||
actions: policy.actions,
|
||||
recoveryPolicy: sessionFailureRecoveryPolicy(kind),
|
||||
};
|
||||
}
|
||||
const id =
|
||||
failureOptions.turnId && !failureOptions.sessionScoped
|
||||
? `${failureOptions.turnId}:error`
|
||||
: `${this.sessionId}:session-error:${this.state.epoch}:${this.state.nextIncident ?? 1}`;
|
||||
if (!failureOptions.turnId || failureOptions.sessionScoped) {
|
||||
this.state.nextIncident = (this.state.nextIncident ?? 1) + 1;
|
||||
}
|
||||
return {
|
||||
id,
|
||||
revision: (this.state.revisions.get(id) ?? 0) + 1,
|
||||
kind,
|
||||
category: policy.category,
|
||||
severity: failureOptions.severity ?? "error",
|
||||
title,
|
||||
...(failureOptions.details ? { details: failureOptions.details } : {}),
|
||||
actions: failureOptions.severity === "warning" ? [] : policy.actions,
|
||||
recoveryPolicy: sessionFailureRecoveryPolicy(
|
||||
kind,
|
||||
failureOptions.turnId && !failureOptions.sessionScoped ? failureOptions.turnId : undefined,
|
||||
),
|
||||
...(failureOptions.turnId && !failureOptions.sessionScoped
|
||||
? { turnId: failureOptions.turnId }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
async publish(
|
||||
kind: ClaudeFailureKind,
|
||||
failureOptions: SessionFailureOptions = {},
|
||||
): Promise<void> {
|
||||
const failure = await this.prepare(kind, failureOptions);
|
||||
if (!failure) return;
|
||||
if (await this.emit(failure)) this.recordActive(failure);
|
||||
}
|
||||
|
||||
async restore(
|
||||
id: string,
|
||||
kind: ClaudeFailureKind,
|
||||
title: string,
|
||||
active = true,
|
||||
): Promise<boolean> {
|
||||
if (!this.isSupported() || !this.isCurrent()) return false;
|
||||
const policy = AIR_FAILURE_POLICY[kind];
|
||||
const failure: PublishedSessionFailure = {
|
||||
id,
|
||||
revision: (this.state.revisions.get(id) ?? 0) + 1,
|
||||
kind,
|
||||
category: policy.category,
|
||||
severity: "error",
|
||||
title,
|
||||
actions: policy.actions,
|
||||
recoveryPolicy: sessionFailureRecoveryPolicy(kind),
|
||||
};
|
||||
if (!(await this.emit(failure))) return false;
|
||||
this.state.revisions.set(failure.id, failure.revision);
|
||||
if (active) {
|
||||
this.state.active.set(failure.id, failure);
|
||||
} else {
|
||||
this.state.active.delete(failure.id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function providerFailureCategory(
|
||||
errorKind?: SDKAssistantMessageError,
|
||||
isUsageLimit = false,
|
||||
): ClaudeFailureKind {
|
||||
if (isUsageLimit) return "quota_exhausted";
|
||||
switch (errorKind) {
|
||||
case "authentication_failed":
|
||||
case "oauth_org_not_allowed":
|
||||
return "auth_required";
|
||||
case "billing_error":
|
||||
return "quota_exhausted";
|
||||
case "rate_limit":
|
||||
return "rate_limited";
|
||||
case "overloaded":
|
||||
return "overloaded";
|
||||
case "invalid_request":
|
||||
case "model_not_found":
|
||||
return "bad_request";
|
||||
case "max_output_tokens":
|
||||
return "context_exhausted";
|
||||
case "server_error":
|
||||
case "unknown":
|
||||
case undefined:
|
||||
return "provider_error";
|
||||
default:
|
||||
return "provider_error";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -795,7 +795,7 @@ describe("createSession options merging", () => {
|
||||
return { onUserDialog: capturedOptions!.onUserDialog!, createElicitation };
|
||||
}
|
||||
|
||||
const signal = () => ({ signal: new AbortController().signal });
|
||||
const signal = () => ({ signal: new AbortController().signal, requestId: "1" });
|
||||
|
||||
it("renders the prompt as a form elicitation and maps the retry choice", async () => {
|
||||
const { onUserDialog, createElicitation } = await setupDialog();
|
||||
|
||||
3
src/tests/fixtures/usage-limit-session.jsonl
vendored
Normal file
3
src/tests/fixtures/usage-limit-session.jsonl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{"type":"user","uuid":"11111111-1111-4111-8111-111111111111","parentUuid":null,"sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:00.000Z","message":{"role":"user","content":[{"type":"text","text":"Create a short HTML page"}]}}
|
||||
{"type":"assistant","uuid":"22222222-2222-4222-8222-222222222222","parentUuid":"11111111-1111-4111-8111-111111111111","sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:01.000Z","error":"rate_limit","isApiErrorMessage":true,"message":{"id":"msg_usage_limit","type":"message","role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"You've hit your individual spend limit · run /usage-credits to ask your admin for a higher limit"}],"stop_reason":"stop_sequence","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}
|
||||
{"type":"assistant","uuid":"33333333-3333-4333-8333-333333333333","parentUuid":"22222222-2222-4222-8222-222222222222","sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:02.000Z","message":{"id":"msg_local_command","type":"message","role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."}],"stop_reason":"stop_sequence","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}
|
||||
@@ -59,7 +59,7 @@ describe("providers", () => {
|
||||
expect(response.agentCapabilities?.providers).toEqual({});
|
||||
});
|
||||
|
||||
it("lists a single optional 'main' provider, unconfigured by default", async () => {
|
||||
it("lists one mutually exclusive provider slot with native routing", async () => {
|
||||
const [agent] = await createAgentMock();
|
||||
const response = await agent.unstable_listProviders({});
|
||||
expect(response.providers).toEqual([
|
||||
@@ -67,7 +67,7 @@ describe("providers", () => {
|
||||
providerId: "main",
|
||||
supported: ["anthropic", "bedrock", "vertex"],
|
||||
required: false,
|
||||
current: null,
|
||||
current: { apiType: "anthropic", baseUrl: "https://api.anthropic.com" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -126,7 +126,7 @@ describe("providers", () => {
|
||||
).rejects.toMatchObject({ code: -32602 });
|
||||
});
|
||||
|
||||
it("disables the 'main' provider by clearing config and reporting current: null", async () => {
|
||||
it("restores native routing after the last override is disabled", async () => {
|
||||
const [agent] = await createAgentMock();
|
||||
await agent.unstable_setProvider({
|
||||
providerId: "main",
|
||||
@@ -137,7 +137,36 @@ describe("providers", () => {
|
||||
|
||||
await expect(agent.unstable_disableProvider({ providerId: "main" })).resolves.toEqual({});
|
||||
|
||||
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
|
||||
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
|
||||
apiType: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces the active api type when the single slot is set again", async () => {
|
||||
const [agent] = await createAgentMock();
|
||||
await agent.unstable_setProvider({
|
||||
providerId: "main",
|
||||
apiType: "anthropic",
|
||||
baseUrl: "https://anthropic.example",
|
||||
});
|
||||
await agent.unstable_setProvider({
|
||||
providerId: "main",
|
||||
apiType: "bedrock",
|
||||
baseUrl: "https://bedrock.example",
|
||||
});
|
||||
|
||||
const overridden = await agent.unstable_listProviders({});
|
||||
expect(overridden.providers[0].current).toEqual({
|
||||
apiType: "bedrock",
|
||||
baseUrl: "https://bedrock.example",
|
||||
});
|
||||
|
||||
await agent.unstable_disableProvider({ providerId: "main" });
|
||||
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
|
||||
apiType: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("treats disabling an unknown provider as an idempotent no-op", async () => {
|
||||
@@ -298,6 +327,9 @@ describe("providers", () => {
|
||||
|
||||
await agent.logout({});
|
||||
|
||||
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
|
||||
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
|
||||
apiType: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { describe, it, expect, afterEach, beforeEach, vi } from "vitest";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { ClientCapabilities } from "@agentclientprotocol/sdk";
|
||||
import { ImageBlockParam, ToolResultBlockParam } from "@anthropic-ai/sdk/resources";
|
||||
import {
|
||||
@@ -18,8 +21,10 @@ import {
|
||||
toolInfoFromToolUse,
|
||||
planEntries,
|
||||
applyTaskCreate,
|
||||
applyTaskList,
|
||||
applyTaskUpdate,
|
||||
parseTaskCreateOutput,
|
||||
parseTaskListOutput,
|
||||
taskStateToPlanEntries,
|
||||
TaskState,
|
||||
} from "../tools.js";
|
||||
@@ -1817,6 +1822,14 @@ describe("planEntries - undefined input regression", () => {
|
||||
{ content: "Task 2", status: "completed", priority: "medium" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses activeForm while a todo is in progress", () => {
|
||||
expect(
|
||||
planEntries({
|
||||
todos: [{ content: "Run tests", status: "in_progress", activeForm: "Running tests" }],
|
||||
}),
|
||||
).toEqual([{ content: "Running tests", status: "in_progress", priority: "medium" }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toAcpNotifications - TodoWrite with undefined input regression", () => {
|
||||
@@ -1884,6 +1897,20 @@ describe("parseTaskCreateOutput", () => {
|
||||
expect(parsed).toEqual({ task: { id: "2", subject: "Y" } });
|
||||
});
|
||||
|
||||
it("continues past unrelated JSON blocks", () => {
|
||||
const parsed = parseTaskCreateOutput([
|
||||
{ type: "text", text: JSON.stringify({ metadata: "unrelated" }) },
|
||||
{ type: "text", text: JSON.stringify({ task: { id: "2", subject: "Y" } }) },
|
||||
]);
|
||||
expect(parsed).toEqual({ task: { id: "2", subject: "Y" } });
|
||||
});
|
||||
|
||||
it("parses the human-readable TaskCreate format used in session history", () => {
|
||||
expect(parseTaskCreateOutput("Task #42 created successfully: Run tests")).toEqual({
|
||||
task: { id: "42", subject: "Run tests" },
|
||||
});
|
||||
});
|
||||
|
||||
it("returns undefined for non-JSON content", () => {
|
||||
expect(parseTaskCreateOutput("not json")).toBeUndefined();
|
||||
expect(parseTaskCreateOutput([{ type: "text", text: "not json" }])).toBeUndefined();
|
||||
@@ -1918,11 +1945,15 @@ describe("applyTaskCreate / applyTaskUpdate", () => {
|
||||
|
||||
it("updates fields by task ID and keeps insertion order in plan entries", () => {
|
||||
const state: TaskState = new Map();
|
||||
applyTaskCreate(state, { subject: "A", description: "" }, { task: { id: "1", subject: "A" } });
|
||||
applyTaskCreate(
|
||||
state,
|
||||
{ subject: "A", description: "", activeForm: "Working on A" },
|
||||
{ task: { id: "1", subject: "A" } },
|
||||
);
|
||||
applyTaskCreate(state, { subject: "B", description: "" }, { task: { id: "2", subject: "B" } });
|
||||
applyTaskUpdate(state, { taskId: "1", status: "in_progress" });
|
||||
expect(taskStateToPlanEntries(state)).toEqual([
|
||||
{ content: "A", status: "in_progress", priority: "medium" },
|
||||
{ content: "Working on A", status: "in_progress", priority: "medium" },
|
||||
{ content: "B", status: "pending", priority: "medium" },
|
||||
]);
|
||||
});
|
||||
@@ -1952,6 +1983,84 @@ describe("applyTaskCreate / applyTaskUpdate", () => {
|
||||
// update is dropped instead of synthesizing a blank placeholder.
|
||||
expect(state.has("5")).toBe(false);
|
||||
});
|
||||
|
||||
it("rebuilds task state from TaskList while preserving richer local fields", () => {
|
||||
const state: TaskState = new Map([
|
||||
[
|
||||
"1",
|
||||
{
|
||||
subject: "Old subject",
|
||||
status: "pending",
|
||||
activeForm: "Running the task",
|
||||
description: "Details",
|
||||
},
|
||||
],
|
||||
["deleted", { subject: "Stale", status: "pending" }],
|
||||
]);
|
||||
const output = parseTaskListOutput(
|
||||
JSON.stringify({
|
||||
tasks: [
|
||||
{ id: "1", subject: "Current subject", status: "in_progress", blockedBy: [] },
|
||||
{ id: "2", subject: "New task", status: "pending", blockedBy: [] },
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(output).toBeDefined();
|
||||
applyTaskList(state, output!);
|
||||
|
||||
expect([...state.entries()]).toEqual([
|
||||
[
|
||||
"1",
|
||||
{
|
||||
subject: "Current subject",
|
||||
status: "in_progress",
|
||||
activeForm: "Running the task",
|
||||
description: "Details",
|
||||
},
|
||||
],
|
||||
[
|
||||
"2",
|
||||
{
|
||||
subject: "New task",
|
||||
status: "pending",
|
||||
activeForm: undefined,
|
||||
description: undefined,
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("finds a TaskList snapshot after an unrelated JSON block", () => {
|
||||
expect(
|
||||
parseTaskListOutput([
|
||||
{ type: "text", text: JSON.stringify({ metadata: "unrelated" }) },
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
tasks: [{ id: "1", subject: "Recovered", status: "pending", blockedBy: [] }],
|
||||
}),
|
||||
},
|
||||
]),
|
||||
).toEqual({
|
||||
tasks: [{ id: "1", subject: "Recovered", status: "pending", blockedBy: [] }],
|
||||
});
|
||||
});
|
||||
|
||||
it("parses the human-readable TaskList format used in session history", () => {
|
||||
expect(
|
||||
parseTaskListOutput(
|
||||
"#1 [in_progress] Run tests\n#2 [pending] Write release notes [blocked by #1]",
|
||||
),
|
||||
).toEqual({
|
||||
tasks: [
|
||||
{ id: "1", subject: "Run tests", status: "in_progress", blockedBy: [] },
|
||||
{ id: "2", subject: "Write release notes", status: "pending", blockedBy: ["1"] },
|
||||
],
|
||||
});
|
||||
|
||||
expect(parseTaskListOutput("No tasks found")).toEqual({ tasks: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("toAcpNotifications - Task* tools", () => {
|
||||
@@ -2112,10 +2221,9 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("suppresses TaskList and TaskGet tool_result without touching task state", () => {
|
||||
it("uses the structured TaskList result as an authoritative plan snapshot", () => {
|
||||
const toolUseCache: ToolUseCache = {
|
||||
"list-1": { type: "tool_use", id: "list-1", name: "TaskList", input: {} },
|
||||
"get-1": { type: "tool_use", id: "get-1", name: "TaskGet", input: { taskId: "1" } },
|
||||
};
|
||||
const taskState: TaskState = new Map([
|
||||
["1", { subject: "Existing", status: "in_progress" as const }],
|
||||
@@ -2123,8 +2231,93 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
{ type: "tool_result", tool_use_id: "list-1", content: "...", is_error: false },
|
||||
{ type: "tool_result", tool_use_id: "get-1", content: "...", is_error: false },
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "list-1",
|
||||
content: "#2 [pending] Recovered",
|
||||
is_error: false,
|
||||
},
|
||||
] as any,
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{
|
||||
taskState,
|
||||
toolUseResult: {
|
||||
tasks: [{ id: "2", subject: "Recovered", status: "pending", blockedBy: [] }],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(notifications).toHaveLength(1);
|
||||
expect(notifications[0].update).toMatchObject({
|
||||
sessionUpdate: "plan",
|
||||
entries: [{ content: "Recovered", status: "pending", priority: "medium" }],
|
||||
});
|
||||
expect([...taskState.keys()]).toEqual(["2"]);
|
||||
});
|
||||
|
||||
it("does not apply a logically failed TaskUpdate", () => {
|
||||
const toolUseCache: ToolUseCache = {
|
||||
"update-1": {
|
||||
type: "tool_use",
|
||||
id: "update-1",
|
||||
name: "TaskUpdate",
|
||||
input: { taskId: "1", status: "completed" },
|
||||
},
|
||||
};
|
||||
const taskState: TaskState = new Map([["1", { subject: "Existing", status: "pending" }]]);
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "update-1",
|
||||
content: "Task #1 not found",
|
||||
is_error: false,
|
||||
},
|
||||
] as any,
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{
|
||||
taskState,
|
||||
toolUseResult: {
|
||||
success: false,
|
||||
taskId: "1",
|
||||
updatedFields: [],
|
||||
error: "Task not found",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(notifications).toHaveLength(0);
|
||||
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "pending" });
|
||||
});
|
||||
|
||||
it("does not apply a replayed TaskUpdate failure without structured output", () => {
|
||||
const toolUseCache: ToolUseCache = {
|
||||
"update-1": {
|
||||
type: "tool_use",
|
||||
id: "update-1",
|
||||
name: "TaskUpdate",
|
||||
input: { taskId: "1", status: "completed" },
|
||||
},
|
||||
};
|
||||
const taskState: TaskState = new Map([["1", { subject: "Existing", status: "pending" }]]);
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "update-1",
|
||||
content: "Task #1 not found",
|
||||
is_error: false,
|
||||
},
|
||||
] as any,
|
||||
"user",
|
||||
"test-session",
|
||||
@@ -2135,7 +2328,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
);
|
||||
|
||||
expect(notifications).toHaveLength(0);
|
||||
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "in_progress" });
|
||||
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "pending" });
|
||||
});
|
||||
|
||||
it("does not apply TaskCreate/TaskUpdate when the tool_result reports an error", () => {
|
||||
@@ -2984,3 +3177,147 @@ describe("structured tool_use_result rendering (Read/Bash/WebSearch)", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Skill tool rendering", () => {
|
||||
const mockLogger: Logger = { log: () => {}, error: () => {} };
|
||||
|
||||
describe("toolInfoFromToolUse", () => {
|
||||
it("sets title to 'Load skill: <name>' and returns empty content", () => {
|
||||
const info = toolInfoFromToolUse(
|
||||
{ name: "Skill", id: "toolu_1", input: { skill: "commits" } },
|
||||
false,
|
||||
);
|
||||
expect(info.title).toBe("Load skill: commits");
|
||||
expect(info.kind).toBe("other");
|
||||
expect(info.content).toEqual([]);
|
||||
});
|
||||
|
||||
it("falls back to 'Load skill' when skill name is absent", () => {
|
||||
const info = toolInfoFromToolUse({ name: "Skill", id: "toolu_2", input: {} }, false);
|
||||
expect(info.title).toBe("Load skill");
|
||||
expect(info.content).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not throw when input is undefined", () => {
|
||||
const info = toolInfoFromToolUse({ name: "Skill", id: "toolu_3", input: undefined }, false);
|
||||
expect(info.title).toBe("Load skill");
|
||||
expect(info.content).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toolUpdateFromToolResult", () => {
|
||||
it("suppresses the raw 'Launching skill' result text", () => {
|
||||
const toolUse = {
|
||||
type: "tool_use",
|
||||
id: "toolu_4",
|
||||
name: "Skill",
|
||||
input: { skill: "commits" },
|
||||
};
|
||||
const toolResult = {
|
||||
type: "tool_result" as const,
|
||||
tool_use_id: "toolu_4",
|
||||
content: "Launching skill: commits",
|
||||
is_error: false,
|
||||
};
|
||||
const update = toolUpdateFromToolResult(toolResult, toolUse, false);
|
||||
expect(update).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("_meta.claudeCode.skill in tool_call notification", () => {
|
||||
it("includes skill name in _meta.claudeCode when Skill tool is invoked", () => {
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
{ type: "tool_use", id: "toolu_5", name: "Skill", input: { skill: "commits", args: "" } },
|
||||
] as any,
|
||||
"assistant",
|
||||
"test-session",
|
||||
{},
|
||||
{} as AcpClient,
|
||||
mockLogger,
|
||||
);
|
||||
expect(notifications[0]?.update).toMatchObject({
|
||||
sessionUpdate: "tool_call",
|
||||
_meta: { claudeCode: { toolName: "Skill", skill: "commits" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("omits skill from _meta.claudeCode when skill name is missing", () => {
|
||||
const notifications = toAcpNotifications(
|
||||
[{ type: "tool_use", id: "toolu_6", name: "Skill", input: {} }] as any,
|
||||
"assistant",
|
||||
"test-session",
|
||||
{},
|
||||
{} as AcpClient,
|
||||
mockLogger,
|
||||
);
|
||||
const meta = (notifications[0]?.update as any)?._meta?.claudeCode;
|
||||
expect(meta).toBeDefined();
|
||||
expect(meta.skill).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("_meta.claudeCode.skillPath", () => {
|
||||
const skillMeta = (skill: string, cwd?: string) =>
|
||||
(
|
||||
toAcpNotifications(
|
||||
[{ type: "tool_use", id: "toolu_skill_path", name: "Skill", input: { skill } }] as any,
|
||||
"assistant",
|
||||
"test-session",
|
||||
{},
|
||||
{} as AcpClient,
|
||||
mockLogger,
|
||||
cwd ? { cwd } : undefined,
|
||||
)[0]?.update as any
|
||||
)?._meta?.claudeCode;
|
||||
|
||||
let root: string;
|
||||
|
||||
beforeEach(() => {
|
||||
root = mkdtempSync(path.join(tmpdir(), "acp-skill-path-"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const writeSkill = (relativeDir: string) => {
|
||||
const dir = path.join(root, relativeDir);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
const file = path.join(dir, "SKILL.md");
|
||||
writeFileSync(file, "# skill\n");
|
||||
return file;
|
||||
};
|
||||
|
||||
it("resolves a project-level .claude/skills skill", () => {
|
||||
const file = writeSkill(".claude/skills/commits");
|
||||
expect(skillMeta("commits", root).skillPath).toBe(file);
|
||||
});
|
||||
|
||||
it("resolves a project-level .agents/skills skill", () => {
|
||||
const file = writeSkill(".agents/skills/commits");
|
||||
expect(skillMeta("commits", root).skillPath).toBe(file);
|
||||
});
|
||||
|
||||
it("resolves a directory-scoped skill spelled prefix:name", () => {
|
||||
const file = writeSkill("apps/web/.claude/skills/deploy");
|
||||
expect(skillMeta("apps/web:deploy", root).skillPath).toBe(file);
|
||||
});
|
||||
|
||||
it("resolves a plugin skill spelled plugin:name", () => {
|
||||
const file = writeSkill(".claude/plugins/reviewer/skills/audit");
|
||||
expect(skillMeta("reviewer:audit", root).skillPath).toBe(file);
|
||||
});
|
||||
|
||||
it("omits skillPath when no known layout holds the skill", () => {
|
||||
const meta = skillMeta("nonexistent", root);
|
||||
expect(meta.skill).toBe("nonexistent");
|
||||
expect(meta.skillPath).toBeUndefined();
|
||||
});
|
||||
|
||||
it("omits skillPath when the session has no cwd", () => {
|
||||
writeSkill(".claude/skills/commits");
|
||||
expect(skillMeta("commits").skillPath).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
179
src/tools.ts
179
src/tools.ts
@@ -21,7 +21,9 @@ import {
|
||||
ReportFindingsInput,
|
||||
TaskCreateInput,
|
||||
TaskCreateOutput,
|
||||
TaskListOutput,
|
||||
TaskUpdateInput,
|
||||
TaskUpdateOutput,
|
||||
TodoWriteInput,
|
||||
WebFetchInput,
|
||||
WebSearchInput,
|
||||
@@ -216,7 +218,7 @@ export function toolInfoFromToolUse(
|
||||
}
|
||||
const displayPath = input?.file_path ? toDisplayPath(input.file_path, cwd) : undefined;
|
||||
return {
|
||||
title: displayPath ? `Write ${displayPath}` : "Write",
|
||||
title: displayPath ? `Write ${displayPath}` : "Preparing file…",
|
||||
kind: "edit",
|
||||
content,
|
||||
locations: input?.file_path ? [{ path: input.file_path }] : [],
|
||||
@@ -439,6 +441,16 @@ export function toolInfoFromToolUse(
|
||||
};
|
||||
}
|
||||
|
||||
case "Skill": {
|
||||
const input = toolUse.input as { skill?: string; args?: string } | undefined;
|
||||
const skillName = input?.skill;
|
||||
return {
|
||||
title: skillName ? `Load skill: ${skillName}` : "Load skill",
|
||||
kind: "other",
|
||||
content: [],
|
||||
};
|
||||
}
|
||||
|
||||
case "AskUserQuestion": {
|
||||
const input = toolUse.input as Partial<AskUserQuestionInput> | undefined;
|
||||
const questions = Array.isArray(input?.questions) ? input.questions : [];
|
||||
@@ -861,6 +873,10 @@ export function toolUpdateFromToolResult(
|
||||
);
|
||||
}
|
||||
|
||||
case "Skill": {
|
||||
return {};
|
||||
}
|
||||
|
||||
case "Edit": // Edit is handled in hooks
|
||||
case "Write": {
|
||||
return {};
|
||||
@@ -1030,7 +1046,7 @@ export type ClaudePlanEntry = {
|
||||
|
||||
export function planEntries(input: { todos: ClaudePlanEntry[] } | undefined): PlanEntry[] {
|
||||
return (input?.todos ?? []).map((todo) => ({
|
||||
content: todo.content,
|
||||
content: todo.status === "in_progress" && todo.activeForm ? todo.activeForm : todo.content,
|
||||
status: todo.status,
|
||||
priority: "medium",
|
||||
}));
|
||||
@@ -1052,31 +1068,29 @@ export type TaskEntry = {
|
||||
export type TaskState = Map<string, TaskEntry>;
|
||||
|
||||
/**
|
||||
* Best-effort parse of a TaskCreate tool_result content into the structured
|
||||
* TaskCreateOutput. The SDK delivers tool outputs either as a string or as
|
||||
* an array of TextBlockParam-like blocks containing JSON text; try both.
|
||||
* Best-effort parse of a structured Task* tool_result. The SDK delivers tool
|
||||
* outputs either as a string or as an array of TextBlockParam-like blocks
|
||||
* containing JSON text; try both.
|
||||
*/
|
||||
export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | undefined {
|
||||
const tryParse = (text: string): TaskCreateOutput | undefined => {
|
||||
function parseJsonToolOutput<T>(
|
||||
content: unknown,
|
||||
isExpectedOutput: (value: unknown) => value is T,
|
||||
): T | undefined {
|
||||
const tryParse = (text: string): T | undefined => {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (
|
||||
parsed &&
|
||||
typeof parsed === "object" &&
|
||||
parsed.task &&
|
||||
typeof parsed.task.id === "string"
|
||||
) {
|
||||
return parsed as TaskCreateOutput;
|
||||
}
|
||||
const parsed: unknown = JSON.parse(text);
|
||||
return isExpectedOutput(parsed) ? parsed : undefined;
|
||||
} catch {
|
||||
// ignore
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
if (typeof content === "string") {
|
||||
return tryParse(content);
|
||||
}
|
||||
if (content && typeof content === "object" && !Array.isArray(content)) {
|
||||
return isExpectedOutput(content) ? content : undefined;
|
||||
}
|
||||
if (Array.isArray(content)) {
|
||||
for (const block of content) {
|
||||
if (block && typeof block === "object" && "type" in block && block.type === "text") {
|
||||
@@ -1091,6 +1105,119 @@ export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | unde
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function toolOutputTexts(content: unknown): string[] {
|
||||
if (typeof content === "string") return [content];
|
||||
if (!Array.isArray(content)) return [];
|
||||
return content.flatMap((block) =>
|
||||
block &&
|
||||
typeof block === "object" &&
|
||||
"type" in block &&
|
||||
block.type === "text" &&
|
||||
"text" in block &&
|
||||
typeof block.text === "string"
|
||||
? [block.text]
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | undefined {
|
||||
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskCreateOutput =>
|
||||
Boolean(
|
||||
parsed &&
|
||||
typeof parsed === "object" &&
|
||||
"task" in parsed &&
|
||||
parsed.task &&
|
||||
typeof parsed.task === "object" &&
|
||||
"id" in parsed.task &&
|
||||
typeof parsed.task.id === "string",
|
||||
),
|
||||
);
|
||||
if (structured) return structured;
|
||||
|
||||
for (const text of toolOutputTexts(content)) {
|
||||
const match = /^Task #(\S+) created successfully: (.+)$/.exec(text.trim());
|
||||
if (match) return { task: { id: match[1], subject: match[2] } };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function parseTaskListOutput(content: unknown): TaskListOutput | undefined {
|
||||
const validStatuses = new Set(["pending", "in_progress", "completed"]);
|
||||
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskListOutput =>
|
||||
Boolean(
|
||||
parsed &&
|
||||
typeof parsed === "object" &&
|
||||
"tasks" in parsed &&
|
||||
Array.isArray(parsed.tasks) &&
|
||||
parsed.tasks.every(
|
||||
(task) =>
|
||||
task &&
|
||||
typeof task === "object" &&
|
||||
typeof task.id === "string" &&
|
||||
typeof task.subject === "string" &&
|
||||
typeof task.status === "string" &&
|
||||
validStatuses.has(task.status),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (structured) return structured;
|
||||
|
||||
for (const text of toolOutputTexts(content)) {
|
||||
if (text.trim() === "No tasks found") return { tasks: [] };
|
||||
|
||||
const tasks: TaskListOutput["tasks"] = [];
|
||||
const lines = text.trim().split("\n");
|
||||
for (const line of lines) {
|
||||
const match =
|
||||
/^#(\S+) \[(pending|in_progress|completed)\] (.+?)(?: \(([^()]*)\))?(?: \[blocked by ((?:#[^,\]]+(?:, )?)+)\])?$/.exec(
|
||||
line,
|
||||
);
|
||||
if (!match) {
|
||||
tasks.length = 0;
|
||||
break;
|
||||
}
|
||||
tasks.push({
|
||||
id: match[1],
|
||||
subject: match[3],
|
||||
status: match[2] as TaskListOutput["tasks"][number]["status"],
|
||||
...(match[4] ? { owner: match[4] } : {}),
|
||||
blockedBy: match[5] ? match[5].split(", ").map((id) => id.slice(1)) : [],
|
||||
});
|
||||
}
|
||||
if (tasks.length > 0) return { tasks };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function parseTaskUpdateOutput(
|
||||
content: unknown,
|
||||
expectedTaskId?: string,
|
||||
): TaskUpdateOutput | undefined {
|
||||
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskUpdateOutput =>
|
||||
Boolean(
|
||||
parsed &&
|
||||
typeof parsed === "object" &&
|
||||
"success" in parsed &&
|
||||
typeof parsed.success === "boolean" &&
|
||||
"taskId" in parsed &&
|
||||
typeof parsed.taskId === "string" &&
|
||||
"updatedFields" in parsed &&
|
||||
Array.isArray(parsed.updatedFields) &&
|
||||
parsed.updatedFields.every((field) => typeof field === "string"),
|
||||
),
|
||||
);
|
||||
if (structured) return structured;
|
||||
|
||||
for (const text of toolOutputTexts(content)) {
|
||||
const notFound = /^Task #(\S+) not found$/.exec(text.trim());
|
||||
const taskId = notFound?.[1] ?? expectedTaskId;
|
||||
if (taskId && (notFound || text.trim() === "Failed to delete task")) {
|
||||
return { success: false, taskId, updatedFields: [], error: text.trim() };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function applyTaskCreate(
|
||||
state: TaskState,
|
||||
input: TaskCreateInput | undefined,
|
||||
@@ -1125,9 +1252,23 @@ export function applyTaskUpdate(state: TaskState, input: TaskUpdateInput | undef
|
||||
});
|
||||
}
|
||||
|
||||
export function applyTaskList(state: TaskState, output: TaskListOutput): void {
|
||||
const previous = new Map(state);
|
||||
state.clear();
|
||||
for (const task of output.tasks) {
|
||||
const existing = previous.get(task.id);
|
||||
state.set(task.id, {
|
||||
subject: task.subject,
|
||||
status: task.status,
|
||||
activeForm: existing?.activeForm,
|
||||
description: existing?.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function taskStateToPlanEntries(state: TaskState): PlanEntry[] {
|
||||
return Array.from(state.values()).map((task) => ({
|
||||
content: task.subject,
|
||||
content: task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject,
|
||||
status: task.status,
|
||||
priority: "medium",
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user