@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 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.4.6",
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
- "main": "./dist/index.js",
10
- "exports": {
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
- "dist"
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
- "./dist/index.js"
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.95.0",
44
- "@biomejs/biome": "^2.4.14",
45
- "@earendil-works/pi-ai": "^0.74.0",
46
- "@earendil-works/pi-coding-agent": "^0.74.0",
47
- "markdownlint-cli2": "^0.22.1",
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
- "vitest": "^4.1.5"
60
+ "typescript-eslint": "^8.61.0",
61
+ "vitest": "^4.1.8"
50
62
  },
51
63
  "scripts": {
52
- "build": "tsc -p tsconfig.build.json",
53
- "check": "tsc -p tsconfig.check.json --noEmit",
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 = "You are an expert coding assistant operating inside pi, a coding agent harness.";
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 = "- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)";
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 = "You are an expert coding assistant.";
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
- MINIMAL_ANTHROPIC_OAUTH_PROMPT_PREFIX,
32
- "Be concise and helpful.",
33
- "Use the available tools to answer the user's request.",
34
- "Show file paths clearly when working with files.",
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 = "You are Claude Code, Anthropic's official CLI";
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.119";
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
- // Pi identity sentence
90
- "operating inside pi, a coding agent harness",
91
- // Pi-specific filler about custom tools
92
- "In addition to the tools above",
93
- // Pi documentation block — references Pi-specific docs/paths
94
- "Pi documentation (read only when the user asks about pi itself",
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
- match: "Here is some useful information about the environment you are running in:",
114
- replacement: "Environment context you are running in:",
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
+ }