pi-provider-antigravity 0.15.0 → 0.16.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.
- package/README.md +1 -23
- package/dist/index.js +170 -160
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Use Google Antigravity models directly in [Pi](https://pi.dev).
|
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
- Pi >= 0.86.0 (
|
|
25
|
+
- **Pi >= 0.86.0** (Pi 0.86+ introduced breaking changes; older versions are no longer supported. Please upgrade Pi, or pin to `0.13.0` via `pi install npm:pi-provider-antigravity@0.13.0` if using Pi 0.80 ~ 0.85.)
|
|
26
26
|
- A Google account with Antigravity access
|
|
27
27
|
|
|
28
28
|
## Installation
|
|
@@ -31,28 +31,6 @@ Use Google Antigravity models directly in [Pi](https://pi.dev).
|
|
|
31
31
|
pi install npm:pi-provider-antigravity
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Or try it without installing:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pi -e npm:pi-provider-antigravity
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Legacy Pi Support
|
|
41
|
-
|
|
42
|
-
We strongly recommend upgrading to Pi >= 0.86.0 to take advantage of the latest SDK capabilities, streaming reliability improvements, and full feature parity.
|
|
43
|
-
|
|
44
|
-
However, if you are unable to upgrade your Pi installation just yet (for environment or workflow reasons), you can pin to the compatible `0.13.0` release for Pi 0.80.0 ~ 0.85.x:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
pi install npm:pi-provider-antigravity@0.13.0
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Or try it without installing:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
pi -e npm:pi-provider-antigravity@0.13.0
|
|
54
|
-
```
|
|
55
|
-
|
|
56
34
|
## Quick Start
|
|
57
35
|
|
|
58
36
|
1. Authenticate:
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
import { Type } from "@earendil-works/pi-ai";
|
|
3
|
-
|
|
4
1
|
// src/auth.ts
|
|
5
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
6
3
|
|
|
7
4
|
// src/protocol.ts
|
|
8
5
|
var DEFAULT_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
|
|
9
|
-
var DEFAULT_USER_AGENT = "antigravity/cli/1.2.
|
|
6
|
+
var DEFAULT_USER_AGENT = "antigravity/cli/1.2.7 (aidev_client; os_type=linux; arch=amd64; cl=984112147; auth_method=consumer)";
|
|
10
7
|
var PROVIDER_ID = "antigravity";
|
|
11
8
|
var AntigravityHttpError = class extends Error {
|
|
12
9
|
status;
|
|
@@ -787,33 +784,6 @@ function saveSubsystemState(subsystem, state, file = defaultConfigFile()) {
|
|
|
787
784
|
config.states = states;
|
|
788
785
|
});
|
|
789
786
|
}
|
|
790
|
-
var SettingsRegistry = class {
|
|
791
|
-
fields = [];
|
|
792
|
-
register(field) {
|
|
793
|
-
const existingIndex = this.fields.findIndex((f) => f.key === field.key);
|
|
794
|
-
if (existingIndex >= 0) {
|
|
795
|
-
this.fields[existingIndex] = field;
|
|
796
|
-
} else {
|
|
797
|
-
this.fields.push(field);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
unregister(key) {
|
|
801
|
-
this.fields = this.fields.filter((f) => f.key !== key);
|
|
802
|
-
}
|
|
803
|
-
getFields() {
|
|
804
|
-
return [...this.fields];
|
|
805
|
-
}
|
|
806
|
-
clear() {
|
|
807
|
-
this.fields = [];
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
var defaultSettingsRegistry = new SettingsRegistry();
|
|
811
|
-
function registerSettingField(field) {
|
|
812
|
-
defaultSettingsRegistry.register(field);
|
|
813
|
-
}
|
|
814
|
-
function getRegisteredSettingFields() {
|
|
815
|
-
return defaultSettingsRegistry.getFields();
|
|
816
|
-
}
|
|
817
787
|
function fieldDisplayValue(config, field) {
|
|
818
788
|
const raw = config?.settings?.[field.key];
|
|
819
789
|
if (typeof raw === "string" && raw.trim()) return raw.trim();
|
|
@@ -836,7 +806,7 @@ async function applySettingValue(field, value, file, ctx) {
|
|
|
836
806
|
function writeFailedMessage(file) {
|
|
837
807
|
return `Failed to write ${file}. If the file is not valid JSON, fix or delete it.`;
|
|
838
808
|
}
|
|
839
|
-
function buildSettingsItems(config, fields
|
|
809
|
+
function buildSettingsItems(config, fields) {
|
|
840
810
|
return fields.map((f) => ({
|
|
841
811
|
id: f.key,
|
|
842
812
|
label: f.label,
|
|
@@ -859,15 +829,7 @@ async function settingsListTheme(theme) {
|
|
|
859
829
|
};
|
|
860
830
|
}
|
|
861
831
|
}
|
|
862
|
-
function
|
|
863
|
-
if (!registryOrFields) return getRegisteredSettingFields();
|
|
864
|
-
if ("getFields" in registryOrFields && typeof registryOrFields.getFields === "function") {
|
|
865
|
-
return registryOrFields.getFields();
|
|
866
|
-
}
|
|
867
|
-
return registryOrFields;
|
|
868
|
-
}
|
|
869
|
-
async function openSettings(ctx, registryOrFields) {
|
|
870
|
-
const fields = resolveFields(registryOrFields);
|
|
832
|
+
async function openSettings(ctx, fields) {
|
|
871
833
|
const file = defaultConfigFile();
|
|
872
834
|
if (ctx.mode === "tui" && ctx.hasUI) {
|
|
873
835
|
await openSettingsDialog(ctx, fields, file);
|
|
@@ -1007,11 +969,11 @@ function sortQuotaBuckets(buckets) {
|
|
|
1007
969
|
(a, b) => QUOTA_WINDOW_RANK[classifyQuotaWindow(a)] - QUOTA_WINDOW_RANK[classifyQuotaWindow(b)]
|
|
1008
970
|
);
|
|
1009
971
|
}
|
|
1010
|
-
function formatResetRemaining(resetTime) {
|
|
972
|
+
function formatResetRemaining(resetTime, nowFn = Date.now) {
|
|
1011
973
|
if (!resetTime) return null;
|
|
1012
974
|
const ts = Date.parse(resetTime);
|
|
1013
975
|
if (!Number.isFinite(ts)) return null;
|
|
1014
|
-
const delta = ts -
|
|
976
|
+
const delta = ts - nowFn();
|
|
1015
977
|
if (delta <= 0) return "ready";
|
|
1016
978
|
const totalMin = Math.ceil(delta / 6e4);
|
|
1017
979
|
const days = Math.floor(totalMin / (60 * 24));
|
|
@@ -1046,7 +1008,7 @@ function parseQuotaSummary(data) {
|
|
|
1046
1008
|
description: data.description
|
|
1047
1009
|
};
|
|
1048
1010
|
}
|
|
1049
|
-
function formatQuotaSummary(summary) {
|
|
1011
|
+
function formatQuotaSummary(summary, nowFn = Date.now) {
|
|
1050
1012
|
const lines = [];
|
|
1051
1013
|
for (const group of summary.groups) {
|
|
1052
1014
|
if (lines.length > 0) lines.push("");
|
|
@@ -1054,7 +1016,7 @@ function formatQuotaSummary(summary) {
|
|
|
1054
1016
|
for (const b of sortQuotaBuckets(group.buckets)) {
|
|
1055
1017
|
const pct = Math.round(b.remainingFraction * 100);
|
|
1056
1018
|
const label = shortWindowLabel(b, classifyQuotaWindow(b)).padEnd(3);
|
|
1057
|
-
const reset = formatResetRemaining(b.resetTime);
|
|
1019
|
+
const reset = formatResetRemaining(b.resetTime, nowFn);
|
|
1058
1020
|
const suffix = reset && pct < 100 ? ` (${reset})` : "";
|
|
1059
1021
|
lines.push(
|
|
1060
1022
|
` ${label} ${renderProgressBar(b.remainingFraction)} ${String(pct).padStart(3)}% left${suffix}`
|
|
@@ -1127,22 +1089,22 @@ function selectUrgentBucket(group, weeklyTo5hRatio) {
|
|
|
1127
1089
|
}
|
|
1128
1090
|
return worst;
|
|
1129
1091
|
}
|
|
1130
|
-
function buildQuotaFooter(summary, modelId, weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO) {
|
|
1092
|
+
function buildQuotaFooter(summary, modelId, weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO, nowFn = Date.now) {
|
|
1131
1093
|
const group = selectQuotaGroup(summary.groups, modelId);
|
|
1132
1094
|
if (!group) return void 0;
|
|
1133
1095
|
const urgent = selectUrgentBucket(group, weeklyTo5hRatio);
|
|
1134
|
-
return urgent ? formatQuotaWindowPart(urgent) : void 0;
|
|
1096
|
+
return urgent ? formatQuotaWindowPart(urgent, nowFn) : void 0;
|
|
1135
1097
|
}
|
|
1136
|
-
function buildQuotaFooterBoth(summary, modelId) {
|
|
1098
|
+
function buildQuotaFooterBoth(summary, modelId, nowFn = Date.now) {
|
|
1137
1099
|
const group = selectQuotaGroup(summary.groups, modelId);
|
|
1138
1100
|
if (!group || group.buckets.length === 0) return void 0;
|
|
1139
|
-
return sortQuotaBuckets(group.buckets).map(formatQuotaWindowPart).join(" \xB7 ");
|
|
1101
|
+
return sortQuotaBuckets(group.buckets).map((b) => formatQuotaWindowPart(b, nowFn)).join(" \xB7 ");
|
|
1140
1102
|
}
|
|
1141
|
-
function formatQuotaWindowPart(bucket) {
|
|
1103
|
+
function formatQuotaWindowPart(bucket, nowFn = Date.now) {
|
|
1142
1104
|
const kind = classifyQuotaWindow(bucket);
|
|
1143
1105
|
const prefix = kind === "5h" ? "5h" : kind === "weekly" ? "Wk" : bucket.displayName || bucket.bucketId;
|
|
1144
1106
|
const pct = Math.round(bucket.remainingFraction * 100);
|
|
1145
|
-
const short = formatResetRemaining(bucket.resetTime)?.replace(/^in /, "");
|
|
1107
|
+
const short = formatResetRemaining(bucket.resetTime, nowFn)?.replace(/^in /, "");
|
|
1146
1108
|
if (short && pct < 100) return `${prefix} ${pct}% (${short})`;
|
|
1147
1109
|
return `${prefix} ${pct}%`;
|
|
1148
1110
|
}
|
|
@@ -1202,25 +1164,22 @@ function createQuotaFooterField(quotaStatus) {
|
|
|
1202
1164
|
options: FOOTER_MODE_OPTIONS,
|
|
1203
1165
|
defaultValue: "off",
|
|
1204
1166
|
optionNotes: FOOTER_MODE_NOTES,
|
|
1205
|
-
renderPreview: (mode, ctx, theme) => {
|
|
1167
|
+
renderPreview: quotaStatus ? (mode, ctx, theme) => {
|
|
1206
1168
|
if (mode === "off") return "hidden";
|
|
1207
1169
|
const activeTheme = theme || ctx.ui?.theme;
|
|
1208
1170
|
return previewQuotaFooterText(quotaStatus, ctx.model?.id, mode, activeTheme);
|
|
1209
|
-
},
|
|
1210
|
-
prepare: async (ctx) => {
|
|
1171
|
+
} : void 0,
|
|
1172
|
+
prepare: quotaStatus ? async (ctx) => {
|
|
1211
1173
|
await quotaStatus.ensurePreview(ctx);
|
|
1212
|
-
},
|
|
1213
|
-
onChange: async (ctx, _value) => {
|
|
1174
|
+
} : void 0,
|
|
1175
|
+
onChange: quotaStatus ? async (ctx, _value) => {
|
|
1214
1176
|
if (quotaStatus.mode() !== "off") {
|
|
1215
1177
|
await quotaStatus.refresh(ctx, { ignoreMode: true });
|
|
1216
1178
|
}
|
|
1217
1179
|
quotaStatus.paint(ctx);
|
|
1218
|
-
}
|
|
1180
|
+
} : void 0
|
|
1219
1181
|
};
|
|
1220
1182
|
}
|
|
1221
|
-
function registerQuotaSettings(quotaStatus) {
|
|
1222
|
-
registerSettingField(createQuotaFooterField(quotaStatus));
|
|
1223
|
-
}
|
|
1224
1183
|
function isFiniteNumber(value) {
|
|
1225
1184
|
return typeof value === "number" && Number.isFinite(value);
|
|
1226
1185
|
}
|
|
@@ -1251,18 +1210,18 @@ var FOOTER_MODES = Object.freeze({
|
|
|
1251
1210
|
smart: {
|
|
1252
1211
|
key: "smart",
|
|
1253
1212
|
note: FOOTER_MODE_NOTES.smart,
|
|
1254
|
-
render: (summary, modelId, ratio = DEFAULT_WEEKLY_TO_5H_RATIO, style) => {
|
|
1213
|
+
render: (summary, modelId, ratio = DEFAULT_WEEKLY_TO_5H_RATIO, style, nowFn = Date.now) => {
|
|
1255
1214
|
if (!summary) return {};
|
|
1256
|
-
const plain = buildQuotaFooter(summary, modelId, ratio);
|
|
1215
|
+
const plain = buildQuotaFooter(summary, modelId, ratio, nowFn);
|
|
1257
1216
|
return { plain, colored: colorizeQuotaFooter(plain, style) };
|
|
1258
1217
|
}
|
|
1259
1218
|
},
|
|
1260
1219
|
all: {
|
|
1261
1220
|
key: "all",
|
|
1262
1221
|
note: FOOTER_MODE_NOTES.all,
|
|
1263
|
-
render: (summary, modelId, _ratio, style) => {
|
|
1222
|
+
render: (summary, modelId, _ratio, style, nowFn = Date.now) => {
|
|
1264
1223
|
if (!summary) return {};
|
|
1265
|
-
const plain = buildQuotaFooterBoth(summary, modelId);
|
|
1224
|
+
const plain = buildQuotaFooterBoth(summary, modelId, nowFn);
|
|
1266
1225
|
return { plain, colored: colorizeQuotaFooterBoth(plain, style) };
|
|
1267
1226
|
}
|
|
1268
1227
|
}
|
|
@@ -1277,11 +1236,19 @@ var QuotaStatusCoordinator = class {
|
|
|
1277
1236
|
weeklyTo5hRatio;
|
|
1278
1237
|
lastPairs;
|
|
1279
1238
|
store;
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1239
|
+
fetchSummaryOverride;
|
|
1240
|
+
nowFn;
|
|
1241
|
+
constructor(depsOrStoreOrFile) {
|
|
1242
|
+
if (typeof depsOrStoreOrFile === "string" || !depsOrStoreOrFile) {
|
|
1243
|
+
this.store = fileQuotaStatusStore(depsOrStoreOrFile || defaultConfigFile());
|
|
1244
|
+
this.nowFn = Date.now;
|
|
1245
|
+
} else if ("loadMode" in depsOrStoreOrFile) {
|
|
1246
|
+
this.store = depsOrStoreOrFile;
|
|
1247
|
+
this.nowFn = Date.now;
|
|
1283
1248
|
} else {
|
|
1284
|
-
this.store =
|
|
1249
|
+
this.store = depsOrStoreOrFile.store ?? fileQuotaStatusStore(depsOrStoreOrFile.configFile || defaultConfigFile());
|
|
1250
|
+
this.fetchSummaryOverride = depsOrStoreOrFile.fetchQuotaSummary;
|
|
1251
|
+
this.nowFn = depsOrStoreOrFile.now ?? Date.now;
|
|
1285
1252
|
}
|
|
1286
1253
|
this.weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO;
|
|
1287
1254
|
this.loadPersisted();
|
|
@@ -1294,13 +1261,13 @@ var QuotaStatusCoordinator = class {
|
|
|
1294
1261
|
return this.store.loadMode();
|
|
1295
1262
|
}
|
|
1296
1263
|
footerFor(modelId, mode = "smart") {
|
|
1297
|
-
return FOOTER_MODES[mode]?.render(this.summary, modelId, this.weeklyTo5hRatio).plain;
|
|
1264
|
+
return FOOTER_MODES[mode]?.render(this.summary, modelId, this.weeklyTo5hRatio, void 0, this.nowFn).plain;
|
|
1298
1265
|
}
|
|
1299
1266
|
renderFooter(modelId, mode = "smart", style) {
|
|
1300
|
-
return FOOTER_MODES[mode]?.render(this.summary, modelId, this.weeklyTo5hRatio, style) ?? {};
|
|
1267
|
+
return FOOTER_MODES[mode]?.render(this.summary, modelId, this.weeklyTo5hRatio, style, this.nowFn) ?? {};
|
|
1301
1268
|
}
|
|
1302
1269
|
get isFresh() {
|
|
1303
|
-
return !!this.summary &&
|
|
1270
|
+
return !!this.summary && this.nowFn() - this.fetchedAt < QUOTA_REFRESH_INTERVAL_MS;
|
|
1304
1271
|
}
|
|
1305
1272
|
refresh(ctx, opts = {}) {
|
|
1306
1273
|
if (!isLiveCtx(ctx)) return Promise.resolve(void 0);
|
|
@@ -1339,7 +1306,10 @@ var QuotaStatusCoordinator = class {
|
|
|
1339
1306
|
throw new Error("Failed to fetch usage.");
|
|
1340
1307
|
}
|
|
1341
1308
|
this.paint(ctx);
|
|
1342
|
-
return formatQuotaSummary(summary);
|
|
1309
|
+
return formatQuotaSummary(summary, this.nowFn);
|
|
1310
|
+
}
|
|
1311
|
+
createSettingsField() {
|
|
1312
|
+
return createQuotaFooterField(this);
|
|
1343
1313
|
}
|
|
1344
1314
|
// One fetch for preview purposes even when the footer slot is off or the
|
|
1345
1315
|
// current model belongs to another provider: opening quota settings is an
|
|
@@ -1360,22 +1330,29 @@ var QuotaStatusCoordinator = class {
|
|
|
1360
1330
|
try {
|
|
1361
1331
|
const creds = await resolveCredentials(ctx);
|
|
1362
1332
|
if (!creds) return void 0;
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1333
|
+
let rawSummary;
|
|
1334
|
+
if (this.fetchSummaryOverride) {
|
|
1335
|
+
rawSummary = await this.fetchSummaryOverride(creds.token, signal);
|
|
1336
|
+
} else {
|
|
1337
|
+
rawSummary = await postAntigravityJson({
|
|
1338
|
+
auth: creds,
|
|
1339
|
+
path: "v1internal:retrieveUserQuotaSummary",
|
|
1340
|
+
body: { project: creds.projectId },
|
|
1341
|
+
signal
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
if (!rawSummary) return void 0;
|
|
1345
|
+
const summary = rawSummary.groups ? parseQuotaSummary(rawSummary) : rawSummary;
|
|
1370
1346
|
this.ingest(summary);
|
|
1371
1347
|
return summary;
|
|
1372
1348
|
} catch {
|
|
1373
1349
|
return void 0;
|
|
1374
1350
|
}
|
|
1375
1351
|
}
|
|
1376
|
-
// Accepts an externally fetched summary into the shared cache: same
|
|
1352
|
+
// Accepts an externally fetched summary or raw JSON into the shared cache: same
|
|
1377
1353
|
// calibration, persistence, and freshness as fetch().
|
|
1378
|
-
ingest(
|
|
1354
|
+
ingest(summaryOrRaw) {
|
|
1355
|
+
const summary = summaryOrRaw && summaryOrRaw.groups && typeof summaryOrRaw.groups[0]?.buckets?.[0]?.remainingFraction === "number" ? parseQuotaSummary(summaryOrRaw) : summaryOrRaw;
|
|
1379
1356
|
const calibrated = calibrateWeeklyTo5hRatio(
|
|
1380
1357
|
this.lastPairs,
|
|
1381
1358
|
extractWindowFractionPairs(summary),
|
|
@@ -1384,7 +1361,7 @@ var QuotaStatusCoordinator = class {
|
|
|
1384
1361
|
this.weeklyTo5hRatio = calibrated;
|
|
1385
1362
|
this.lastPairs = extractWindowFractionPairs(summary);
|
|
1386
1363
|
this.summary = summary;
|
|
1387
|
-
this.fetchedAt =
|
|
1364
|
+
this.fetchedAt = this.nowFn();
|
|
1388
1365
|
this.saveState();
|
|
1389
1366
|
}
|
|
1390
1367
|
loadPersisted() {
|
|
@@ -1394,7 +1371,7 @@ var QuotaStatusCoordinator = class {
|
|
|
1394
1371
|
this.weeklyTo5hRatio = ratio;
|
|
1395
1372
|
}
|
|
1396
1373
|
const at = entry?.updatedAt;
|
|
1397
|
-
this.lastPairs = typeof at === "number" &&
|
|
1374
|
+
this.lastPairs = typeof at === "number" && this.nowFn() - at <= MAX_OBSERVATION_AGE_MS ? validPairs(entry?.previousObservation) : void 0;
|
|
1398
1375
|
}
|
|
1399
1376
|
// Persists calibration state through the injected store. Best-effort: a
|
|
1400
1377
|
// stale ratio is still usable.
|
|
@@ -1403,12 +1380,15 @@ var QuotaStatusCoordinator = class {
|
|
|
1403
1380
|
this.store.saveQuotaState({
|
|
1404
1381
|
weeklyTo5hRatio: this.weeklyTo5hRatio,
|
|
1405
1382
|
previousObservation: this.lastPairs ?? {},
|
|
1406
|
-
updatedAt:
|
|
1383
|
+
updatedAt: this.nowFn()
|
|
1407
1384
|
});
|
|
1408
1385
|
} catch {
|
|
1409
1386
|
}
|
|
1410
1387
|
}
|
|
1411
1388
|
};
|
|
1389
|
+
function createQuotaStatus(deps) {
|
|
1390
|
+
return new QuotaStatusCoordinator(deps);
|
|
1391
|
+
}
|
|
1412
1392
|
|
|
1413
1393
|
// src/stream.ts
|
|
1414
1394
|
import {
|
|
@@ -1808,6 +1788,11 @@ function attachLoneSignature(content, lastThoughtSignature) {
|
|
|
1808
1788
|
}
|
|
1809
1789
|
content.push({ type: "text", text: "", textSignature: lastThoughtSignature });
|
|
1810
1790
|
}
|
|
1791
|
+
function hasAnswerContent(content) {
|
|
1792
|
+
return content.some(
|
|
1793
|
+
(block) => block.type === "toolCall" ? true : block.type === "text" ? block.text.trim().length > 0 : false
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1811
1796
|
function closeOpenBlock(state, onEvent) {
|
|
1812
1797
|
if (state.openType === null) return;
|
|
1813
1798
|
const contentIndex = state.content.length - 1;
|
|
@@ -2025,13 +2010,14 @@ function streamAntigravity(model, context, options, catalog) {
|
|
|
2025
2010
|
const effort = options?.reasoning;
|
|
2026
2011
|
const trajectoryId = options && "trajectoryId" in options && typeof options.trajectoryId === "string" ? options.trajectoryId : void 0;
|
|
2027
2012
|
const plan = catalog.resolvePlan(model.id, effort);
|
|
2013
|
+
const maxOutputTokens = options?.maxTokens ?? model.maxTokens;
|
|
2028
2014
|
let requestBody = buildAntigravityRequestBody({
|
|
2029
2015
|
projectId,
|
|
2030
2016
|
plan,
|
|
2031
2017
|
context,
|
|
2032
2018
|
sessionId: options?.sessionId,
|
|
2033
2019
|
trajectoryId,
|
|
2034
|
-
maxOutputTokens
|
|
2020
|
+
maxOutputTokens,
|
|
2035
2021
|
toolChoice: options?.toolChoice
|
|
2036
2022
|
});
|
|
2037
2023
|
if (options?.onPayload) {
|
|
@@ -2079,6 +2065,19 @@ function streamAntigravity(model, context, options, catalog) {
|
|
|
2079
2065
|
output.rawStopReason ? `Provider stopped with: ${output.rawStopReason}` : output.errorMessage || "An unknown error occurred"
|
|
2080
2066
|
);
|
|
2081
2067
|
}
|
|
2068
|
+
if (output.stopReason === "stop" && !hasAnswerContent(output.content)) {
|
|
2069
|
+
const reasoningTokens = output.usage.reasoning ?? 0;
|
|
2070
|
+
const budgetThreshold = Math.max(1, Math.floor(maxOutputTokens * 0.9));
|
|
2071
|
+
const isDeliberatedToBudget = reasoningTokens >= budgetThreshold;
|
|
2072
|
+
if (isDeliberatedToBudget) {
|
|
2073
|
+
throw new Error(
|
|
2074
|
+
`Antigravity returned no answer: the turn ended after thinking only (reasoning ${reasoningTokens} of ${maxOutputTokens} max output tokens). Lower the thinking level or prompt again.`
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
`Antigravity returned an empty response with no answer (reasoning ${reasoningTokens} of ${maxOutputTokens} max output tokens). Please retry your request.`
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2082
2081
|
const doneReason = output.stopReason === "toolUse" || output.stopReason === "length" ? output.stopReason : "stop";
|
|
2083
2082
|
stream.push({
|
|
2084
2083
|
type: "done",
|
|
@@ -2098,8 +2097,10 @@ function streamAntigravity(model, context, options, catalog) {
|
|
|
2098
2097
|
}
|
|
2099
2098
|
|
|
2100
2099
|
// src/search.ts
|
|
2101
|
-
|
|
2100
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
2101
|
+
var WEB_SEARCH_TOOL_NAME = "antigravity_websearch";
|
|
2102
2102
|
var SEARCH_MODEL = "gemini-3.1-flash-lite";
|
|
2103
|
+
var SEARCH_SYSTEM_INSTRUCTION = "You are a search engine bot. You will be given a query from a user. Your task is to search the web for relevant information that will help the user. You MUST perform a web search. Do not respond or interact with the user, please respond as if they typed the query into a search bar.";
|
|
2103
2104
|
function formatSearchResults(rawText, metadata, query, createdAt, completedAt) {
|
|
2104
2105
|
const sources = (metadata?.groundingChunks || []).map((chunk, i) => ({
|
|
2105
2106
|
index: i + 1,
|
|
@@ -2192,6 +2193,84 @@ async function performWebSearch(creds, options) {
|
|
|
2192
2193
|
const metadata = candidate?.groundingMetadata;
|
|
2193
2194
|
return formatSearchResults(rawText, metadata, effectiveQuery, createdAt, completedAt);
|
|
2194
2195
|
}
|
|
2196
|
+
function notifyUser(ctx, text, type = "info") {
|
|
2197
|
+
if (ctx.hasUI) {
|
|
2198
|
+
ctx.ui.notify(text, type);
|
|
2199
|
+
} else {
|
|
2200
|
+
if (type === "error" || type === "warning") console.error(text);
|
|
2201
|
+
else console.log(text);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
function registerWebSearchTool(pi) {
|
|
2205
|
+
if (typeof pi.registerTool !== "function") return;
|
|
2206
|
+
pi.registerTool({
|
|
2207
|
+
name: WEB_SEARCH_TOOL_NAME,
|
|
2208
|
+
label: "Antigravity Web Search",
|
|
2209
|
+
description: "Performs a web search for a given query. Returns a summary of relevant information along with URL citations.",
|
|
2210
|
+
promptSnippet: "Search the web using Google Search Grounding with source citations",
|
|
2211
|
+
promptGuidelines: [
|
|
2212
|
+
"Use antigravity_websearch when you need real-time web search, latest documentation, or up-to-date facts with verified citations."
|
|
2213
|
+
],
|
|
2214
|
+
parameters: Type.Object({
|
|
2215
|
+
query: Type.String({ description: "The search query string to look up on the web." }),
|
|
2216
|
+
domain: Type.Optional(
|
|
2217
|
+
Type.String({
|
|
2218
|
+
description: "Optional domain to recommend the search prioritize (e.g. 'github.com', 'developer.mozilla.org')."
|
|
2219
|
+
})
|
|
2220
|
+
)
|
|
2221
|
+
}),
|
|
2222
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
2223
|
+
const creds = await resolveCredentials(ctx);
|
|
2224
|
+
if (!creds) {
|
|
2225
|
+
return {
|
|
2226
|
+
content: [{ type: "text", text: `Error: ${NOT_LOGGED_IN}` }],
|
|
2227
|
+
details: { error: "not_logged_in" }
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
try {
|
|
2231
|
+
const result = await performWebSearch(creds, {
|
|
2232
|
+
query: params.query,
|
|
2233
|
+
domain: params.domain,
|
|
2234
|
+
signal
|
|
2235
|
+
});
|
|
2236
|
+
return {
|
|
2237
|
+
content: [{ type: "text", text: result.formattedOutput }],
|
|
2238
|
+
details: {
|
|
2239
|
+
sources: result.sources,
|
|
2240
|
+
queries: result.queries
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
} catch (err) {
|
|
2244
|
+
return {
|
|
2245
|
+
content: [{ type: "text", text: `Search failed: ${err.message}` }],
|
|
2246
|
+
details: { error: err.message }
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
async function executeWebSearchCommand(ctx, query) {
|
|
2253
|
+
const creds = await resolveCredentials(ctx);
|
|
2254
|
+
if (!creds) {
|
|
2255
|
+
notifyUser(ctx, NOT_LOGGED_IN, "warning");
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
const trimmedQuery = (query || "").trim();
|
|
2259
|
+
if (!trimmedQuery) {
|
|
2260
|
+
notifyUser(ctx, "Usage: /antigravity websearch <query>", "warning");
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
try {
|
|
2264
|
+
if (ctx.hasUI) ctx.ui.notify(`Searching: "${trimmedQuery}"\u2026`, "info");
|
|
2265
|
+
const result = await performWebSearch(creds, {
|
|
2266
|
+
query: trimmedQuery,
|
|
2267
|
+
signal: ctx.signal
|
|
2268
|
+
});
|
|
2269
|
+
notifyUser(ctx, result.formattedOutput);
|
|
2270
|
+
} catch (err) {
|
|
2271
|
+
notifyUser(ctx, `Search failed: ${err.message}`, "error");
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2195
2274
|
|
|
2196
2275
|
// src/commands.ts
|
|
2197
2276
|
function emitOutput(ctx, text, type = "info") {
|
|
@@ -2224,14 +2303,14 @@ var SUBCOMMANDS = Object.freeze([
|
|
|
2224
2303
|
description: "Configure provider settings",
|
|
2225
2304
|
aliases: Object.freeze(["setting"]),
|
|
2226
2305
|
run: async ({ ctx, quotaStatus }) => {
|
|
2227
|
-
|
|
2228
|
-
await openSettings(ctx);
|
|
2306
|
+
const qs = quotaStatus ?? createQuotaStatus();
|
|
2307
|
+
await openSettings(ctx, [qs.createSettingsField()]);
|
|
2229
2308
|
}
|
|
2230
2309
|
},
|
|
2231
2310
|
{
|
|
2232
2311
|
name: "websearch",
|
|
2233
2312
|
description: "Search the web using Google Search Grounding",
|
|
2234
|
-
run: async ({ ctx, subArgs }) =>
|
|
2313
|
+
run: async ({ ctx, subArgs }) => executeWebSearchCommand(ctx, subArgs)
|
|
2235
2314
|
},
|
|
2236
2315
|
{
|
|
2237
2316
|
name: "login",
|
|
@@ -2338,28 +2417,6 @@ function runLoginSubcommand(ctx) {
|
|
|
2338
2417
|
emitOutput(ctx, "Please run /login antigravity to authenticate.");
|
|
2339
2418
|
}
|
|
2340
2419
|
}
|
|
2341
|
-
async function runSearchSubcommand(ctx, query) {
|
|
2342
|
-
const creds = await resolveCredentials(ctx);
|
|
2343
|
-
if (!creds) {
|
|
2344
|
-
emitOutput(ctx, NOT_LOGGED_IN, "warning");
|
|
2345
|
-
return;
|
|
2346
|
-
}
|
|
2347
|
-
const trimmedQuery = (query || "").trim();
|
|
2348
|
-
if (!trimmedQuery) {
|
|
2349
|
-
emitOutput(ctx, "Usage: /antigravity websearch <query>", "warning");
|
|
2350
|
-
return;
|
|
2351
|
-
}
|
|
2352
|
-
try {
|
|
2353
|
-
if (ctx.hasUI) ctx.ui.notify(`Searching: "${trimmedQuery}"\u2026`, "info");
|
|
2354
|
-
const result = await performWebSearch(creds, {
|
|
2355
|
-
query: trimmedQuery,
|
|
2356
|
-
signal: ctx.signal
|
|
2357
|
-
});
|
|
2358
|
-
emitOutput(ctx, result.formattedOutput);
|
|
2359
|
-
} catch (err) {
|
|
2360
|
-
emitOutput(ctx, `Search failed: ${err.message}`, "error");
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
2420
|
async function runAntigravitySubcommand(args, ctx, quotaStatus, catalog) {
|
|
2364
2421
|
const raw = (args || "").trim();
|
|
2365
2422
|
const parts = raw.split(/\s+/).filter(Boolean);
|
|
@@ -2376,8 +2433,7 @@ async function runAntigravitySubcommand(args, ctx, quotaStatus, catalog) {
|
|
|
2376
2433
|
// src/index.ts
|
|
2377
2434
|
var PROVIDER_NAME = "Antigravity";
|
|
2378
2435
|
function index_default(pi) {
|
|
2379
|
-
const quotaStatus =
|
|
2380
|
-
registerQuotaSettings(quotaStatus);
|
|
2436
|
+
const quotaStatus = createQuotaStatus();
|
|
2381
2437
|
const catalog = createModelCatalog();
|
|
2382
2438
|
pi.registerProvider(PROVIDER_ID, {
|
|
2383
2439
|
name: PROVIDER_NAME,
|
|
@@ -2402,53 +2458,7 @@ function index_default(pi) {
|
|
|
2402
2458
|
await runAntigravitySubcommand(args, ctx, quotaStatus, catalog);
|
|
2403
2459
|
}
|
|
2404
2460
|
});
|
|
2405
|
-
|
|
2406
|
-
pi.registerTool({
|
|
2407
|
-
name: "antigravity_websearch",
|
|
2408
|
-
label: "Antigravity Web Search",
|
|
2409
|
-
description: "Performs a web search for a given query. Returns a summary of relevant information along with URL citations.",
|
|
2410
|
-
promptSnippet: "Search the web using Google Search Grounding with source citations",
|
|
2411
|
-
promptGuidelines: [
|
|
2412
|
-
"Use antigravity_websearch when you need real-time web search, latest documentation, or up-to-date facts with verified citations."
|
|
2413
|
-
],
|
|
2414
|
-
parameters: Type.Object({
|
|
2415
|
-
query: Type.String({ description: "The search query string to look up on the web." }),
|
|
2416
|
-
domain: Type.Optional(
|
|
2417
|
-
Type.String({
|
|
2418
|
-
description: "Optional domain to recommend the search prioritize (e.g. 'github.com', 'developer.mozilla.org')."
|
|
2419
|
-
})
|
|
2420
|
-
)
|
|
2421
|
-
}),
|
|
2422
|
-
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
2423
|
-
const creds = await resolveCredentials(ctx);
|
|
2424
|
-
if (!creds) {
|
|
2425
|
-
return {
|
|
2426
|
-
content: [{ type: "text", text: "Error: Not logged in. Run /login antigravity first." }],
|
|
2427
|
-
details: { error: "not_logged_in" }
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
try {
|
|
2431
|
-
const result = await performWebSearch(creds, {
|
|
2432
|
-
query: params.query,
|
|
2433
|
-
domain: params.domain,
|
|
2434
|
-
signal
|
|
2435
|
-
});
|
|
2436
|
-
return {
|
|
2437
|
-
content: [{ type: "text", text: result.formattedOutput }],
|
|
2438
|
-
details: {
|
|
2439
|
-
sources: result.sources,
|
|
2440
|
-
queries: result.queries
|
|
2441
|
-
}
|
|
2442
|
-
};
|
|
2443
|
-
} catch (err) {
|
|
2444
|
-
return {
|
|
2445
|
-
content: [{ type: "text", text: `Search failed: ${err.message}` }],
|
|
2446
|
-
details: { error: err.message }
|
|
2447
|
-
};
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
});
|
|
2451
|
-
}
|
|
2461
|
+
registerWebSearchTool(pi);
|
|
2452
2462
|
pi.on("session_start", async (_event, ctx) => {
|
|
2453
2463
|
await quotaStatus.refreshAndPaint(ctx);
|
|
2454
2464
|
});
|