@gotgenes/pi-anthropic-auth 0.4.6 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +185 -0
- package/README.md +5 -0
- package/package.json +32 -24
- package/src/anthropic-oauth.ts +38 -0
- package/{dist/constants.js → src/constants.ts} +42 -22
- package/src/debug.ts +38 -0
- package/src/index.ts +36 -0
- package/src/oauth-transport.ts +97 -0
- package/src/request-shaping.ts +274 -0
- package/src/system-prompt-shaping.ts +205 -0
- package/dist/anthropic-oauth.d.ts +0 -8
- package/dist/anthropic-oauth.js +0 -24
- package/dist/constants.d.ts +0 -78
- package/dist/debug.d.ts +0 -3
- package/dist/debug.js +0 -32
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -10
- package/dist/request-shaping.d.ts +0 -1
- package/dist/request-shaping.js +0 -189
- package/dist/system-prompt-shaping.d.ts +0 -53
- package/dist/system-prompt-shaping.js +0 -146
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.5.1](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.5.0...v0.5.1) (2026-06-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* shape Anthropic OAuth requests at the transport layer ([a8c2015](https://github.com/gotgenes/pi-anthropic-auth/commit/a8c2015c293c6b60d1110f73620dd3825057c6e6))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* document the OAuth transport-wrapper architecture ([6ce28a4](https://github.com/gotgenes/pi-anthropic-auth/commit/6ce28a484189a3f1500bc0eb3e18c87fcb1e5c6f))
|
|
14
|
+
* note prek rumdl-fmt pre-commit hook in markdown-conventions skill ([5838075](https://github.com/gotgenes/pi-anthropic-auth/commit/58380753961e3fa1bb0b10c1cd72ca936b926e48))
|
|
15
|
+
* port reusable skills from pi-packages ([1e606a9](https://github.com/gotgenes/pi-anthropic-auth/commit/1e606a97d2dfd509ad4b8c5b29f9b2eea2771afc))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Miscellaneous Chores
|
|
19
|
+
|
|
20
|
+
* bump dependencies and establish Pi 0.79.1 base ([62696c9](https://github.com/gotgenes/pi-anthropic-auth/commit/62696c97934a3bdbd67091fb30ee2af7d8959f8b))
|
|
21
|
+
|
|
22
|
+
## [0.5.0](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.6...v0.5.0) (2026-05-24)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* add ESLint with type-aware rules matching pi-packages ([645e145](https://github.com/gotgenes/pi-anthropic-auth/commit/645e14517681c3ff37e798a5d8c8db94374b3fa1))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
* clarify that non-OAuth requests pass through untouched ([1706c1f](https://github.com/gotgenes/pi-anthropic-auth/commit/1706c1f188af2dee2ec1229596d169e61ff2f0a4))
|
|
33
|
+
* update AGENTS.md for new build and import conventions ([01c923a](https://github.com/gotgenes/pi-anthropic-auth/commit/01c923a41405641485ec95f454d4962058a8d72f))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Miscellaneous Chores
|
|
37
|
+
|
|
38
|
+
* bump CLAUDE_CODE_VERSION to 2.1.150 ([76081d2](https://github.com/gotgenes/pi-anthropic-auth/commit/76081d2f57b2809ef7eba49bc7703b55689129f2))
|
|
39
|
+
* bump pi packages to 0.75.5 and update tooling deps ([23e360d](https://github.com/gotgenes/pi-anthropic-auth/commit/23e360d408640e12b65ff92bd9ac8a893444bcec))
|
|
40
|
+
* update linting toolchain to match pi-packages ([40ddc5a](https://github.com/gotgenes/pi-anthropic-auth/commit/40ddc5aa6f9f18b69a6340c0bcf1a92c8614cbd8))
|
|
41
|
+
* update pnpm ([629f2b7](https://github.com/gotgenes/pi-anthropic-auth/commit/629f2b78c8730a2a64f1941d0d1607c6bd6ef7c3))
|
|
42
|
+
|
|
43
|
+
## [0.4.6](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.5...v0.4.6) (2026-05-08)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Bug Fixes
|
|
47
|
+
|
|
48
|
+
* bump CLAUDE_CODE_VERSION to 2.1.119 ([119e8e5](https://github.com/gotgenes/pi-anthropic-auth/commit/119e8e5b6c725dd20907747d52852b15bbeafc83))
|
|
49
|
+
|
|
50
|
+
## [0.4.5](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.4...v0.4.5) (2026-05-08)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Bug Fixes
|
|
54
|
+
|
|
55
|
+
* pin pnpm to 11.0.6 via packageManager field ([0fa7cea](https://github.com/gotgenes/pi-anthropic-auth/commit/0fa7cea1e1622db75448c4d8ccebc730daf7b021))
|
|
56
|
+
|
|
57
|
+
## [0.4.4](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.3...v0.4.4) (2026-05-08)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Miscellaneous Chores
|
|
61
|
+
|
|
62
|
+
* approve build of "@google/genai" ([30d595e](https://github.com/gotgenes/pi-anthropic-auth/commit/30d595e0168a038aae45d8c4e338a96b5c168bf6))
|
|
63
|
+
* update dev dependencies to latest ([c5c6d69](https://github.com/gotgenes/pi-anthropic-auth/commit/c5c6d6921b108b63f70aa2280852b9ede3926cc2))
|
|
64
|
+
|
|
65
|
+
## [0.4.3](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.2...v0.4.3) (2026-05-08)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Miscellaneous Chores
|
|
69
|
+
|
|
70
|
+
* migrate from [@mariozechner](https://github.com/mariozechner) to [@earendil-works](https://github.com/earendil-works) pi packages ([e36ab59](https://github.com/gotgenes/pi-anthropic-auth/commit/e36ab598733a2ff6501d007363a4398fff15140c))
|
|
71
|
+
|
|
72
|
+
## [0.4.2](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.1...v0.4.2) (2026-05-02)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### Documentation
|
|
76
|
+
|
|
77
|
+
* update badge to pnpm 10 ([77b0ae8](https://github.com/gotgenes/pi-anthropic-auth/commit/77b0ae8964ef857d4094df09bf099dc27dc862a4))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Miscellaneous Chores
|
|
81
|
+
|
|
82
|
+
* bump CLAUDE_CODE_VERSION to 2.1.112 ([be65bc8](https://github.com/gotgenes/pi-anthropic-auth/commit/be65bc85822bc1752bc0d75bd2fd44cca7da8342))
|
|
83
|
+
|
|
84
|
+
## [0.4.1](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.4.0...v0.4.1) (2026-04-27)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
### Bug Fixes
|
|
88
|
+
|
|
89
|
+
* refine OAuth prompt shaping and add debug logging ([7c2b2af](https://github.com/gotgenes/pi-anthropic-auth/commit/7c2b2afb351a18fbb3569b8552d59a9d321ef7f8))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
### Documentation
|
|
93
|
+
|
|
94
|
+
* add comparison notes for similar Anthropic OAuth projects ([b6ab2de](https://github.com/gotgenes/pi-anthropic-auth/commit/b6ab2defa7f7b220d159692ed39598a84d8a8197))
|
|
95
|
+
* add final pull step after release merge ([9f33ca4](https://github.com/gotgenes/pi-anthropic-auth/commit/9f33ca4d06813a1a35a0821e77b7f951e775edb3))
|
|
96
|
+
* add git workflow and Haiku repro guidance ([a2a193a](https://github.com/gotgenes/pi-anthropic-auth/commit/a2a193a03ded63029009a57de197759ccf00d816))
|
|
97
|
+
* capture repo workflow and model alias gotchas ([99aa8f6](https://github.com/gotgenes/pi-anthropic-auth/commit/99aa8f6e80b40e28c5c8ee67d87aef2205459b75))
|
|
98
|
+
* clarify release-please end-of-workflow ([c1f83a6](https://github.com/gotgenes/pi-anthropic-auth/commit/c1f83a63691f4403e9c8333c85b87e6d9f07a68b))
|
|
99
|
+
* refine release workflow steps ([17480a1](https://github.com/gotgenes/pi-anthropic-auth/commit/17480a1d8605d2273e21ddc5754b546d87c0ab73))
|
|
100
|
+
* require explicit PR number for release merge ([59a8665](https://github.com/gotgenes/pi-anthropic-auth/commit/59a86655d9e02fe3ab45b0484975fcbf4211ab1e))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### Miscellaneous Chores
|
|
104
|
+
|
|
105
|
+
* remove pi-ask-user package ([0e51ce0](https://github.com/gotgenes/pi-anthropic-auth/commit/0e51ce05f87019bd063d99dd05e011876fe8bd68))
|
|
106
|
+
|
|
107
|
+
## [0.4.0](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.3.0...v0.4.0) (2026-04-25)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Features
|
|
111
|
+
|
|
112
|
+
* adopt anchor-driven sanitizer for system prompt shaping (issue [#10](https://github.com/gotgenes/pi-anthropic-auth/issues/10)) ([f740a35](https://github.com/gotgenes/pi-anthropic-auth/commit/f740a3524e255b9153227e1f67eef342a5b57196))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
### Bug Fixes
|
|
116
|
+
|
|
117
|
+
* preserve content appended after Pi default preamble for OAuth shaping ([52b2ca8](https://github.com/gotgenes/pi-anthropic-auth/commit/52b2ca83903d84c63471841c6ff90f3971c8643d)), closes [#9](https://github.com/gotgenes/pi-anthropic-auth/issues/9)
|
|
118
|
+
* remove body-level anthropic-beta injection that caused 400 rejection ([4ab21db](https://github.com/gotgenes/pi-anthropic-auth/commit/4ab21dbafd30f2e1d8190370cfa04a7417e5f2c5))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### Documentation
|
|
122
|
+
|
|
123
|
+
* refresh AGENTS.md testing guidance for vitest harness ([ed1b344](https://github.com/gotgenes/pi-anthropic-auth/commit/ed1b3444e001dbff74ee5197d1c37b4fe21f7602))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
### Miscellaneous Chores
|
|
127
|
+
|
|
128
|
+
* switch test runner from node:test to vitest ([e0200f9](https://github.com/gotgenes/pi-anthropic-auth/commit/e0200f9308b5ee2cb31a085ac1aa256ccc3f95dd))
|
|
129
|
+
|
|
130
|
+
## [0.3.0](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.2.1...v0.3.0) (2026-04-24)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
### Features
|
|
134
|
+
|
|
135
|
+
* add ask_user tool via pi-ask-user plugin ([8cb181a](https://github.com/gotgenes/pi-anthropic-auth/commit/8cb181a6824074f9fe3ad1f0e16bf608f3a99f68))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
### Bug Fixes
|
|
139
|
+
|
|
140
|
+
* always inject required OAuth betas regardless of upstream anthropic-beta header ([a048535](https://github.com/gotgenes/pi-anthropic-auth/commit/a048535098d5ed8cfd189ac9c6e146e044b135b1))
|
|
141
|
+
* bump CLAUDE_CODE_VERSION to 2.1.108 ([641c406](https://github.com/gotgenes/pi-anthropic-auth/commit/641c40628f9c665790da9f3ebbab492d133b1f78))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
### Documentation
|
|
145
|
+
|
|
146
|
+
* add Conventional Commits guidelines to AGENTS.md ([72dc990](https://github.com/gotgenes/pi-anthropic-auth/commit/72dc990e154a708600902ed95a11c5d6e8a45c4d))
|
|
147
|
+
* simplify README to focus on what, not how ([2becbd8](https://github.com/gotgenes/pi-anthropic-auth/commit/2becbd8b5c23a06ba2b52e39c5513bea4a311d6f))
|
|
148
|
+
|
|
149
|
+
## [0.2.1](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.2.0...v0.2.1) (2026-04-22)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
### Documentation
|
|
153
|
+
|
|
154
|
+
* add badges and acknowledgments to README ([e963e64](https://github.com/gotgenes/pi-anthropic-auth/commit/e963e642414e73e1724d7ee3e28bc00cc9a73e5b))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
### Miscellaneous Chores
|
|
158
|
+
|
|
159
|
+
* upgrade TypeScript 5 -> 6.0.3 ([bd227e0](https://github.com/gotgenes/pi-anthropic-auth/commit/bd227e0bfd8165d7dd68a517fd405f1bb1ecb33a))
|
|
160
|
+
|
|
161
|
+
## [0.2.0](https://github.com/gotgenes/pi-anthropic-auth/compare/v0.1.0...v0.2.0) (2026-04-22)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
### Features
|
|
165
|
+
|
|
166
|
+
* add minimal anthropic oauth compatibility override ([092da47](https://github.com/gotgenes/pi-anthropic-auth/commit/092da47c6390dff82cbf98b0e42acc473b69e41a))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
### Bug Fixes
|
|
170
|
+
|
|
171
|
+
* avoid extra cached anthropic billing block ([a0e1c7e](https://github.com/gotgenes/pi-anthropic-auth/commit/a0e1c7eb70676e703bf5d7d4945ebf5211bd8a1e))
|
|
172
|
+
* move system prompt shaping into before_provider_request ([23e68e5](https://github.com/gotgenes/pi-anthropic-auth/commit/23e68e5c5e18a32846c17f9098e363eef91a6cbe))
|
|
173
|
+
* stabilize anthropic oauth prompt and payload shaping ([52819a9](https://github.com/gotgenes/pi-anthropic-auth/commit/52819a941573c64e440fddbcc4a828c4fa30609a))
|
|
174
|
+
* sync lockfile with package.json dependencies ([8db4a45](https://github.com/gotgenes/pi-anthropic-auth/commit/8db4a458a05d97b3e829b453f3093c90fd9a30a5))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
### Documentation
|
|
178
|
+
|
|
179
|
+
* fix license copyright year ([75c2995](https://github.com/gotgenes/pi-anthropic-auth/commit/75c29954ea5b62f977be910eb174953a0abe07ce))
|
|
180
|
+
* update README with pi install instructions and NPM usage ([5cbdab8](https://github.com/gotgenes/pi-anthropic-auth/commit/5cbdab84017e619984dcccf8aa38e8e30fb0871c))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
### Miscellaneous Chores
|
|
184
|
+
|
|
185
|
+
* fix prek hook ordering and add auto-fix flags ([204676d](https://github.com/gotgenes/pi-anthropic-auth/commit/204676d4c12732cee42c0110ce5d5e7bee54534b))
|
package/README.md
CHANGED
|
@@ -16,6 +16,11 @@ This extension fills in the gaps for users who want to use their **Claude Pro or
|
|
|
16
16
|
|
|
17
17
|
It keeps everything you'd expect — the built-in `anthropic` provider, the full model list, API-key behavior, and the native `/login anthropic` flow — and layers on the compatibility fixes needed to make OAuth subscriptions work reliably.
|
|
18
18
|
|
|
19
|
+
Requests to non-Anthropic providers and plain API-key Anthropic requests pass through completely untouched — the extension only activates when it detects an Anthropic OAuth access token (`sk-ant-oat`).
|
|
20
|
+
|
|
21
|
+
Shaping runs in a thin transport wrapper around Pi's own Anthropic transport, so it applies to every OAuth call path — the interactive loop, compaction, and any background-agent work — not just the main turn.
|
|
22
|
+
See [docs/architecture.md](docs/architecture.md) for how this works.
|
|
23
|
+
|
|
19
24
|
## Install
|
|
20
25
|
|
|
21
26
|
```bash
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotgenes/pi-anthropic-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Pi extension package for Anthropic OAuth compatibility",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Chris Lasher"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"types": "./dist/index.d.ts",
|
|
13
|
-
"default": "./dist/index.js"
|
|
14
|
-
}
|
|
9
|
+
"imports": {
|
|
10
|
+
"#src/*": "./src/*",
|
|
11
|
+
"#test/*": "./test/*"
|
|
15
12
|
},
|
|
16
13
|
"files": [
|
|
17
|
-
"
|
|
14
|
+
"src",
|
|
15
|
+
"README.md",
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"keywords": [
|
|
20
20
|
"pi",
|
|
@@ -27,35 +27,43 @@
|
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "git+https://github.com/gotgenes/pi-anthropic-auth.git"
|
|
29
29
|
},
|
|
30
|
+
"homepage": "https://github.com/gotgenes/pi-anthropic-auth#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/gotgenes/pi-anthropic-auth/issues"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22"
|
|
36
|
+
},
|
|
30
37
|
"publishConfig": {
|
|
31
38
|
"access": "public"
|
|
32
39
|
},
|
|
33
40
|
"pi": {
|
|
34
41
|
"extensions": [
|
|
35
|
-
"./
|
|
42
|
+
"./src/index.ts"
|
|
36
43
|
]
|
|
37
44
|
},
|
|
38
45
|
"peerDependencies": {
|
|
39
|
-
"@earendil-works/pi-ai": "
|
|
40
|
-
"@earendil-works/pi-coding-agent": "
|
|
46
|
+
"@earendil-works/pi-ai": ">=0.79.1",
|
|
47
|
+
"@earendil-works/pi-coding-agent": ">=0.79.1"
|
|
41
48
|
},
|
|
42
49
|
"devDependencies": {
|
|
43
|
-
"@anthropic-ai/sdk": "^0.
|
|
44
|
-
"@biomejs/biome": "^2.4.
|
|
45
|
-
"@earendil-works/pi-ai": "
|
|
46
|
-
"@earendil-works/pi-coding-agent": "
|
|
47
|
-
"
|
|
50
|
+
"@anthropic-ai/sdk": "^0.104.1",
|
|
51
|
+
"@biomejs/biome": "^2.4.16",
|
|
52
|
+
"@earendil-works/pi-ai": "0.79.1",
|
|
53
|
+
"@earendil-works/pi-coding-agent": "0.79.1",
|
|
54
|
+
"@eslint/js": "^10.0.1",
|
|
55
|
+
"@types/node": "^22.15.3",
|
|
56
|
+
"eslint": "^10.4.1",
|
|
57
|
+
"globals": "^17.6.0",
|
|
58
|
+
"rumdl": "^0.2.14",
|
|
48
59
|
"typescript": "^6.0.3",
|
|
49
|
-
"
|
|
60
|
+
"typescript-eslint": "^8.61.0",
|
|
61
|
+
"vitest": "^4.1.8"
|
|
50
62
|
},
|
|
51
63
|
"scripts": {
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"lint": "biome check .",
|
|
55
|
-
"lint:fix": "biome check --write .",
|
|
56
|
-
"lint:md": "markdownlint-cli2 '*.md'",
|
|
57
|
-
"lint:all": "pnpm run lint && pnpm run lint:md",
|
|
58
|
-
"format": "biome format --write .",
|
|
64
|
+
"check": "tsc --noEmit",
|
|
65
|
+
"lint:md": "rumdl check *.md docs/**/*.md",
|
|
66
|
+
"lint": "biome check . && eslint . && pnpm run lint:md",
|
|
59
67
|
"test": "vitest run",
|
|
60
68
|
"test:watch": "vitest"
|
|
61
69
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OAuthCredentials,
|
|
3
|
+
OAuthLoginCallbacks,
|
|
4
|
+
} from "@earendil-works/pi-ai";
|
|
5
|
+
import {
|
|
6
|
+
loginAnthropic,
|
|
7
|
+
refreshAnthropicToken,
|
|
8
|
+
} from "@earendil-works/pi-ai/oauth";
|
|
9
|
+
|
|
10
|
+
export function mergeRefreshedCredentials(
|
|
11
|
+
credentials: OAuthCredentials,
|
|
12
|
+
refreshed: Partial<OAuthCredentials>,
|
|
13
|
+
): OAuthCredentials {
|
|
14
|
+
return {
|
|
15
|
+
...credentials,
|
|
16
|
+
...refreshed,
|
|
17
|
+
refresh:
|
|
18
|
+
typeof refreshed.refresh === "string" &&
|
|
19
|
+
refreshed.refresh.trim().length > 0
|
|
20
|
+
? refreshed.refresh
|
|
21
|
+
: credentials.refresh,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const anthropicOAuthOverride = {
|
|
26
|
+
name: "Anthropic (Claude Pro/Max)",
|
|
27
|
+
login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
28
|
+
return loginAnthropic(callbacks);
|
|
29
|
+
},
|
|
30
|
+
async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
|
|
31
|
+
const refreshed = await refreshAnthropicToken(credentials.refresh);
|
|
32
|
+
|
|
33
|
+
return mergeRefreshedCredentials(credentials, refreshed);
|
|
34
|
+
},
|
|
35
|
+
getApiKey(credentials: OAuthCredentials): string {
|
|
36
|
+
return credentials.access;
|
|
37
|
+
},
|
|
38
|
+
} as const;
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Used to detect whether a system block contains Pi's original verbose
|
|
5
5
|
* preamble so it can be replaced with the minimal neutral prompt.
|
|
6
6
|
*/
|
|
7
|
-
export const PI_DEFAULT_PROMPT_PREFIX =
|
|
7
|
+
export const PI_DEFAULT_PROMPT_PREFIX =
|
|
8
|
+
"You are an expert coding assistant operating inside pi, a coding agent harness.";
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Final line of Pi's built-in default system prompt preamble.
|
|
10
12
|
*
|
|
@@ -12,7 +14,9 @@ export const PI_DEFAULT_PROMPT_PREFIX = "You are an expert coding assistant oper
|
|
|
12
14
|
* neutral Anthropic OAuth prompt while preserving anything appended after the
|
|
13
15
|
* preamble (project context, skills, and date/cwd footer).
|
|
14
16
|
*/
|
|
15
|
-
export const PI_DEFAULT_PROMPT_TERMINATOR =
|
|
17
|
+
export const PI_DEFAULT_PROMPT_TERMINATOR =
|
|
18
|
+
"- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)";
|
|
19
|
+
|
|
16
20
|
/**
|
|
17
21
|
* Prefix of the minimal neutral Anthropic OAuth system prompt.
|
|
18
22
|
*
|
|
@@ -20,7 +24,9 @@ export const PI_DEFAULT_PROMPT_TERMINATOR = "- Always read pi .md files complete
|
|
|
20
24
|
* that have already been shaped. Must match the first line of
|
|
21
25
|
* MINIMAL_ANTHROPIC_OAUTH_PROMPT.
|
|
22
26
|
*/
|
|
23
|
-
export const MINIMAL_ANTHROPIC_OAUTH_PROMPT_PREFIX =
|
|
27
|
+
export const MINIMAL_ANTHROPIC_OAUTH_PROMPT_PREFIX =
|
|
28
|
+
"You are an expert coding assistant.";
|
|
29
|
+
|
|
24
30
|
/**
|
|
25
31
|
* Minimal neutral system prompt used for Anthropic OAuth requests.
|
|
26
32
|
*
|
|
@@ -28,11 +34,12 @@ export const MINIMAL_ANTHROPIC_OAUTH_PROMPT_PREFIX = "You are an expert coding a
|
|
|
28
34
|
* while preserving any project context that follows.
|
|
29
35
|
*/
|
|
30
36
|
export const MINIMAL_ANTHROPIC_OAUTH_PROMPT = [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
MINIMAL_ANTHROPIC_OAUTH_PROMPT_PREFIX,
|
|
38
|
+
"Be concise and helpful.",
|
|
39
|
+
"Use the available tools to answer the user's request.",
|
|
40
|
+
"Show file paths clearly when working with files.",
|
|
35
41
|
].join("\n");
|
|
42
|
+
|
|
36
43
|
/**
|
|
37
44
|
* Prefix of Claude Code's identity injection block.
|
|
38
45
|
*
|
|
@@ -40,7 +47,9 @@ export const MINIMAL_ANTHROPIC_OAUTH_PROMPT = [
|
|
|
40
47
|
* provider, which injects a "You are Claude Code, Anthropic's official CLI"
|
|
41
48
|
* system block for OAuth sessions.
|
|
42
49
|
*/
|
|
43
|
-
export const CLAUDE_CODE_IDENTITY_PREFIX =
|
|
50
|
+
export const CLAUDE_CODE_IDENTITY_PREFIX =
|
|
51
|
+
"You are Claude Code, Anthropic's official CLI";
|
|
52
|
+
|
|
44
53
|
// ---------------------------------------------------------------------------
|
|
45
54
|
// Billing header constants
|
|
46
55
|
//
|
|
@@ -53,6 +62,7 @@ export const CLAUDE_CODE_IDENTITY_PREFIX = "You are Claude Code, Anthropic's off
|
|
|
53
62
|
// version at https://github.com/anthropics/claude-code or in a working Claude
|
|
54
63
|
// Code installation (`claude --version`).
|
|
55
64
|
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
56
66
|
/**
|
|
57
67
|
* Claude Code version string embedded in the billing header.
|
|
58
68
|
*
|
|
@@ -61,13 +71,17 @@ export const CLAUDE_CODE_IDENTITY_PREFIX = "You are Claude Code, Anthropic's off
|
|
|
61
71
|
* too far from what Anthropic expects, OAuth requests may be rejected or
|
|
62
72
|
* counted incorrectly.
|
|
63
73
|
*/
|
|
64
|
-
export const CLAUDE_CODE_VERSION = "2.1.
|
|
74
|
+
export const CLAUDE_CODE_VERSION = "2.1.150";
|
|
75
|
+
|
|
65
76
|
/** Salt used in the billing header suffix hash. */
|
|
66
77
|
export const BILLING_HEADER_SALT = "59cf53e54c78";
|
|
78
|
+
|
|
67
79
|
/** Character positions sampled from the first user message for the billing hash. */
|
|
68
|
-
export const BILLING_HEADER_POSITIONS = [4, 7, 20];
|
|
80
|
+
export const BILLING_HEADER_POSITIONS = [4, 7, 20] as const;
|
|
81
|
+
|
|
69
82
|
/** Entrypoint identifier included in the billing header. */
|
|
70
83
|
export const CLAUDE_CODE_ENTRYPOINT = "sdk-cli";
|
|
84
|
+
|
|
71
85
|
// ---------------------------------------------------------------------------
|
|
72
86
|
// Anchor-driven sanitizer constants
|
|
73
87
|
//
|
|
@@ -80,19 +94,21 @@ export const CLAUDE_CODE_ENTRYPOINT = "sdk-cli";
|
|
|
80
94
|
// rewording — as long as the anchor still appears somewhere in the paragraph,
|
|
81
95
|
// removal works regardless of surrounding text changes.
|
|
82
96
|
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
83
98
|
/**
|
|
84
99
|
* Strings whose presence in a paragraph marks it as Pi-specific and droppable.
|
|
85
100
|
*
|
|
86
101
|
* Each entry is checked with `paragraph.includes(anchor)`.
|
|
87
102
|
*/
|
|
88
|
-
export const PARAGRAPH_REMOVAL_ANCHORS = [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
export const PARAGRAPH_REMOVAL_ANCHORS: readonly string[] = [
|
|
104
|
+
// Pi identity sentence
|
|
105
|
+
"operating inside pi, a coding agent harness",
|
|
106
|
+
// Pi-specific filler about custom tools
|
|
107
|
+
"In addition to the tools above",
|
|
108
|
+
// Pi documentation block — references Pi-specific docs/paths
|
|
109
|
+
"Pi documentation (read only when the user asks about pi itself",
|
|
95
110
|
];
|
|
111
|
+
|
|
96
112
|
/**
|
|
97
113
|
* Inline text replacements applied after paragraph removal.
|
|
98
114
|
*
|
|
@@ -108,9 +124,13 @@ export const PARAGRAPH_REMOVAL_ANCHORS = [
|
|
|
108
124
|
* We don't currently emit this phrase, but it's included as a documented
|
|
109
125
|
* future risk per Issue #10.
|
|
110
126
|
*/
|
|
111
|
-
export const TEXT_REPLACEMENTS
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
export const TEXT_REPLACEMENTS: readonly {
|
|
128
|
+
match: string;
|
|
129
|
+
replacement: string;
|
|
130
|
+
}[] = [
|
|
131
|
+
{
|
|
132
|
+
match:
|
|
133
|
+
"Here is some useful information about the environment you are running in:",
|
|
134
|
+
replacement: "Environment context you are running in:",
|
|
135
|
+
},
|
|
116
136
|
];
|
package/src/debug.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type DebugMode = "off" | "all" | "tool-use";
|
|
2
|
+
|
|
3
|
+
function getDebugMode(value: string | undefined): DebugMode {
|
|
4
|
+
if (!value) return "off";
|
|
5
|
+
|
|
6
|
+
switch (value.trim().toLowerCase()) {
|
|
7
|
+
case "1":
|
|
8
|
+
case "true":
|
|
9
|
+
case "yes":
|
|
10
|
+
case "on":
|
|
11
|
+
case "debug":
|
|
12
|
+
case "all":
|
|
13
|
+
return "all";
|
|
14
|
+
case "tool":
|
|
15
|
+
case "tools":
|
|
16
|
+
case "tool-use":
|
|
17
|
+
case "tool_use":
|
|
18
|
+
return "tool-use";
|
|
19
|
+
default:
|
|
20
|
+
return "off";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isDebugEnabled(): boolean {
|
|
25
|
+
return getDebugMode(process.env.PI_ANTHROPIC_AUTH_DEBUG) !== "off";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isToolUseOnlyDebugEnabled(): boolean {
|
|
29
|
+
return getDebugMode(process.env.PI_ANTHROPIC_AUTH_DEBUG) === "tool-use";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function debugLog(scope: string, data: unknown): void {
|
|
33
|
+
if (!isDebugEnabled()) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.error(`[pi-anthropic-auth][debug] ${scope} ${JSON.stringify(data)}`);
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getApiProvider } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { anthropicOAuthOverride } from "./anthropic-oauth";
|
|
4
|
+
import { createAnthropicOAuthStreamSimple } from "./oauth-transport";
|
|
5
|
+
|
|
6
|
+
export default function (pi: ExtensionAPI) {
|
|
7
|
+
// Capture Pi's built-in anthropic-messages transport BEFORE we override it,
|
|
8
|
+
// so our wrapper can delegate to it (delegating to the registered wrapper
|
|
9
|
+
// instead would recurse). pi-ai registers its built-ins on import, so this
|
|
10
|
+
// is resolved by the time extensions load.
|
|
11
|
+
const builtinTransport = getApiProvider("anthropic-messages");
|
|
12
|
+
|
|
13
|
+
// Re-register the built-in `anthropic` provider with:
|
|
14
|
+
// 1. our OAuth login + refresh override (`oauth`), and
|
|
15
|
+
// 2. a thin transport wrapper (`streamSimple`) that applies Claude Code
|
|
16
|
+
// OAuth request shaping on every Anthropic call path.
|
|
17
|
+
//
|
|
18
|
+
// Omitting `models` preserves Pi's built-in Anthropic model list. The
|
|
19
|
+
// transport wrapper replaces our previous `before_provider_request` handler:
|
|
20
|
+
// that hook only fires for the interactive agent loop, so auxiliary OAuth
|
|
21
|
+
// requests (built-in compaction, third-party background agents) bypassed it
|
|
22
|
+
// and failed with Anthropic "extra usage" 400s. Registering `streamSimple`
|
|
23
|
+
// routes through Pi's singleton API registry, so the same shaping now covers
|
|
24
|
+
// the main loop, `completeSimple` compaction, and `agentLoop` background work.
|
|
25
|
+
pi.registerProvider("anthropic", {
|
|
26
|
+
oauth: anthropicOAuthOverride,
|
|
27
|
+
...(builtinTransport
|
|
28
|
+
? {
|
|
29
|
+
api: "anthropic-messages",
|
|
30
|
+
streamSimple: createAnthropicOAuthStreamSimple(
|
|
31
|
+
builtinTransport.streamSimple,
|
|
32
|
+
),
|
|
33
|
+
}
|
|
34
|
+
: {}),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Api,
|
|
3
|
+
AssistantMessageEventStream,
|
|
4
|
+
Context,
|
|
5
|
+
Model,
|
|
6
|
+
SimpleStreamOptions,
|
|
7
|
+
} from "@earendil-works/pi-ai";
|
|
8
|
+
import { shapeAnthropicOAuthPayload } from "./request-shaping";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Anthropic OAuth access tokens are issued with an `sk-ant-oat` prefix.
|
|
12
|
+
*
|
|
13
|
+
* This is the same signal Pi's built-in Anthropic provider uses internally to
|
|
14
|
+
* decide whether to emit Claude Code identity headers, so gating on it keeps
|
|
15
|
+
* our shaping aligned with Pi's own OAuth detection.
|
|
16
|
+
*/
|
|
17
|
+
const ANTHROPIC_OAUTH_TOKEN_MARKER = "sk-ant-oat";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The transport-level `streamSimple` handler shape Pi's API registry uses.
|
|
21
|
+
*
|
|
22
|
+
* It matches `ApiStreamSimpleFunction` from `@earendil-works/pi-ai` and is
|
|
23
|
+
* intentionally wider than a single concrete model type because Pi registers
|
|
24
|
+
* it per `Api`, not per model.
|
|
25
|
+
*/
|
|
26
|
+
export type AnthropicStreamSimple = (
|
|
27
|
+
model: Model<Api>,
|
|
28
|
+
context: Context,
|
|
29
|
+
options?: SimpleStreamOptions,
|
|
30
|
+
) => AssistantMessageEventStream;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns true when the resolved API key is an Anthropic OAuth access token.
|
|
34
|
+
*
|
|
35
|
+
* API-key requests (and OAuth tokens for other providers) return false, so the
|
|
36
|
+
* caller leaves their payloads untouched.
|
|
37
|
+
*/
|
|
38
|
+
export function isAnthropicOAuthToken(
|
|
39
|
+
apiKey: string | undefined,
|
|
40
|
+
): apiKey is string {
|
|
41
|
+
return (
|
|
42
|
+
typeof apiKey === "string" && apiKey.includes(ANTHROPIC_OAUTH_TOKEN_MARKER)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Wraps Pi's built-in Anthropic `streamSimple` transport so OAuth request
|
|
48
|
+
* shaping runs on **every** Anthropic call path, not only the main agent loop.
|
|
49
|
+
*
|
|
50
|
+
* Pi only threads its `before_provider_request` hook into the interactive agent
|
|
51
|
+
* loop. Auxiliary calls — built-in compaction/summarization (`completeSimple`)
|
|
52
|
+
* and third-party background agents (e.g. pi-observational-memory's observer /
|
|
53
|
+
* reflector / dropper running via `agentLoop`) — issue requests through the
|
|
54
|
+
* same singleton API-registry transport but without that hook. Those OAuth
|
|
55
|
+
* requests then reach Anthropic with no Claude Code billing header and are
|
|
56
|
+
* classified as third-party app usage, producing the misleading "extra usage"
|
|
57
|
+
* HTTP 400.
|
|
58
|
+
*
|
|
59
|
+
* By injecting our shaping as an `onPayload` on the underlying transport, every
|
|
60
|
+
* Anthropic OAuth request is shaped regardless of which Pi code path issued it.
|
|
61
|
+
* The wrapper composes (does not replace) any caller-provided `onPayload`, so
|
|
62
|
+
* other extensions' `before_provider_request` handlers continue to run on the
|
|
63
|
+
* main loop, and our shaping is applied last — closest to the wire.
|
|
64
|
+
*
|
|
65
|
+
* Gating is strictly OAuth-only: when the request is not an Anthropic OAuth
|
|
66
|
+
* token, the payload passes through untouched, preserving Pi's normal
|
|
67
|
+
* API-key and non-Anthropic transport behavior.
|
|
68
|
+
*
|
|
69
|
+
* @param delegate The built-in Anthropic `streamSimple` transport, captured via
|
|
70
|
+
* `getApiProvider("anthropic-messages")` **before** this wrapper is
|
|
71
|
+
* registered. It must be the underlying transport, never the registered
|
|
72
|
+
* wrapper, otherwise calls would recurse.
|
|
73
|
+
*/
|
|
74
|
+
export function createAnthropicOAuthStreamSimple(
|
|
75
|
+
delegate: AnthropicStreamSimple,
|
|
76
|
+
): AnthropicStreamSimple {
|
|
77
|
+
return (model, context, options) => {
|
|
78
|
+
const callerOnPayload = options?.onPayload;
|
|
79
|
+
|
|
80
|
+
const onPayload: SimpleStreamOptions["onPayload"] = async (
|
|
81
|
+
payload,
|
|
82
|
+
payloadModel,
|
|
83
|
+
) => {
|
|
84
|
+
const upstream = callerOnPayload
|
|
85
|
+
? ((await callerOnPayload(payload, payloadModel)) ?? payload)
|
|
86
|
+
: payload;
|
|
87
|
+
|
|
88
|
+
if (!isAnthropicOAuthToken(options?.apiKey)) {
|
|
89
|
+
return upstream;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return shapeAnthropicOAuthPayload(upstream);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return delegate(model, context, { ...options, onPayload });
|
|
96
|
+
};
|
|
97
|
+
}
|