@madgagarin/pi-agentrouter 1.1.1 → 1.2.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 +124 -7
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -4,6 +4,7 @@ import * as fs from "fs";
4
4
  import * as path from "path";
5
5
 
6
6
  const CONFIG_FILE = path.join(process.env.HOME || "", ".pi/agent/agentrouter.json");
7
+ const SETTINGS_FILE = path.join(process.env.HOME || "", ".pi/agent/settings.json");
7
8
 
8
9
  export interface AgentRouterConfig {
9
10
  apiKey?: string;
@@ -34,10 +35,62 @@ export function normalizeApiKey(key?: string): string {
34
35
  return key.trim().replace(/^["']|["']$/g, "").trim();
35
36
  }
36
37
 
38
+ export interface PackageOrderState {
39
+ agentRouterIndex: number;
40
+ cacheOptimizerIndex: number;
41
+ needsFix: boolean;
42
+ }
43
+
44
+ export function getPackageOrderState(): PackageOrderState {
45
+ try {
46
+ if (!fs.existsSync(SETTINGS_FILE)) {
47
+ return { agentRouterIndex: -1, cacheOptimizerIndex: -1, needsFix: false };
48
+ }
49
+ const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, "utf-8"));
50
+ if (!Array.isArray(settings.packages)) {
51
+ return { agentRouterIndex: -1, cacheOptimizerIndex: -1, needsFix: false };
52
+ }
53
+ const arIdx = settings.packages.findIndex((p: string) =>
54
+ typeof p === "string" && (p.includes("@madgagarin/pi-agentrouter") || p.includes("pi-agentrouter"))
55
+ );
56
+ const cacheIdx = settings.packages.findIndex((p: string) =>
57
+ typeof p === "string" && p.includes("pi-cache-optimizer")
58
+ );
59
+ const needsFix = cacheIdx !== -1 && arIdx !== -1 && arIdx > cacheIdx;
60
+ return { agentRouterIndex: arIdx, cacheOptimizerIndex: cacheIdx, needsFix };
61
+ } catch {
62
+ return { agentRouterIndex: -1, cacheOptimizerIndex: -1, needsFix: false };
63
+ }
64
+ }
65
+
66
+ export function fixPackagePriorityInSettings(): boolean {
67
+ try {
68
+ if (!fs.existsSync(SETTINGS_FILE)) return false;
69
+ const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, "utf-8"));
70
+ if (!Array.isArray(settings.packages)) return false;
71
+ const arIdx = settings.packages.findIndex((p: string) =>
72
+ typeof p === "string" && (p.includes("@madgagarin/pi-agentrouter") || p.includes("pi-agentrouter"))
73
+ );
74
+ const cacheIdx = settings.packages.findIndex((p: string) =>
75
+ typeof p === "string" && p.includes("pi-cache-optimizer")
76
+ );
77
+ if (cacheIdx !== -1 && arIdx !== -1 && arIdx > cacheIdx) {
78
+ const pkg = settings.packages.splice(arIdx, 1)[0];
79
+ const targetCacheIdx = settings.packages.findIndex((p: string) =>
80
+ typeof p === "string" && p.includes("pi-cache-optimizer")
81
+ );
82
+ settings.packages.splice(targetCacheIdx, 0, pkg);
83
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
84
+ return true;
85
+ }
86
+ } catch {}
87
+ return false;
88
+ }
89
+
37
90
  const initialConfig = loadConfig();
38
91
  let currentApiKey = normalizeApiKey(process.env.AGENTROUTER_API_KEY || initialConfig.apiKey || "");
39
92
  let minIntervalMs = initialConfig.minIntervalMs ?? 2500;
40
- let lastRequestTime = 0;
93
+ let lastRequestEndTime = 0;
41
94
 
42
95
  export function isAgentRouter(providerName?: string, baseUrl?: string): boolean {
43
96
  if (providerName && providerName.toLowerCase().includes("agentrouter")) return true;
@@ -120,7 +173,25 @@ export default function (pi: ExtensionAPI) {
120
173
 
121
174
  pi.on("session_start", async (_event, ctx) => {
122
175
  updatePromptRewriteEnvForModel(ctx.model);
123
- lastRequestTime = Date.now();
176
+ lastRequestEndTime = Date.now();
177
+
178
+ const order = getPackageOrderState();
179
+ if (order.needsFix && ctx.hasUI && typeof (ctx.ui as any).confirm === "function") {
180
+ try {
181
+ const confirmed = await (ctx.ui as any).confirm(
182
+ "Pi AgentRouter Package Priority",
183
+ "@madgagarin/pi-agentrouter is listed AFTER pi-cache-optimizer in settings.json packages.\n\n" +
184
+ "It must be placed before pi-cache-optimizer so prompt cache bypass takes effect before cache-optimizer transforms the prompt.\n\n" +
185
+ "Move @madgagarin/pi-agentrouter directly above pi-cache-optimizer in settings.json?"
186
+ );
187
+ if (confirmed) {
188
+ const success = fixPackagePriorityInSettings();
189
+ if (success) {
190
+ ctx.ui.notify("@madgagarin/pi-agentrouter moved above pi-cache-optimizer in settings.json. Please restart Pi for changes to take full effect.", "info");
191
+ }
192
+ }
193
+ } catch {}
194
+ }
124
195
  });
125
196
 
126
197
  pi.on("model_select", async (event) => {
@@ -139,14 +210,26 @@ export default function (pi: ExtensionAPI) {
139
210
  }
140
211
 
141
212
  const now = Date.now();
142
- const elapsed = now - lastRequestTime;
213
+ const elapsed = now - lastRequestEndTime;
143
214
 
144
- if (lastRequestTime > 0 && elapsed < minIntervalMs) {
215
+ if (lastRequestEndTime > 0 && elapsed < minIntervalMs) {
145
216
  const waitMs = minIntervalMs - elapsed;
146
217
  await new Promise((resolve) => setTimeout(resolve, waitMs));
147
218
  }
219
+ });
220
+
221
+ pi.on("turn_end", async (_event, ctx) => {
222
+ const model = ctx.model;
223
+ if (isAgentRouter(model?.provider, (model as any)?.baseUrl)) {
224
+ lastRequestEndTime = Date.now();
225
+ }
226
+ });
148
227
 
149
- lastRequestTime = Date.now();
228
+ pi.on("agent_end", async (_event, ctx) => {
229
+ const model = ctx.model;
230
+ if (isAgentRouter(model?.provider, (model as any)?.baseUrl)) {
231
+ lastRequestEndTime = Date.now();
232
+ }
150
233
  });
151
234
 
152
235
  pi.on("session_before_compact", async (event, ctx) => {
@@ -241,6 +324,27 @@ export default function (pi: ExtensionAPI) {
241
324
  return;
242
325
  }
243
326
 
327
+ if (action === "fix-order" || action === "order") {
328
+ const order = getPackageOrderState();
329
+ if (!order.needsFix) {
330
+ if (order.cacheOptimizerIndex === -1) {
331
+ ctx.ui.notify("pi-cache-optimizer is not installed in settings.json. Package order is optimal.", "info");
332
+ } else if (order.agentRouterIndex !== -1 && order.agentRouterIndex < order.cacheOptimizerIndex) {
333
+ ctx.ui.notify("@madgagarin/pi-agentrouter is already placed before pi-cache-optimizer (Optimal).", "info");
334
+ } else {
335
+ ctx.ui.notify("@madgagarin/pi-agentrouter was not found in settings.json packages.", "warning");
336
+ }
337
+ return;
338
+ }
339
+ const success = fixPackagePriorityInSettings();
340
+ if (success) {
341
+ ctx.ui.notify("@madgagarin/pi-agentrouter moved directly above pi-cache-optimizer in settings.json. Please restart Pi for changes to take full effect.", "info");
342
+ } else {
343
+ ctx.ui.notify("Failed to update settings.json.", "error");
344
+ }
345
+ return;
346
+ }
347
+
244
348
  if (action === "pacing" || action === "throttle") {
245
349
  const val = parseInt(parts[1], 10);
246
350
  if (isNaN(val) || val < 0) {
@@ -256,16 +360,29 @@ export default function (pi: ExtensionAPI) {
256
360
  const maskedKey = currentApiKey.length > 8 ? `${currentApiKey.slice(0, 7)}...${currentApiKey.slice(-4)}` : "not set";
257
361
  const activeModel = ctx.model;
258
362
  const isAR = isAgentRouter(activeModel?.provider, (activeModel as any)?.baseUrl);
363
+ const order = getPackageOrderState();
364
+ let priorityStatus = "Optimal";
365
+ if (order.needsFix) {
366
+ priorityStatus = "Listed AFTER pi-cache-optimizer (Run: /agentrouter fix-order)";
367
+ } else if (order.cacheOptimizerIndex !== -1 && order.agentRouterIndex < order.cacheOptimizerIndex) {
368
+ priorityStatus = "Before pi-cache-optimizer (Optimal)";
369
+ } else if (order.agentRouterIndex !== -1) {
370
+ priorityStatus = "Active (Optimal)";
371
+ } else {
372
+ priorityStatus = "Not in packages";
373
+ }
259
374
 
260
375
  ctx.ui.notify(
261
376
  `[AgentRouter Plugin]\n` +
262
377
  `- Active model: ${activeModel?.id || "none"} (${isAR ? "AgentRouter [yes]" : "Other Provider"})\n` +
378
+ `- Package Priority: ${priorityStatus}\n` +
263
379
  `- API Key: ${maskedKey}\n` +
264
380
  `- Caching: Enabled (Prompt Cache + Session Affinity + Adaptive Thinking)\n` +
265
- `- Pacing Interval: ${minIntervalMs} ms\n` +
381
+ `- Pacing Interval: ${minIntervalMs} ms (Measured from turn completion)\n` +
266
382
  `- Commands:\n` +
267
383
  ` /agentrouter key <key> (update API key)\n` +
268
- ` /agentrouter pacing <ms> (set minimum request delay)`,
384
+ ` /agentrouter pacing <ms> (set request delay after completion)\n` +
385
+ ` /agentrouter fix-order (move plugin above pi-cache-optimizer in settings.json)`,
269
386
  "info"
270
387
  );
271
388
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madgagarin/pi-agentrouter",
3
- "version": "1.1.1",
3
+ "version": "1.2.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"