oira666_pi-limits-wait 0.4.14 → 0.4.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oira666_pi-limits-wait",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Extension for PI that allows to automatically wait for limits to reset and retry the request",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/constants.ts CHANGED
@@ -10,6 +10,13 @@ export const PI_REMOVAL_ANCHORS = [
10
10
  export const PI_IDENTITY_SENTENCE_PATTERN =
11
11
  /(?:^|\n)\s*You are pi\b[^.!?\n]*(?:[.!?](?=\s|$)|(?=\n|$))/gi;
12
12
 
13
+ // Strips the host-harness identity clause pi prepends to its system prompt, e.g.
14
+ // "You are an expert coding assistant operating inside pi, a coding agent harness."
15
+ // The Claude Code identity block already establishes who the assistant is, so
16
+ // leaving this clause in tells the model (and Anthropic) it is running inside pi.
17
+ export const PI_HARNESS_IDENTITY_PATTERN =
18
+ /\s+operating inside pi,\s*a coding agent harness/gi;
19
+
13
20
  export const CLAUDE_CODE_IDENTITY_PATTERN =
14
21
  /(?:^|\n)\s*You are Claude Code, Anthropic's official CLI for Claude\.\s*/gi;
15
22
 
package/src/index.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import { isInternalSyntheticModel } from "./models.js";
3
+ import { sanitiseAnthropicPayloadSystem } from "./prompt.js";
3
4
  import { loadFallbackSettings } from "./settings.js";
4
5
  import { state } from "./state.js";
5
6
  import { registerWrappedApi } from "./stream.js";
6
7
 
7
- export { sanitiseSystemPrompt } from "./prompt.js";
8
+ export { sanitiseAnthropicPayloadSystem, sanitiseSystemPrompt } from "./prompt.js";
8
9
  export {
9
10
  getRetryableError,
10
11
  isAuthenticationRefreshError,
@@ -39,6 +40,16 @@ function registerContextApis(pi: ExtensionAPI, ctx: ExtensionContext): void {
39
40
  export default function (pi: ExtensionAPI) {
40
41
  state.extensionApi = pi;
41
42
 
43
+ // Anthropic now rejects Claude Pro/Max (OAuth) requests whose system prompt
44
+ // still carries the host agent's fingerprints (pi-coding-agent paths, "You
45
+ // are pi", etc.). pi-ai prepends the Claude Code identity block for OAuth
46
+ // tokens but passes the host system prompt through as a second block, which
47
+ // trips the check. Sanitise that block on the outbound payload right before
48
+ // it is sent. This hook fires for every provider request and only mutates
49
+ // Anthropic OAuth payloads (identified by the Claude Code identity block), so
50
+ // API-key Anthropic calls and non-Anthropic providers are untouched.
51
+ pi.on("before_provider_request", (event) => sanitiseAnthropicPayloadSystem(event.payload));
52
+
42
53
  pi.on("model_select", (event) => {
43
54
  if (state.suppressNextModelSelect) {
44
55
  state.suppressNextModelSelect = false;
package/src/prompt.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import type { Api, Context, Model, SimpleStreamOptions } from "@mariozechner/pi-ai";
3
- import { CLAUDE_CODE_IDENTITY, CLAUDE_CODE_IDENTITY_PATTERN, PI_IDENTITY_SENTENCE_PATTERN, PI_REMOVAL_ANCHORS } from "./constants.js";
3
+ import { CLAUDE_CODE_IDENTITY, CLAUDE_CODE_IDENTITY_PATTERN, PI_HARNESS_IDENTITY_PATTERN, PI_IDENTITY_SENTENCE_PATTERN, PI_REMOVAL_ANCHORS } from "./constants.js";
4
4
  import { state } from "./state.js";
5
5
 
6
6
  export function sanitiseSystemPrompt(raw: string): string {
@@ -13,6 +13,7 @@ export function sanitiseSystemPrompt(raw: string): string {
13
13
  .join("\n\n")
14
14
  .replace(CLAUDE_CODE_IDENTITY_PATTERN, "")
15
15
  .replace(PI_IDENTITY_SENTENCE_PATTERN, "")
16
+ .replace(PI_HARNESS_IDENTITY_PATTERN, "")
16
17
  .replace(/\n{3,}/g, "\n\n")
17
18
  .trim();
18
19
  }
@@ -87,3 +88,61 @@ export function anthropicSubscriptionContext(
87
88
  : CLAUDE_CODE_IDENTITY,
88
89
  };
89
90
  }
91
+
92
+ interface SystemTextBlock {
93
+ type: string;
94
+ text?: unknown;
95
+ [key: string]: unknown;
96
+ }
97
+
98
+ function isClaudeCodeIdentityBlock(block: unknown): block is SystemTextBlock {
99
+ if (!block || typeof block !== "object") return false;
100
+ const b = block as SystemTextBlock;
101
+ return b.type === "text" && typeof b.text === "string" && b.text.includes(CLAUDE_CODE_IDENTITY);
102
+ }
103
+
104
+ /**
105
+ * Sanitise the `system` blocks of an Anthropic provider request payload.
106
+ *
107
+ * Anthropic now rejects Claude Pro/Max (OAuth) requests whose system prompt
108
+ * still carries third-party-agent fingerprints (e.g. pi-coding-agent paths or
109
+ * "You are pi" identity). pi-ai already prepends the Claude Code identity as
110
+ * the first `system` block for OAuth tokens, but passes the host agent's full
111
+ * system prompt through as a second block unchanged, which trips the check.
112
+ *
113
+ * This runs from the `before_provider_request` extension hook, which fires for
114
+ * every provider request and whose return value replaces the outbound payload.
115
+ * It only touches payloads that already carry the Claude Code identity block
116
+ * (i.e. Anthropic OAuth requests), so API-key Anthropic calls and non-Anthropic
117
+ * providers are left untouched. Each remaining text block is run through
118
+ * `sanitiseSystemPrompt`; blocks that become empty are dropped.
119
+ */
120
+ export function sanitiseAnthropicPayloadSystem(payload: unknown): unknown {
121
+ if (!payload || typeof payload !== "object") return payload;
122
+ const system = (payload as { system?: unknown }).system;
123
+ if (!Array.isArray(system)) return payload;
124
+
125
+ const identityIndex = system.findIndex(isClaudeCodeIdentityBlock);
126
+ if (identityIndex === -1) return payload;
127
+
128
+ const newSystem: unknown[] = [];
129
+ for (let i = 0; i < system.length; i++) {
130
+ const block = system[i];
131
+ if (i === identityIndex) {
132
+ newSystem.push(block);
133
+ continue;
134
+ }
135
+ if (
136
+ block && typeof block === "object"
137
+ && (block as SystemTextBlock).type === "text"
138
+ && typeof (block as SystemTextBlock).text === "string"
139
+ ) {
140
+ const sanitised = sanitiseSystemPrompt((block as SystemTextBlock).text as string);
141
+ if (sanitised) newSystem.push({ ...block, text: sanitised });
142
+ } else {
143
+ newSystem.push(block);
144
+ }
145
+ }
146
+
147
+ return { ...payload, system: newSystem };
148
+ }