paseo-acp-agy 1.2.4 → 1.2.5
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/dist/acp-server.js +17 -8
- package/dist/paseo-patcher.d.ts +8 -2
- package/dist/paseo-patcher.js +526 -65
- package/dist/protocol.js +6 -2
- package/dist/session.d.ts +1 -1
- package/dist/session.js +19 -4
- package/package.json +1 -1
package/dist/acp-server.js
CHANGED
|
@@ -85,12 +85,14 @@ export class ACPServer {
|
|
|
85
85
|
}
|
|
86
86
|
publishUsageUpdate(session) {
|
|
87
87
|
const costUsd = roundUsageCostUsd(session.usage.totalCostUsd);
|
|
88
|
+
const maxTokens = session.usage.contextWindowMaxTokens;
|
|
89
|
+
const usedTokens = Math.min(session.usage.contextWindowUsedTokens, maxTokens);
|
|
88
90
|
this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
|
|
89
91
|
sessionId: session.id,
|
|
90
92
|
update: {
|
|
91
93
|
sessionUpdate: "usage_update",
|
|
92
|
-
size:
|
|
93
|
-
used:
|
|
94
|
+
size: maxTokens,
|
|
95
|
+
used: usedTokens,
|
|
94
96
|
cost: { amount: costUsd, currency: "USD" },
|
|
95
97
|
inputTokens: session.usage.inputTokens,
|
|
96
98
|
outputTokens: session.usage.outputTokens,
|
|
@@ -98,8 +100,8 @@ export class ACPServer {
|
|
|
98
100
|
cachedInputTokens: session.usage.cachedInputTokens,
|
|
99
101
|
totalTokens: session.usage.totalTokens,
|
|
100
102
|
totalCostUsd: costUsd,
|
|
101
|
-
contextWindowMaxTokens:
|
|
102
|
-
contextWindowUsedTokens:
|
|
103
|
+
contextWindowMaxTokens: maxTokens,
|
|
104
|
+
contextWindowUsedTokens: usedTokens,
|
|
103
105
|
},
|
|
104
106
|
});
|
|
105
107
|
}
|
|
@@ -138,7 +140,7 @@ export class ACPServer {
|
|
|
138
140
|
const thought = turnUsage?.thinking_tokens;
|
|
139
141
|
const total = turnUsage?.total_tokens ?? (input + output);
|
|
140
142
|
const maxTokens = getModelContextWindow(executingModel || session.model);
|
|
141
|
-
const usedTokens = session.usage.contextWindowUsedTokens;
|
|
143
|
+
const usedTokens = Math.min(session.usage.contextWindowUsedTokens, maxTokens);
|
|
142
144
|
const costUsd = roundUsageCostUsd(session.usage.totalCostUsd);
|
|
143
145
|
return {
|
|
144
146
|
inputTokens: input,
|
|
@@ -356,7 +358,10 @@ export class ACPServer {
|
|
|
356
358
|
break;
|
|
357
359
|
}
|
|
358
360
|
let hasSentNonNarration = false;
|
|
361
|
+
let latestStepContextTokens;
|
|
362
|
+
let stepCount = 0;
|
|
359
363
|
const onStepUpdate = (event) => {
|
|
364
|
+
stepCount++;
|
|
360
365
|
const step = event.step_update;
|
|
361
366
|
if (!step)
|
|
362
367
|
return;
|
|
@@ -437,15 +442,18 @@ export class ACPServer {
|
|
|
437
442
|
}
|
|
438
443
|
}
|
|
439
444
|
if (step.usage) {
|
|
440
|
-
const turnCost = calculateUsageCostUsd(executingModel, step.usage.input_tokens || 0, step.usage.output_tokens || 0, step.usage.cache_read_tokens || 0);
|
|
441
445
|
const inputTokens = step.usage.input_tokens ?? 0;
|
|
442
446
|
const outputTokens = step.usage.output_tokens ?? 0;
|
|
447
|
+
if (inputTokens > 0) {
|
|
448
|
+
latestStepContextTokens = inputTokens + outputTokens;
|
|
449
|
+
}
|
|
450
|
+
const turnCost = calculateUsageCostUsd(executingModel, inputTokens, outputTokens, step.usage.cache_read_tokens || 0);
|
|
443
451
|
const cachedReadTokens = step.usage.cache_read_tokens ?? 0;
|
|
444
452
|
const thoughtTokens = step.usage.thinking_tokens;
|
|
445
453
|
const totalTokens = step.usage.total_tokens ?? (inputTokens + outputTokens);
|
|
446
454
|
const totalCostUsd = roundUsageCostUsd(session.usage.totalCostUsd + turnCost);
|
|
447
455
|
const contextWindowMaxTokens = getModelContextWindow(executingModel);
|
|
448
|
-
const contextWindowUsedTokens = inputTokens + outputTokens;
|
|
456
|
+
const contextWindowUsedTokens = Math.min(inputTokens + outputTokens, contextWindowMaxTokens);
|
|
449
457
|
this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
|
|
450
458
|
sessionId: session.id,
|
|
451
459
|
update: {
|
|
@@ -468,9 +476,10 @@ export class ACPServer {
|
|
|
468
476
|
try {
|
|
469
477
|
const resultEvent = await session.process.sendPrompt(promptText, onStepUpdate);
|
|
470
478
|
const turnUsage = resultEvent.result?.usage;
|
|
479
|
+
const numTurns = resultEvent.result?.num_turns || stepCount || 1;
|
|
471
480
|
// Usage is billable even when the turn terminates with ERROR
|
|
472
481
|
// after model/tool work. Record it before branching on status.
|
|
473
|
-
session.recordTurnUsage(turnUsage, executingModel);
|
|
482
|
+
session.recordTurnUsage(turnUsage, executingModel, latestStepContextTokens, numTurns);
|
|
474
483
|
const usagePayload = this.turnUsagePayload(session, turnUsage, executingModel);
|
|
475
484
|
this.publishUsageUpdate(session);
|
|
476
485
|
if (session.isCancelled) {
|
package/dist/paseo-patcher.d.ts
CHANGED
|
@@ -16,10 +16,16 @@ export declare function findPaseoServerInstallations(): string[];
|
|
|
16
16
|
* injected into Paseo server's quota-fetcher providers.
|
|
17
17
|
*/
|
|
18
18
|
export declare function generateAntigravityQuotaProviderJs(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generates the TypeScript source for the Paseo 0.11+ Plugin Usage Source.
|
|
21
|
+
* This integrates Antigravity quota and balance metrics into Paseo 0.11+'s
|
|
22
|
+
* new Plugin UsageSource architecture (Settings -> Usage).
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateAntigravityPluginUsageTs(): string;
|
|
19
25
|
/**
|
|
20
26
|
* Patches a Paseo server installation directory to enable Antigravity:
|
|
21
|
-
* 1.
|
|
22
|
-
* 2.
|
|
27
|
+
* 1. For Paseo 0.11+: patches builtin-plugins/antigravity-provider with usage source
|
|
28
|
+
* 2. For Paseo <= 0.10: patches quota-fetcher/manifest.js and writes providers/antigravity.js
|
|
23
29
|
* 3. Patches acp-agent.js to map context window tokens and emit usage updates
|
|
24
30
|
*/
|
|
25
31
|
export declare function patchPaseoServer(serverDir: string): {
|
package/dist/paseo-patcher.js
CHANGED
|
@@ -222,6 +222,9 @@ const execFileAsync = promisify(execFile);
|
|
|
222
222
|
let cachedAgyBin = null;
|
|
223
223
|
let cachedAgyBinTime = 0;
|
|
224
224
|
const BIN_CACHE_TTL_MS = 86400000; // 24 hours
|
|
225
|
+
let lastSuccessfulQuota = null;
|
|
226
|
+
let lastSuccessfulQuotaTime = 0;
|
|
227
|
+
const QUOTA_CACHE_TTL_MS = 60000; // 60 seconds
|
|
225
228
|
|
|
226
229
|
function resolveAgyBinary() {
|
|
227
230
|
if (process.env.AGY_BIN_PATH) return process.env.AGY_BIN_PATH;
|
|
@@ -346,6 +349,11 @@ export class AntigravityQuotaProvider {
|
|
|
346
349
|
}
|
|
347
350
|
|
|
348
351
|
async fetchUsage() {
|
|
352
|
+
const now = Date.now();
|
|
353
|
+
if (lastSuccessfulQuota && (now - lastSuccessfulQuotaTime < QUOTA_CACHE_TTL_MS)) {
|
|
354
|
+
return lastSuccessfulQuota;
|
|
355
|
+
}
|
|
356
|
+
|
|
349
357
|
try {
|
|
350
358
|
const isWin = process.platform === "win32";
|
|
351
359
|
const bin = resolveAgyBinary();
|
|
@@ -388,14 +396,23 @@ export class AntigravityQuotaProvider {
|
|
|
388
396
|
env.Path = newPath;
|
|
389
397
|
}
|
|
390
398
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
execFileAsync(bin, ["--print-timeout", "24h", "--print", "/
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
+
let rawUsageOut = "";
|
|
400
|
+
try {
|
|
401
|
+
const usageRes = await execFileAsync(bin, ["--print-timeout", "24h", "--print", "/usage"], {
|
|
402
|
+
timeout: 30000,
|
|
403
|
+
env,
|
|
404
|
+
shell: isBatch,
|
|
405
|
+
windowsHide: true,
|
|
406
|
+
});
|
|
407
|
+
rawUsageOut = usageRes.stdout || usageRes.stderr || "";
|
|
408
|
+
} catch (usageErr) {
|
|
409
|
+
if (lastSuccessfulQuota) {
|
|
410
|
+
if (this.logger && typeof this.logger.warn === "function") {
|
|
411
|
+
this.logger.warn("Transient failure fetching agy /usage; returning cached quota", { error: usageErr.message });
|
|
412
|
+
}
|
|
413
|
+
return lastSuccessfulQuota;
|
|
414
|
+
}
|
|
415
|
+
const msg = usageErr instanceof Error ? usageErr.message : String(usageErr);
|
|
399
416
|
return unavailableUsage({
|
|
400
417
|
providerId: this.providerId,
|
|
401
418
|
displayName: "Antigravity",
|
|
@@ -403,8 +420,16 @@ export class AntigravityQuotaProvider {
|
|
|
403
420
|
});
|
|
404
421
|
}
|
|
405
422
|
|
|
406
|
-
|
|
407
|
-
|
|
423
|
+
let rawCreditsOut = "";
|
|
424
|
+
try {
|
|
425
|
+
const creditsRes = await execFileAsync(bin, ["--print-timeout", "24h", "--print", "/credits"], {
|
|
426
|
+
timeout: 6000,
|
|
427
|
+
env,
|
|
428
|
+
shell: isBatch,
|
|
429
|
+
windowsHide: true,
|
|
430
|
+
});
|
|
431
|
+
rawCreditsOut = creditsRes.stdout || creditsRes.stderr || "";
|
|
432
|
+
} catch {}
|
|
408
433
|
|
|
409
434
|
const usageOut = (rawUsageOut || "").replace(/\\r\\n/g, "\\n");
|
|
410
435
|
const creditsOut = (rawCreditsOut || "").replace(/\\r\\n/g, "\\n");
|
|
@@ -496,7 +521,7 @@ export class AntigravityQuotaProvider {
|
|
|
496
521
|
tone: remainingCredits > 0 ? "ok" : "default",
|
|
497
522
|
});
|
|
498
523
|
|
|
499
|
-
|
|
524
|
+
const result = {
|
|
500
525
|
providerId: this.providerId,
|
|
501
526
|
displayName: "Antigravity",
|
|
502
527
|
status: "available",
|
|
@@ -506,7 +531,12 @@ export class AntigravityQuotaProvider {
|
|
|
506
531
|
details: [],
|
|
507
532
|
error: null,
|
|
508
533
|
};
|
|
534
|
+
|
|
535
|
+
lastSuccessfulQuota = result;
|
|
536
|
+
lastSuccessfulQuotaTime = Date.now();
|
|
537
|
+
return result;
|
|
509
538
|
} catch (err) {
|
|
539
|
+
if (lastSuccessfulQuota) return lastSuccessfulQuota;
|
|
510
540
|
return unavailableUsage({
|
|
511
541
|
providerId: this.providerId,
|
|
512
542
|
displayName: "Antigravity",
|
|
@@ -517,16 +547,387 @@ export class AntigravityQuotaProvider {
|
|
|
517
547
|
}
|
|
518
548
|
`;
|
|
519
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Generates the TypeScript source for the Paseo 0.11+ Plugin Usage Source.
|
|
552
|
+
* This integrates Antigravity quota and balance metrics into Paseo 0.11+'s
|
|
553
|
+
* new Plugin UsageSource architecture (Settings -> Usage).
|
|
554
|
+
*/
|
|
555
|
+
export function generateAntigravityPluginUsageTs() {
|
|
556
|
+
return `import { execFileSync } from "node:child_process";
|
|
557
|
+
import fs from "node:fs";
|
|
558
|
+
import os from "node:os";
|
|
559
|
+
import path from "node:path";
|
|
560
|
+
import { z } from "zod";
|
|
561
|
+
|
|
562
|
+
let cachedAgyBin = null;
|
|
563
|
+
let lastAgyBinCheck = 0;
|
|
564
|
+
const RESOLVE_TTL_MS = 30000;
|
|
565
|
+
let lastSuccessfulPluginQuota = null;
|
|
566
|
+
let lastSuccessfulPluginQuotaTime = 0;
|
|
567
|
+
const PLUGIN_QUOTA_CACHE_TTL_MS = 60000;
|
|
568
|
+
|
|
569
|
+
function resolveAgyBinary() {
|
|
570
|
+
const now = Date.now();
|
|
571
|
+
if (cachedAgyBin && (now - lastAgyBinCheck) < RESOLVE_TTL_MS) {
|
|
572
|
+
return cachedAgyBin;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (process.env.AGY_BIN_PATH && fs.existsSync(process.env.AGY_BIN_PATH)) {
|
|
576
|
+
cachedAgyBin = process.env.AGY_BIN_PATH;
|
|
577
|
+
lastAgyBinCheck = now;
|
|
578
|
+
return cachedAgyBin;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const home = os.homedir();
|
|
582
|
+
const isWin = process.platform === "win32";
|
|
583
|
+
|
|
584
|
+
if (isWin) {
|
|
585
|
+
const localAppData = process.env.LOCALAPPDATA || (home ? path.join(home, "AppData", "Local") : "");
|
|
586
|
+
const programFiles = process.env.ProgramFiles || "C:\\\\Program Files";
|
|
587
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\\\Program Files (x86)";
|
|
588
|
+
|
|
589
|
+
const winCandidates = [
|
|
590
|
+
path.join(localAppData, "Programs", "Antigravity", "bin", "agy.exe"),
|
|
591
|
+
path.join(localAppData, "Programs", "antigravity", "bin", "agy.exe"),
|
|
592
|
+
path.join(localAppData, "Programs", "Antigravity", "agy.exe"),
|
|
593
|
+
path.join(localAppData, "Programs", "antigravity", "agy.exe"),
|
|
594
|
+
path.join(localAppData, "antigravity", "agy.exe"),
|
|
595
|
+
path.join(programFiles, "Antigravity", "bin", "agy.exe"),
|
|
596
|
+
path.join(programFiles, "antigravity", "bin", "agy.exe"),
|
|
597
|
+
path.join(programFiles, "Antigravity", "agy.exe"),
|
|
598
|
+
path.join(programFiles, "antigravity", "agy.exe"),
|
|
599
|
+
path.join(programFilesX86, "Antigravity", "agy.exe"),
|
|
600
|
+
path.join(localAppData, "Microsoft", "WindowsApps", "agy.exe"),
|
|
601
|
+
];
|
|
602
|
+
|
|
603
|
+
for (const cand of winCandidates) {
|
|
604
|
+
if (cand && fs.existsSync(cand)) {
|
|
605
|
+
cachedAgyBin = cand;
|
|
606
|
+
lastAgyBinCheck = now;
|
|
607
|
+
return cand;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
try {
|
|
612
|
+
const out = execFileSync("where.exe", ["agy"], {
|
|
613
|
+
encoding: "utf-8",
|
|
614
|
+
timeout: 3000,
|
|
615
|
+
windowsHide: true,
|
|
616
|
+
});
|
|
617
|
+
const lines = out.split(/\\r?\\n/).map(l => l.trim()).filter(Boolean);
|
|
618
|
+
const exeMatch = lines.find(l => /\\.exe$/i.test(l) && fs.existsSync(l));
|
|
619
|
+
if (exeMatch) {
|
|
620
|
+
cachedAgyBin = exeMatch;
|
|
621
|
+
lastAgyBinCheck = now;
|
|
622
|
+
return exeMatch;
|
|
623
|
+
}
|
|
624
|
+
const scriptMatch = lines.find(l => /\\.(cmd|bat)$/i.test(l) && fs.existsSync(l));
|
|
625
|
+
if (scriptMatch) {
|
|
626
|
+
cachedAgyBin = scriptMatch;
|
|
627
|
+
lastAgyBinCheck = now;
|
|
628
|
+
return scriptMatch;
|
|
629
|
+
}
|
|
630
|
+
if (lines.length > 0 && fs.existsSync(lines[0])) {
|
|
631
|
+
cachedAgyBin = lines[0];
|
|
632
|
+
lastAgyBinCheck = now;
|
|
633
|
+
return lines[0];
|
|
634
|
+
}
|
|
635
|
+
} catch {}
|
|
636
|
+
|
|
637
|
+
cachedAgyBin = "agy.exe";
|
|
638
|
+
lastAgyBinCheck = now;
|
|
639
|
+
return cachedAgyBin;
|
|
640
|
+
} else {
|
|
641
|
+
const posixCandidates = [
|
|
642
|
+
path.join(home, ".local", "bin", "agy"),
|
|
643
|
+
"/usr/local/bin/agy",
|
|
644
|
+
"/usr/bin/agy",
|
|
645
|
+
"/opt/homebrew/bin/agy",
|
|
646
|
+
];
|
|
647
|
+
for (const cand of posixCandidates) {
|
|
648
|
+
if (fs.existsSync(cand)) {
|
|
649
|
+
cachedAgyBin = cand;
|
|
650
|
+
lastAgyBinCheck = now;
|
|
651
|
+
return cand;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
try {
|
|
655
|
+
const out = execFileSync("which", ["agy"], {
|
|
656
|
+
encoding: "utf-8",
|
|
657
|
+
timeout: 3000,
|
|
658
|
+
}).trim();
|
|
659
|
+
if (out && fs.existsSync(out)) {
|
|
660
|
+
cachedAgyBin = out;
|
|
661
|
+
lastAgyBinCheck = now;
|
|
662
|
+
return out;
|
|
663
|
+
}
|
|
664
|
+
} catch {}
|
|
665
|
+
|
|
666
|
+
cachedAgyBin = "agy";
|
|
667
|
+
lastAgyBinCheck = now;
|
|
668
|
+
return cachedAgyBin;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function toneFromUsedPct(usedPct) {
|
|
673
|
+
if (usedPct >= 90) return "critical";
|
|
674
|
+
if (usedPct >= 75) return "warn";
|
|
675
|
+
return "ok";
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export async function fetchAntigravityUsage() {
|
|
679
|
+
const now = Date.now();
|
|
680
|
+
if (lastSuccessfulPluginQuota && (now - lastSuccessfulPluginQuotaTime < PLUGIN_QUOTA_CACHE_TTL_MS)) {
|
|
681
|
+
return lastSuccessfulPluginQuota;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
const bin = resolveAgyBinary();
|
|
686
|
+
const isWin = process.platform === "win32";
|
|
687
|
+
const isBatch = isWin && /\\.(cmd|bat)$/i.test(bin);
|
|
688
|
+
|
|
689
|
+
let usageOut = "";
|
|
690
|
+
try {
|
|
691
|
+
usageOut = execFileSync(bin, ["--print-timeout", "24h", "--print", "/usage"], {
|
|
692
|
+
encoding: "utf-8",
|
|
693
|
+
timeout: 30000,
|
|
694
|
+
windowsHide: true,
|
|
695
|
+
shell: isBatch,
|
|
696
|
+
});
|
|
697
|
+
} catch (usageErr) {
|
|
698
|
+
if (lastSuccessfulPluginQuota) return lastSuccessfulPluginQuota;
|
|
699
|
+
throw usageErr;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
let creditsOut = "";
|
|
703
|
+
try {
|
|
704
|
+
creditsOut = execFileSync(bin, ["--print-timeout", "24h", "--print", "/credits"], {
|
|
705
|
+
encoding: "utf-8",
|
|
706
|
+
timeout: 6000,
|
|
707
|
+
windowsHide: true,
|
|
708
|
+
shell: isBatch,
|
|
709
|
+
});
|
|
710
|
+
} catch {}
|
|
711
|
+
|
|
712
|
+
const normalizedUsage = usageOut.replace(/\\r\\n/g, "\\n");
|
|
713
|
+
const lines = normalizedUsage.split("\\n");
|
|
714
|
+
const rawWindows = [];
|
|
715
|
+
let currentModel = "";
|
|
716
|
+
|
|
717
|
+
for (const rawLine of lines) {
|
|
718
|
+
const line = rawLine.trim();
|
|
719
|
+
if (!line) continue;
|
|
720
|
+
|
|
721
|
+
if (line.endsWith(":") && !line.includes("%")) {
|
|
722
|
+
currentModel = line.slice(0, -1).trim();
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const isGemini = /gemini/i.test(currentModel) || !/claude/i.test(currentModel);
|
|
727
|
+
const match = line.match(/^([A-Za-z0-9\\s_-]+):\\s*(\\d+)%\\s*(?:used)?(?:\\s*\\((?:resets?\\s+in\\s+)?([^)]+)\\))?/i);
|
|
728
|
+
if (match) {
|
|
729
|
+
const limitType = match[1].trim();
|
|
730
|
+
const usedPct = parseInt(match[2], 10);
|
|
731
|
+
const resetStr = match[3] ? match[3].trim() : null;
|
|
732
|
+
|
|
733
|
+
let resetsAt = null;
|
|
734
|
+
if (resetStr) {
|
|
735
|
+
const durMatch = resetStr.match(/(?:(\\d+)h)?\\s*(?:(\\d+)m)?/i);
|
|
736
|
+
if (durMatch && (durMatch[1] || durMatch[2])) {
|
|
737
|
+
const h = parseInt(durMatch[1] || "0", 10);
|
|
738
|
+
const m = parseInt(durMatch[2] || "0", 10);
|
|
739
|
+
resetsAt = new Date(Date.now() + (h * 3600 + m * 60) * 1000).toISOString();
|
|
740
|
+
} else {
|
|
741
|
+
const parsed = Date.parse(resetStr);
|
|
742
|
+
if (!isNaN(parsed)) resetsAt = new Date(parsed).toISOString();
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const isFiveHour = /session|5-?hour|5h/i.test(limitType);
|
|
747
|
+
const isWeekly = /week|7-?day/i.test(limitType);
|
|
748
|
+
|
|
749
|
+
let id = isFiveHour ? "session" : isWeekly ? "weekly" : "quota";
|
|
750
|
+
let label = isFiveHour ? "Session" : isWeekly ? "Weekly" : limitType.replace(/\\s+Remaining$/i, "");
|
|
751
|
+
if (!isGemini) {
|
|
752
|
+
id = \`claude_\${id}\`;
|
|
753
|
+
label = \`Claude \${label}\`;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
rawWindows.push({
|
|
757
|
+
id,
|
|
758
|
+
label,
|
|
759
|
+
shortLabel: isFiveHour ? "5h" : isWeekly ? "wk" : "",
|
|
760
|
+
summary: true,
|
|
761
|
+
usedPct,
|
|
762
|
+
remainingPct: Math.max(0, 100 - usedPct),
|
|
763
|
+
resetsAt,
|
|
764
|
+
tone: toneFromUsedPct(usedPct),
|
|
765
|
+
isFiveHour,
|
|
766
|
+
isGemini,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
rawWindows.sort((a, b) => {
|
|
772
|
+
if (a.isGemini && !b.isGemini) return -1;
|
|
773
|
+
if (!a.isGemini && b.isGemini) return 1;
|
|
774
|
+
if (a.isFiveHour && !b.isFiveHour) return -1;
|
|
775
|
+
if (!a.isFiveHour && b.isFiveHour) return 1;
|
|
776
|
+
return 0;
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
const windows = rawWindows.map(w => ({
|
|
780
|
+
id: w.id,
|
|
781
|
+
label: w.label,
|
|
782
|
+
shortLabel: w.shortLabel,
|
|
783
|
+
summary: w.summary,
|
|
784
|
+
usedPct: w.usedPct,
|
|
785
|
+
remainingPct: w.remainingPct,
|
|
786
|
+
resetsAt: w.resetsAt,
|
|
787
|
+
tone: w.tone,
|
|
788
|
+
}));
|
|
789
|
+
|
|
790
|
+
const balances = [];
|
|
791
|
+
const credMatch = creditsOut.match(/Remaining\\s+credits\\s+([\\d.]+)/i);
|
|
792
|
+
const remainingCredits = credMatch ? parseFloat(credMatch[1]) : 0;
|
|
793
|
+
balances.push({
|
|
794
|
+
id: "credits",
|
|
795
|
+
label: "Credits",
|
|
796
|
+
remaining: remainingCredits,
|
|
797
|
+
unit: "usd",
|
|
798
|
+
tone: remainingCredits > 0 ? "ok" : "default",
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
const result = {
|
|
802
|
+
status: "available",
|
|
803
|
+
planLabel: "Google Gemini",
|
|
804
|
+
windows,
|
|
805
|
+
balances,
|
|
806
|
+
details: [],
|
|
807
|
+
};
|
|
808
|
+
lastSuccessfulPluginQuota = result;
|
|
809
|
+
lastSuccessfulPluginQuotaTime = Date.now();
|
|
810
|
+
return result;
|
|
811
|
+
} catch (err) {
|
|
812
|
+
if (lastSuccessfulPluginQuota) return lastSuccessfulPluginQuota;
|
|
813
|
+
return {
|
|
814
|
+
status: "error",
|
|
815
|
+
error: err instanceof Error ? err.message : String(err),
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
export function registerAntigravityUsageSource(server) {
|
|
821
|
+
if (!server || typeof server.registerUsageSource !== "function") return;
|
|
822
|
+
try {
|
|
823
|
+
server.registerUsageSource({
|
|
824
|
+
id: "antigravity",
|
|
825
|
+
label: "Antigravity",
|
|
826
|
+
icon: "icon.svg",
|
|
827
|
+
input: z.object({}).passthrough(),
|
|
828
|
+
discover: async (scope) => {
|
|
829
|
+
if (scope && scope.kind === "session" && scope.provider && scope.provider !== "antigravity") {
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
return [{ key: "default", label: "Antigravity", input: {} }];
|
|
833
|
+
},
|
|
834
|
+
fetch: async () => {
|
|
835
|
+
return fetchAntigravityUsage();
|
|
836
|
+
},
|
|
837
|
+
});
|
|
838
|
+
} catch (err) {
|
|
839
|
+
console.warn("[antigravity-provider] Failed to register usage source:", err);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
`;
|
|
843
|
+
}
|
|
520
844
|
/**
|
|
521
845
|
* Patches a Paseo server installation directory to enable Antigravity:
|
|
522
|
-
* 1.
|
|
523
|
-
* 2.
|
|
846
|
+
* 1. For Paseo 0.11+: patches builtin-plugins/antigravity-provider with usage source
|
|
847
|
+
* 2. For Paseo <= 0.10: patches quota-fetcher/manifest.js and writes providers/antigravity.js
|
|
524
848
|
* 3. Patches acp-agent.js to map context window tokens and emit usage updates
|
|
525
849
|
*/
|
|
526
850
|
export function patchPaseoServer(serverDir) {
|
|
527
851
|
const changes = [];
|
|
528
852
|
try {
|
|
529
|
-
// 1.
|
|
853
|
+
// 1. Check for Paseo 0.11+ builtin-plugins/antigravity-provider
|
|
854
|
+
const pluginDirCandidates = [
|
|
855
|
+
path.join(serverDir, "dist", "server", "builtin-plugins", "antigravity-provider"),
|
|
856
|
+
path.join(serverDir, "dist", "builtin-plugins", "antigravity-provider"),
|
|
857
|
+
path.join(serverDir, "builtin-plugins", "antigravity-provider"),
|
|
858
|
+
];
|
|
859
|
+
let pluginDir = pluginDirCandidates.find((d) => fs.existsSync(d));
|
|
860
|
+
if (!pluginDir && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
861
|
+
const findPlugin = (dir, depth = 0) => {
|
|
862
|
+
if (depth > 5)
|
|
863
|
+
return null;
|
|
864
|
+
try {
|
|
865
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
866
|
+
for (const e of entries) {
|
|
867
|
+
const full = path.join(dir, e.name);
|
|
868
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
869
|
+
if (e.name === "antigravity-provider")
|
|
870
|
+
return full;
|
|
871
|
+
const found = findPlugin(full, depth + 1);
|
|
872
|
+
if (found)
|
|
873
|
+
return found;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
catch { }
|
|
878
|
+
return null;
|
|
879
|
+
};
|
|
880
|
+
pluginDir = findPlugin(path.join(serverDir, "dist")) || undefined;
|
|
881
|
+
}
|
|
882
|
+
if (pluginDir) {
|
|
883
|
+
const serverSubdir = path.join(pluginDir, "server");
|
|
884
|
+
if (!fs.existsSync(serverSubdir)) {
|
|
885
|
+
fs.mkdirSync(serverSubdir, { recursive: true });
|
|
886
|
+
}
|
|
887
|
+
// Write / update usage.ts
|
|
888
|
+
const usageTsPath = path.join(serverSubdir, "usage.ts");
|
|
889
|
+
fs.writeFileSync(usageTsPath, generateAntigravityPluginUsageTs(), "utf-8");
|
|
890
|
+
changes.push(`Created/Updated ${usageTsPath}`);
|
|
891
|
+
// Locate index.server.ts and/or index.server.js
|
|
892
|
+
const indexCandidates = [
|
|
893
|
+
path.join(pluginDir, "index.server.ts"),
|
|
894
|
+
path.join(pluginDir, "index.server.js"),
|
|
895
|
+
].filter((f) => fs.existsSync(f));
|
|
896
|
+
for (const indexFile of indexCandidates) {
|
|
897
|
+
let indexCode = fs.readFileSync(indexFile, "utf-8");
|
|
898
|
+
let indexModified = false;
|
|
899
|
+
if (!indexCode.includes("registerAntigravityUsageSource")) {
|
|
900
|
+
// Add import
|
|
901
|
+
if (!indexCode.includes('from "./server/usage.js"')) {
|
|
902
|
+
indexCode = `import { registerAntigravityUsageSource } from "./server/usage.js";\n` + indexCode;
|
|
903
|
+
indexModified = true;
|
|
904
|
+
}
|
|
905
|
+
// Inject registration call into contribute function
|
|
906
|
+
const contributeMatch = indexCode.match(/(export\s+default\s+function\s+contribute\s*\([^)]*\)\s*\{)([\s\S]*?)(\})/);
|
|
907
|
+
if (contributeMatch) {
|
|
908
|
+
const before = contributeMatch[1];
|
|
909
|
+
const body = contributeMatch[2];
|
|
910
|
+
const after = contributeMatch[3];
|
|
911
|
+
if (!body.includes("registerAntigravityUsageSource")) {
|
|
912
|
+
const patchedBody = body.replace(/return\s+\(\)\s*=>\s*\{[^}]*\};?/, (m) => `server.registerUsageSource ? registerAntigravityUsageSource(server) : null;\n ${m}`);
|
|
913
|
+
if (patchedBody !== body) {
|
|
914
|
+
indexCode = indexCode.replace(contributeMatch[0], `${before}${patchedBody}${after}`);
|
|
915
|
+
indexModified = true;
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
indexCode = indexCode.replace(contributeMatch[0], `${before}${body}\n server.registerUsageSource ? registerAntigravityUsageSource(server) : null;\n${after}`);
|
|
919
|
+
indexModified = true;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (indexModified) {
|
|
925
|
+
fs.writeFileSync(indexFile, indexCode, "utf-8");
|
|
926
|
+
changes.push(`Patched ${indexFile} with registerAntigravityUsageSource`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
// 2. Check for Paseo <= 0.10 legacy quota-fetcher/manifest.js
|
|
530
931
|
const manifestCandidates = [
|
|
531
932
|
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "manifest.js"),
|
|
532
933
|
path.join(serverDir, "dist", "services", "quota-fetcher", "manifest.js"),
|
|
@@ -580,7 +981,7 @@ export function patchPaseoServer(serverDir) {
|
|
|
580
981
|
changes.push(`Patched ${manifestFile} with AntigravityQuotaProvider`);
|
|
581
982
|
}
|
|
582
983
|
}
|
|
583
|
-
//
|
|
984
|
+
// 3. Locate acp-agent.js
|
|
584
985
|
const acpCandidates = [
|
|
585
986
|
path.join(serverDir, "dist", "server", "server", "agent", "providers", "acp-agent.js"),
|
|
586
987
|
path.join(serverDir, "dist", "server", "agent", "providers", "acp-agent.js"),
|
|
@@ -609,19 +1010,26 @@ export function patchPaseoServer(serverDir) {
|
|
|
609
1010
|
let acpCode = fs.readFileSync(acpFile, "utf-8");
|
|
610
1011
|
let acpModified = false;
|
|
611
1012
|
// Patch mapACPUsage
|
|
612
|
-
if (!acpCode.includes("contextWindowMaxTokens: usage.contextWindowMaxTokens")) {
|
|
1013
|
+
if (!acpCode.includes("contextWindowMaxTokens: usage.contextWindowMaxTokens") || !acpCode.includes("Math.min(usedTokens, maxTokens)")) {
|
|
613
1014
|
const oldMapRegex = /export\s+function\s+mapACPUsage\s*\([^)]*\)\s*\{[\s\S]*?return\s*\{[\s\S]*?\};\s*\}/m;
|
|
614
1015
|
const newMap = `export function mapACPUsage(usage) {
|
|
615
1016
|
if (!usage) {
|
|
616
1017
|
return undefined;
|
|
617
1018
|
}
|
|
1019
|
+
const maxTokens = usage.contextWindowMaxTokens ?? usage.size ?? undefined;
|
|
1020
|
+
let usedTokens = usage.contextWindowUsedTokens ?? usage.used ?? undefined;
|
|
1021
|
+
if (typeof usedTokens === "number" && typeof maxTokens === "number" && maxTokens > 0) {
|
|
1022
|
+
if (usedTokens > maxTokens) {
|
|
1023
|
+
usedTokens = Math.min(usedTokens, maxTokens);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
618
1026
|
return {
|
|
619
1027
|
inputTokens: usage.inputTokens ?? undefined,
|
|
620
1028
|
outputTokens: usage.outputTokens ?? undefined,
|
|
621
1029
|
cachedInputTokens: usage.cachedReadTokens ?? usage.cachedInputTokens ?? undefined,
|
|
622
1030
|
totalCostUsd: usage.totalCostUsd ?? (usage.cost?.amount !== undefined ? Number(usage.cost.amount) : undefined),
|
|
623
1031
|
contextWindowMaxTokens: usage.contextWindowMaxTokens ?? usage.size ?? undefined,
|
|
624
|
-
contextWindowUsedTokens:
|
|
1032
|
+
contextWindowUsedTokens: usedTokens,
|
|
625
1033
|
};
|
|
626
1034
|
}`;
|
|
627
1035
|
if (oldMapRegex.test(acpCode)) {
|
|
@@ -666,7 +1074,7 @@ export function patchPaseoServer(serverDir) {
|
|
|
666
1074
|
}
|
|
667
1075
|
if (depth === 0) {
|
|
668
1076
|
const currentMethodBody = acpCode.slice(startIdx, i);
|
|
669
|
-
const needsPatch = !currentMethodBody.includes(
|
|
1077
|
+
const needsPatch = !currentMethodBody.includes("this.currentTurnUsage =") || currentMethodBody.includes("this.notifySubscribers");
|
|
670
1078
|
if (needsPatch) {
|
|
671
1079
|
const indent = methodMatch[1] || " ";
|
|
672
1080
|
const newHandler = `${indent}handleUsageUpdate(update) {
|
|
@@ -984,43 +1392,15 @@ export function isPaseoServerPatched(serverDir) {
|
|
|
984
1392
|
try {
|
|
985
1393
|
if (!fs.existsSync(serverDir))
|
|
986
1394
|
return false;
|
|
987
|
-
// 1.
|
|
988
|
-
const
|
|
989
|
-
path.join(serverDir, "dist", "server", "
|
|
990
|
-
path.join(serverDir, "dist", "
|
|
1395
|
+
// 1. Check for Paseo 0.11+ builtin-plugins/antigravity-provider
|
|
1396
|
+
const pluginDirCandidates = [
|
|
1397
|
+
path.join(serverDir, "dist", "server", "builtin-plugins", "antigravity-provider"),
|
|
1398
|
+
path.join(serverDir, "dist", "builtin-plugins", "antigravity-provider"),
|
|
1399
|
+
path.join(serverDir, "builtin-plugins", "antigravity-provider"),
|
|
991
1400
|
];
|
|
992
|
-
let
|
|
993
|
-
if (!
|
|
994
|
-
const
|
|
995
|
-
if (depth > 5)
|
|
996
|
-
return false;
|
|
997
|
-
try {
|
|
998
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
999
|
-
for (const e of entries) {
|
|
1000
|
-
if (e.isDirectory() && e.name !== "node_modules") {
|
|
1001
|
-
if (checkRecursive(path.join(dir, e.name), depth + 1))
|
|
1002
|
-
return true;
|
|
1003
|
-
}
|
|
1004
|
-
else if (e.isFile() && e.name === "antigravity.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
1005
|
-
return true;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
catch { }
|
|
1010
|
-
return false;
|
|
1011
|
-
};
|
|
1012
|
-
hasProvider = checkRecursive(path.join(serverDir, "dist"));
|
|
1013
|
-
}
|
|
1014
|
-
if (!hasProvider)
|
|
1015
|
-
return false;
|
|
1016
|
-
// 2. Manifest check
|
|
1017
|
-
const manifestCandidates = [
|
|
1018
|
-
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "manifest.js"),
|
|
1019
|
-
path.join(serverDir, "dist", "services", "quota-fetcher", "manifest.js"),
|
|
1020
|
-
];
|
|
1021
|
-
let manifestFile = manifestCandidates.find((f) => fs.existsSync(f));
|
|
1022
|
-
if (!manifestFile && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
1023
|
-
const findManifest = (dir, depth = 0) => {
|
|
1401
|
+
let pluginDir = pluginDirCandidates.find((d) => fs.existsSync(d));
|
|
1402
|
+
if (!pluginDir && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
1403
|
+
const findPlugin = (dir, depth = 0) => {
|
|
1024
1404
|
if (depth > 5)
|
|
1025
1405
|
return null;
|
|
1026
1406
|
try {
|
|
@@ -1028,27 +1408,104 @@ export function isPaseoServerPatched(serverDir) {
|
|
|
1028
1408
|
for (const e of entries) {
|
|
1029
1409
|
const full = path.join(dir, e.name);
|
|
1030
1410
|
if (e.isDirectory() && e.name !== "node_modules") {
|
|
1031
|
-
|
|
1411
|
+
if (e.name === "antigravity-provider")
|
|
1412
|
+
return full;
|
|
1413
|
+
const found = findPlugin(full, depth + 1);
|
|
1032
1414
|
if (found)
|
|
1033
1415
|
return found;
|
|
1034
1416
|
}
|
|
1035
|
-
else if (e.isFile() && e.name === "manifest.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
1036
|
-
return full;
|
|
1037
|
-
}
|
|
1038
1417
|
}
|
|
1039
1418
|
}
|
|
1040
1419
|
catch { }
|
|
1041
1420
|
return null;
|
|
1042
1421
|
};
|
|
1043
|
-
|
|
1422
|
+
pluginDir = findPlugin(path.join(serverDir, "dist")) || undefined;
|
|
1044
1423
|
}
|
|
1045
|
-
if (
|
|
1046
|
-
|
|
1047
|
-
|
|
1424
|
+
if (pluginDir) {
|
|
1425
|
+
// Paseo 0.11+ architecture
|
|
1426
|
+
const usageFile = [
|
|
1427
|
+
path.join(pluginDir, "server", "usage.ts"),
|
|
1428
|
+
path.join(pluginDir, "server", "usage.js"),
|
|
1429
|
+
].find((f) => fs.existsSync(f));
|
|
1430
|
+
if (!usageFile)
|
|
1431
|
+
return false;
|
|
1432
|
+
const indexFile = [
|
|
1433
|
+
path.join(pluginDir, "index.server.ts"),
|
|
1434
|
+
path.join(pluginDir, "index.server.js"),
|
|
1435
|
+
].find((f) => fs.existsSync(f));
|
|
1436
|
+
if (!indexFile)
|
|
1437
|
+
return false;
|
|
1438
|
+
const indexContent = fs.readFileSync(indexFile, "utf-8");
|
|
1439
|
+
if (!indexContent.includes("registerAntigravityUsageSource"))
|
|
1048
1440
|
return false;
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
// Paseo <= 0.10 legacy architecture
|
|
1444
|
+
const antigravityJsCandidates = [
|
|
1445
|
+
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "providers", "antigravity.js"),
|
|
1446
|
+
path.join(serverDir, "dist", "services", "quota-fetcher", "providers", "antigravity.js"),
|
|
1447
|
+
];
|
|
1448
|
+
let hasProvider = antigravityJsCandidates.some((p) => fs.existsSync(p));
|
|
1449
|
+
if (!hasProvider && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
1450
|
+
const checkRecursive = (dir, depth = 0) => {
|
|
1451
|
+
if (depth > 5)
|
|
1452
|
+
return false;
|
|
1453
|
+
try {
|
|
1454
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1455
|
+
for (const e of entries) {
|
|
1456
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
1457
|
+
if (checkRecursive(path.join(dir, e.name), depth + 1))
|
|
1458
|
+
return true;
|
|
1459
|
+
}
|
|
1460
|
+
else if (e.isFile() && e.name === "antigravity.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
1461
|
+
return true;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
catch { }
|
|
1466
|
+
return false;
|
|
1467
|
+
};
|
|
1468
|
+
hasProvider = checkRecursive(path.join(serverDir, "dist"));
|
|
1469
|
+
}
|
|
1470
|
+
if (!hasProvider)
|
|
1471
|
+
return false;
|
|
1472
|
+
// Manifest check
|
|
1473
|
+
const manifestCandidates = [
|
|
1474
|
+
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "manifest.js"),
|
|
1475
|
+
path.join(serverDir, "dist", "services", "quota-fetcher", "manifest.js"),
|
|
1476
|
+
];
|
|
1477
|
+
let manifestFile = manifestCandidates.find((f) => fs.existsSync(f));
|
|
1478
|
+
if (!manifestFile && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
1479
|
+
const findManifest = (dir, depth = 0) => {
|
|
1480
|
+
if (depth > 5)
|
|
1481
|
+
return null;
|
|
1482
|
+
try {
|
|
1483
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1484
|
+
for (const e of entries) {
|
|
1485
|
+
const full = path.join(dir, e.name);
|
|
1486
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
1487
|
+
const found = findManifest(full, depth + 1);
|
|
1488
|
+
if (found)
|
|
1489
|
+
return found;
|
|
1490
|
+
}
|
|
1491
|
+
else if (e.isFile() && e.name === "manifest.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
1492
|
+
return full;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
catch { }
|
|
1497
|
+
return null;
|
|
1498
|
+
};
|
|
1499
|
+
manifestFile = findManifest(path.join(serverDir, "dist")) || undefined;
|
|
1500
|
+
}
|
|
1501
|
+
if (manifestFile) {
|
|
1502
|
+
const content = fs.readFileSync(manifestFile, "utf-8");
|
|
1503
|
+
if (!content.includes('providerId: "antigravity"')) {
|
|
1504
|
+
return false;
|
|
1505
|
+
}
|
|
1049
1506
|
}
|
|
1050
1507
|
}
|
|
1051
|
-
//
|
|
1508
|
+
// 2. ACP Agent check (common to both architectures)
|
|
1052
1509
|
const acpCandidates = [
|
|
1053
1510
|
path.join(serverDir, "dist", "server", "server", "agent", "providers", "acp-agent.js"),
|
|
1054
1511
|
path.join(serverDir, "dist", "server", "agent", "providers", "acp-agent.js"),
|
|
@@ -1080,7 +1537,7 @@ export function isPaseoServerPatched(serverDir) {
|
|
|
1080
1537
|
}
|
|
1081
1538
|
if (acpFile) {
|
|
1082
1539
|
const content = fs.readFileSync(acpFile, "utf-8");
|
|
1083
|
-
if (!content.includes(
|
|
1540
|
+
if (!content.includes("this.currentTurnUsage =") || !content.includes("contextWindowMaxTokens")) {
|
|
1084
1541
|
return false;
|
|
1085
1542
|
}
|
|
1086
1543
|
}
|
|
@@ -1098,7 +1555,11 @@ export async function isPaseoAsarPatched(asarPath) {
|
|
|
1098
1555
|
if (!fs.existsSync(asarPath))
|
|
1099
1556
|
return false;
|
|
1100
1557
|
const files = listPackage(asarPath);
|
|
1101
|
-
return files.some((f) =>
|
|
1558
|
+
return files.some((f) => {
|
|
1559
|
+
const norm = f.replace(/\\/g, "/");
|
|
1560
|
+
return norm.includes("quota-fetcher/providers/antigravity.js") ||
|
|
1561
|
+
norm.includes("antigravity-provider/server/usage.");
|
|
1562
|
+
});
|
|
1102
1563
|
}
|
|
1103
1564
|
catch {
|
|
1104
1565
|
return false;
|
package/dist/protocol.js
CHANGED
|
@@ -308,14 +308,14 @@ export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
|
308
308
|
try {
|
|
309
309
|
const [usageResult, creditsResult] = await Promise.allSettled([
|
|
310
310
|
execFileAsync(cmd, ["--print", "/usage"], {
|
|
311
|
-
timeout:
|
|
311
|
+
timeout: 30_000,
|
|
312
312
|
maxBuffer: 1024 * 1024,
|
|
313
313
|
env: process.env,
|
|
314
314
|
shell: isBatch,
|
|
315
315
|
windowsHide: true,
|
|
316
316
|
}),
|
|
317
317
|
execFileAsync(cmd, ["--print", "/credits"], {
|
|
318
|
-
timeout:
|
|
318
|
+
timeout: 6_000,
|
|
319
319
|
maxBuffer: 1024 * 1024,
|
|
320
320
|
env: process.env,
|
|
321
321
|
shell: isBatch,
|
|
@@ -331,6 +331,10 @@ export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
|
331
331
|
// /usage is the primary provider-availability probe. allSettled never
|
|
332
332
|
// throws for an exec failure, so rejected results must be handled here.
|
|
333
333
|
if (usageResult.status === "rejected") {
|
|
334
|
+
if (cachedProviderUsage && cachedProviderUsage.status === "available" && cachedProviderUsageBinaryPath === binaryPath) {
|
|
335
|
+
logger.warn("Transient failure in /usage; returning cached provider usage", { error: errorMessage(usageResult.reason) });
|
|
336
|
+
return cachedProviderUsage;
|
|
337
|
+
}
|
|
334
338
|
const error = `Unable to fetch Antigravity quota: ${errorMessage(usageResult.reason)}`;
|
|
335
339
|
logger.warn("Failed to fetch Antigravity provider usage", { error });
|
|
336
340
|
return cacheProviderUsage(binaryPath, {
|
package/dist/session.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare class Session {
|
|
|
47
47
|
output_tokens?: number;
|
|
48
48
|
cache_read_tokens?: number;
|
|
49
49
|
total_tokens?: number;
|
|
50
|
-
}, executingModel?: string): void;
|
|
50
|
+
}, executingModel?: string, latestStepContextTokens?: number, numSteps?: number): void;
|
|
51
51
|
touch(): void;
|
|
52
52
|
cancelTurn(): boolean;
|
|
53
53
|
close(): Promise<void>;
|
package/dist/session.js
CHANGED
|
@@ -146,7 +146,7 @@ export class Session {
|
|
|
146
146
|
throw err;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
recordTurnUsage(turnUsage, executingModel = this.model) {
|
|
149
|
+
recordTurnUsage(turnUsage, executingModel = this.model, latestStepContextTokens, numSteps = 1) {
|
|
150
150
|
if (!turnUsage)
|
|
151
151
|
return;
|
|
152
152
|
const input = turnUsage.input_tokens || 0;
|
|
@@ -159,10 +159,25 @@ export class Session {
|
|
|
159
159
|
this.usage.totalTokens = this.usage.inputTokens + this.usage.outputTokens;
|
|
160
160
|
// Keep the accumulator unrounded. Micro-costs must survive across turns.
|
|
161
161
|
this.usage.totalCostUsd += turnCost;
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
|
|
162
|
+
// Keep the stored meter aligned with the model currently selected for next turn,
|
|
163
|
+
// but evaluate turn occupancy against the model that executed this turn.
|
|
164
|
+
const executingMaxTokens = getModelContextWindow(executingModel);
|
|
165
165
|
this.usage.contextWindowMaxTokens = getModelContextWindow(this.model);
|
|
166
|
+
// Context window represents the active prompt memory buffer of the model.
|
|
167
|
+
// If we observed the latest step's context window size, use it directly.
|
|
168
|
+
// Otherwise, if input + output exceeds executingMaxTokens, it is an accumulation
|
|
169
|
+
// across multiple steps in the turn; derive the single-step context footprint.
|
|
170
|
+
let activeContextTokens = latestStepContextTokens;
|
|
171
|
+
if (!activeContextTokens || activeContextTokens <= 0) {
|
|
172
|
+
const rawTurnTokens = input + output;
|
|
173
|
+
if (rawTurnTokens > executingMaxTokens && numSteps > 1) {
|
|
174
|
+
activeContextTokens = Math.round(rawTurnTokens / numSteps);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
activeContextTokens = rawTurnTokens;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.usage.contextWindowUsedTokens = Math.min(activeContextTokens, executingMaxTokens);
|
|
166
181
|
this.persist();
|
|
167
182
|
}
|
|
168
183
|
touch() {
|