@madgagarin/pi-agentrouter 1.2.1 → 1.3.0

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.
Files changed (2) hide show
  1. package/index.ts +76 -9
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -118,6 +118,53 @@ export function isAgentRouter(providerName?: string, baseUrl?: string): boolean
118
118
  return false;
119
119
  }
120
120
 
121
+ export const CANONICAL_PI_HEADER =
122
+ "You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.";
123
+
124
+ /**
125
+ * Enforces that the canonical pi-code system prompt signature is strictly at index 0.
126
+ * If another plugin or wrapper prepended text before the canonical header, it reorders
127
+ * the header to the very top and shifts the injected prefix right after it.
128
+ * If the header is missing entirely (e.g. from replace mode), it prepends the canonical header.
129
+ * This guarantees both WAF client authentication and stable prompt cache prefix matching.
130
+ */
131
+ export function enforceCanonicalRootPrompt(systemPrompt: string | any[] | undefined): string | any[] {
132
+ if (!systemPrompt) {
133
+ return CANONICAL_PI_HEADER;
134
+ }
135
+
136
+ if (typeof systemPrompt === "string") {
137
+ const text = systemPrompt.trim();
138
+ const piHeaderRegex = /(?:You are [^\n\r]*operating inside pi[^\n\r]*\n?|You are (?:pi|Pi)[^\n\r]*\n?)/i;
139
+
140
+ if (piHeaderRegex.test(text)) {
141
+ const match = text.match(piHeaderRegex);
142
+ if (match && match.index !== undefined && match.index > 0) {
143
+ const header = match[0].trim();
144
+ const prefix = text.slice(0, match.index).trim();
145
+ const rest = text.slice(match.index + match[0].length).trim();
146
+ return `${header}\n\n${prefix}${rest ? "\n\n" + rest : ""}`;
147
+ }
148
+ return text;
149
+ } else {
150
+ return `${CANONICAL_PI_HEADER}\n\n${text}`;
151
+ }
152
+ }
153
+
154
+ if (Array.isArray(systemPrompt)) {
155
+ if (systemPrompt.length === 0) {
156
+ return [{ type: "text", text: CANONICAL_PI_HEADER }];
157
+ }
158
+ const firstBlock = systemPrompt[0];
159
+ if (firstBlock && typeof firstBlock.text === "string") {
160
+ firstBlock.text = enforceCanonicalRootPrompt(firstBlock.text) as string;
161
+ }
162
+ return systemPrompt;
163
+ }
164
+
165
+ return systemPrompt;
166
+ }
167
+
121
168
  export default function (pi: ExtensionAPI) {
122
169
  function registerAgentRouterProviders(apiKey: string): void {
123
170
  pi.registerProvider("agentrouter-openai", {
@@ -149,15 +196,7 @@ export default function (pi: ExtensionAPI) {
149
196
  baseUrl: "https://agentrouter.org",
150
197
  apiKey,
151
198
  api: "anthropic-messages",
152
- headers: {
153
- "User-Agent": "claude-cli/1.0.108 (external, cli)",
154
- "anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,context-management-2025-06-27",
155
- "x-stainless-lang": "js",
156
- "x-stainless-package-version": "0.38.0",
157
- "x-stainless-os": "Linux",
158
- "x-stainless-arch": "x64",
159
- "x-stainless-runtime": "node"
160
- },
199
+
161
200
  compat: {
162
201
  forceAdaptiveThinking: true,
163
202
  sendSessionAffinityHeaders: true,
@@ -203,6 +242,34 @@ export default function (pi: ExtensionAPI) {
203
242
  }
204
243
  }
205
244
 
245
+ // Enforce root system prompt signature and prompt prefix stability at the lowest transport level
246
+ pi.on("before_provider_request", async (event, ctx) => {
247
+ const provider = ((event as any)?.model?.provider || ctx?.model?.provider || "").toLowerCase();
248
+ const baseUrl = ((event as any)?.model?.baseUrl || (ctx?.model as any)?.baseUrl || "");
249
+
250
+ if (isAgentRouter(provider, baseUrl)) {
251
+ const payload = event.payload;
252
+ if (payload) {
253
+ // 1. Enforce canonical root system prompt in payload.system (Anthropic format)
254
+ if (payload.system !== undefined) {
255
+ payload.system = enforceCanonicalRootPrompt(payload.system);
256
+ }
257
+
258
+ // 2. Enforce canonical root system prompt in payload.messages[0] (OpenAI format)
259
+ if (Array.isArray(payload.messages) && payload.messages.length > 0) {
260
+ const firstMsg = payload.messages[0];
261
+ if (firstMsg && firstMsg.role === "system") {
262
+ if (typeof firstMsg.content === "string") {
263
+ firstMsg.content = enforceCanonicalRootPrompt(firstMsg.content);
264
+ } else if (Array.isArray(firstMsg.content)) {
265
+ firstMsg.content = enforceCanonicalRootPrompt(firstMsg.content);
266
+ }
267
+ }
268
+ }
269
+ }
270
+ }
271
+ });
272
+
206
273
  pi.on("session_start", async (_event, ctx) => {
207
274
  updatePromptRewriteEnvForModel(ctx.model);
208
275
  setLastRequestEndTime(Date.now());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madgagarin/pi-agentrouter",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Pi coding agent extension for AgentRouter (GPT-5.6 Sol & Claude Opus 5) with rate-limit pacing and caching.",
5
5
  "publishConfig": {
6
6
  "access": "public"