@herbertgao/pi-extensions 2026.9.18 → 2026.9.20
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 +20 -28
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
- package/node_modules/pi-antigravity/README.md +29 -6
- package/node_modules/pi-antigravity/package.json +12 -7
- package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
- package/node_modules/pi-antigravity/src/client/client.ts +1 -1
- package/node_modules/pi-antigravity/src/image/image.ts +4 -4
- package/node_modules/pi-antigravity/src/index.ts +203 -13
- package/node_modules/pi-antigravity/src/search/index.ts +1 -0
- package/node_modules/pi-antigravity/src/search/search.ts +325 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
- package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
- package/node_modules/pi-antigravity/src/types/types.ts +5 -12
- package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
- package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
- package/node_modules/pi-typesafe/README.md +2 -0
- package/node_modules/pi-typesafe/package.json +2 -1
- package/node_modules/pi-web-access/CHANGELOG.md +72 -0
- package/node_modules/pi-web-access/README.md +34 -13
- package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
- package/node_modules/pi-web-access/curator-page.ts +5 -1
- package/node_modules/pi-web-access/curator-server.ts +2 -0
- package/node_modules/pi-web-access/dist/index.js +1337 -1009
- package/node_modules/pi-web-access/exa.ts +20 -52
- package/node_modules/pi-web-access/extract.ts +41 -4
- package/node_modules/pi-web-access/gemini-search.ts +12 -4
- package/node_modules/pi-web-access/index.ts +29 -37
- package/node_modules/pi-web-access/openai-search.ts +35 -7
- package/node_modules/pi-web-access/package.json +6 -5
- package/node_modules/pi-web-access/page-query.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +2 -3
- package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
- package/node_modules/pi-web-access/storage.ts +48 -77
- package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
- package/node_modules/pi-web-access/summary-review.ts +3 -4
- package/node_modules/pi-web-access/tavily.ts +67 -20
- package/node_modules/pi-web-access/tool-activation.ts +53 -33
- package/node_modules/pi-web-access/utils.ts +1 -1
- package/node_modules/pi-web-access/you.ts +164 -0
- package/node_modules/pi-web-access/zai.ts +161 -0
- package/package.json +7 -11
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- package/node_modules/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import {
|
|
2
|
+
antigravityHeaders,
|
|
3
|
+
endpointCandidates,
|
|
4
|
+
jsonOrTextError,
|
|
5
|
+
parseApiKey,
|
|
6
|
+
} from "../client/client.js";
|
|
7
|
+
import { AntigravityRequestType, AntigravityUserAgent, GeminiRole } from "../types/enums.js";
|
|
8
|
+
import { antigravityFetch } from "../utils/http.js";
|
|
9
|
+
import { safeError } from "../utils/security.js";
|
|
10
|
+
import { antigravityRequestEnvelope, isRecord } from "../utils/util.js";
|
|
11
|
+
|
|
12
|
+
/** Default search engine model: fast, natively supports groundings and reasoning. */
|
|
13
|
+
export const DEFAULT_SEARCH_MODEL = "gemini-3-flash";
|
|
14
|
+
|
|
15
|
+
/** Fallback runtime candidates if the primary flash model is undergoing rollout or capacity limits. */
|
|
16
|
+
export const SEARCH_MODEL_FALLBACKS = [
|
|
17
|
+
DEFAULT_SEARCH_MODEL,
|
|
18
|
+
"gemini-3.6-flash-low",
|
|
19
|
+
"gemini-2.5-flash",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export const SEARCH_SYSTEM_INSTRUCTION = `You are an expert deep-research investigator and technical analyst.
|
|
23
|
+
Your objective is to use Google Search Grounding to unearth rich, high-signal, multi-perspective facts.
|
|
24
|
+
|
|
25
|
+
Guidelines:
|
|
26
|
+
1. DO NOT settle for generic marketing summaries, public relations announcements, or shallow overviews.
|
|
27
|
+
2. Formulate multiple distinct, targeted search queries covering technical architecture, specific parameters, benchmark comparisons, developer issues, pitfalls, and community feedback.
|
|
28
|
+
3. Prioritize hard technical details: exact version numbers, hardware requirements, protocol constraints, benchmarks, error codes, and configuration snippets.
|
|
29
|
+
4. Structure your response into clean, logical Markdown sections citing direct sources.`;
|
|
30
|
+
|
|
31
|
+
export type SearchSource = {
|
|
32
|
+
title: string;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type SearchResult = {
|
|
37
|
+
text: string;
|
|
38
|
+
sources: SearchSource[];
|
|
39
|
+
queries: string[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ExecuteSearchOptions = {
|
|
43
|
+
apiKey?: string;
|
|
44
|
+
query: string;
|
|
45
|
+
instruction?: string;
|
|
46
|
+
urls?: string[];
|
|
47
|
+
thinking?: boolean;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type SearchCommandArgs = {
|
|
52
|
+
query: string;
|
|
53
|
+
urls?: string[];
|
|
54
|
+
thinking?: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse arguments for the interactive `/antigravity.search` command.
|
|
59
|
+
* Supports flags:
|
|
60
|
+
* --thinking: enable deep reasoning for search planning
|
|
61
|
+
* --url <url>: pass target URL for context analysis (can be repeated)
|
|
62
|
+
*
|
|
63
|
+
* @param args - Raw string of CLI arguments.
|
|
64
|
+
* @returns Parsed command arguments containing query, optional URLs, and thinking flag.
|
|
65
|
+
*/
|
|
66
|
+
export function parseSearchCommandArgs(args: string): SearchCommandArgs {
|
|
67
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
68
|
+
const out: SearchCommandArgs = { query: "" };
|
|
69
|
+
const rest: string[] = [];
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
72
|
+
const token = tokens[i];
|
|
73
|
+
if (token === undefined) continue;
|
|
74
|
+
const next = tokens[i + 1];
|
|
75
|
+
|
|
76
|
+
if (token === "--thinking") {
|
|
77
|
+
out.thinking = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (token === "--url" && next) {
|
|
81
|
+
out.urls = out.urls ?? [];
|
|
82
|
+
out.urls.push(next);
|
|
83
|
+
i += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
rest.push(token);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
out.query = rest.join(" ");
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Builds the Antigravity wire request payload for Gemini Google Search Grounding.
|
|
95
|
+
* Includes the required `requestType` and `requestId` envelope fields.
|
|
96
|
+
*
|
|
97
|
+
* @param options - Options including search query, lead agent directives, target URLs, and thinking preference.
|
|
98
|
+
* @param model - Target model identifier (e.g., `gemini-3-flash`).
|
|
99
|
+
* @param projectId - Google Cloud Project ID.
|
|
100
|
+
* @returns Serialized request object matching the Antigravity wire contract.
|
|
101
|
+
*/
|
|
102
|
+
export function buildSearchRequest(
|
|
103
|
+
options: {
|
|
104
|
+
query: string;
|
|
105
|
+
instruction?: string;
|
|
106
|
+
urls?: string[];
|
|
107
|
+
thinking?: boolean;
|
|
108
|
+
},
|
|
109
|
+
model: string,
|
|
110
|
+
projectId: string,
|
|
111
|
+
): Record<string, unknown> {
|
|
112
|
+
let systemInstructionText = SEARCH_SYSTEM_INSTRUCTION;
|
|
113
|
+
if (options.instruction?.trim()) {
|
|
114
|
+
systemInstructionText += `\n\n[SPECIFIC DIRECTIVE FROM LEAD AGENT]:\n${options.instruction.trim()}\nYou MUST strictly follow this directive during search query formulation and synthesis.`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let prompt = options.query.trim();
|
|
118
|
+
if (options.instruction?.trim()) {
|
|
119
|
+
prompt = `[Lead Agent Directive]: ${options.instruction.trim()}\n\nSearch Query: ${prompt}`;
|
|
120
|
+
}
|
|
121
|
+
if (options.urls && options.urls.length > 0) {
|
|
122
|
+
prompt += `\n\nURLs to analyze in detail:\n${options.urls.join("\n")}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const tools: Array<Record<string, unknown>> = [{ googleSearch: {} }];
|
|
126
|
+
if (options.urls && options.urls.length > 0) {
|
|
127
|
+
tools.push({ urlContext: {} });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Thinking budget: 4096 if explicitly requested, baseline 2048 to trigger multi-step search planning
|
|
131
|
+
const thinkingBudget = options.thinking ? 4096 : 2048;
|
|
132
|
+
const envelope = antigravityRequestEnvelope(model, false);
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
project: projectId,
|
|
136
|
+
model,
|
|
137
|
+
request: {
|
|
138
|
+
systemInstruction: {
|
|
139
|
+
role: GeminiRole.User,
|
|
140
|
+
parts: [{ text: systemInstructionText }],
|
|
141
|
+
},
|
|
142
|
+
contents: [
|
|
143
|
+
{
|
|
144
|
+
role: GeminiRole.User,
|
|
145
|
+
parts: [{ text: prompt }],
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
tools,
|
|
149
|
+
generationConfig: {
|
|
150
|
+
thinkingConfig: {
|
|
151
|
+
thinkingBudget,
|
|
152
|
+
includeThoughts: false,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
requestType: AntigravityRequestType.Agent,
|
|
157
|
+
userAgent: AntigravityUserAgent.Antigravity,
|
|
158
|
+
requestId: envelope.requestId,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parse candidate text and Grounding metadata from Antigravity API response.
|
|
164
|
+
*
|
|
165
|
+
* @param data - Raw JSON response from Antigravity generateContent API.
|
|
166
|
+
* @returns Structured SearchResult with synthesized text, web queries, and cited source URLs.
|
|
167
|
+
*/
|
|
168
|
+
function candidateList(data: unknown): unknown {
|
|
169
|
+
if (!isRecord(data)) return undefined;
|
|
170
|
+
if (isRecord(data.response)) return data.response.candidates;
|
|
171
|
+
return data.candidates;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function firstUnknownItem(value: unknown): unknown {
|
|
175
|
+
if (!Array.isArray(value)) return undefined;
|
|
176
|
+
return (value as unknown[])[0];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function parseSearchResponse(data: unknown): SearchResult {
|
|
180
|
+
const result: SearchResult = { text: "", sources: [], queries: [] };
|
|
181
|
+
if (!isRecord(data)) return result;
|
|
182
|
+
|
|
183
|
+
const responseObj = isRecord(data.response) ? data.response : data;
|
|
184
|
+
const candidates = Array.isArray(responseObj.candidates)
|
|
185
|
+
? (responseObj.candidates as unknown[])
|
|
186
|
+
: [];
|
|
187
|
+
const candidate = candidates[0];
|
|
188
|
+
|
|
189
|
+
if (!isRecord(candidate)) {
|
|
190
|
+
const errorObj = isRecord(data.error)
|
|
191
|
+
? data.error
|
|
192
|
+
: isRecord(responseObj.error)
|
|
193
|
+
? responseObj.error
|
|
194
|
+
: undefined;
|
|
195
|
+
const msg =
|
|
196
|
+
typeof errorObj?.message === "string"
|
|
197
|
+
? errorObj.message
|
|
198
|
+
: "No candidate returned from Antigravity Search";
|
|
199
|
+
result.text = `Error: ${msg}`;
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Extract synthesized text (ignoring thinking parts)
|
|
204
|
+
const content = isRecord(candidate.content) ? candidate.content : undefined;
|
|
205
|
+
if (Array.isArray(content?.parts)) {
|
|
206
|
+
result.text = content.parts
|
|
207
|
+
.filter((p) => isRecord(p) && !p.thought && typeof p.text === "string")
|
|
208
|
+
.map((p) => (p as { text: string }).text)
|
|
209
|
+
.filter(Boolean)
|
|
210
|
+
.join("\n\n");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Extract grounding citations & executed queries
|
|
214
|
+
const grounding = isRecord(candidate.groundingMetadata) ? candidate.groundingMetadata : undefined;
|
|
215
|
+
if (grounding) {
|
|
216
|
+
if (Array.isArray(grounding.webSearchQueries)) {
|
|
217
|
+
result.queries = grounding.webSearchQueries.filter((q): q is string => typeof q === "string");
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(grounding.groundingChunks)) {
|
|
220
|
+
for (const chunk of grounding.groundingChunks) {
|
|
221
|
+
if (!isRecord(chunk)) continue;
|
|
222
|
+
const web = isRecord(chunk.web) ? chunk.web : undefined;
|
|
223
|
+
if (typeof web?.uri === "string") {
|
|
224
|
+
result.sources.push({
|
|
225
|
+
title: typeof web.title === "string" && web.title.trim() ? web.title.trim() : web.uri,
|
|
226
|
+
url: web.uri,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Formats structured SearchResult into clean Markdown for agent consumption.
|
|
238
|
+
*
|
|
239
|
+
* @param res - Structured search result containing text, sources, and executed queries.
|
|
240
|
+
* @returns Formatted markdown string.
|
|
241
|
+
*/
|
|
242
|
+
export function formatSearchResult(res: SearchResult): string {
|
|
243
|
+
const sections: string[] = [];
|
|
244
|
+
|
|
245
|
+
if (res.text) {
|
|
246
|
+
sections.push(res.text);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (res.sources.length > 0) {
|
|
250
|
+
const list = res.sources.map((s) => `- [${s.title}](${s.url})`).join("\n");
|
|
251
|
+
sections.push(`### Sources\n${list}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (res.queries.length > 0) {
|
|
255
|
+
const queries = res.queries.map((q) => `\`${q}\``).join(", ");
|
|
256
|
+
sections.push(`*Search queries: ${queries}*`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return sections.join("\n\n");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Execute real-time Google Search grounding via Antigravity backend with automatic fallback.
|
|
264
|
+
*
|
|
265
|
+
* @param options - Execution options containing query, API credentials, and optional directives.
|
|
266
|
+
* @returns Markdown formatted search result with synthesis, citations, and search queries.
|
|
267
|
+
*/
|
|
268
|
+
export async function executeAntigravitySearch(options: ExecuteSearchOptions): Promise<string> {
|
|
269
|
+
const query = options.query.trim();
|
|
270
|
+
if (!query) throw new Error("Search query is required.");
|
|
271
|
+
|
|
272
|
+
const creds = parseApiKey(options.apiKey);
|
|
273
|
+
const headers = antigravityHeaders(creds.token);
|
|
274
|
+
|
|
275
|
+
let lastError = "no endpoint available";
|
|
276
|
+
|
|
277
|
+
for (const model of SEARCH_MODEL_FALLBACKS) {
|
|
278
|
+
const body = JSON.stringify(buildSearchRequest(options, model, creds.projectId));
|
|
279
|
+
|
|
280
|
+
for (const endpoint of endpointCandidates()) {
|
|
281
|
+
if (options.signal?.aborted) throw new Error("Search request was aborted");
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const response = await antigravityFetch(`${endpoint}/v1internal:generateContent`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers,
|
|
287
|
+
body,
|
|
288
|
+
signal: options.signal,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
lastError = jsonOrTextError(await response.text()).slice(0, 400);
|
|
293
|
+
if (response.status === 404 || [429, 500, 502, 503, 504].includes(response.status)) {
|
|
294
|
+
continue; // Retry next endpoint for transient failures or missing model endpoints
|
|
295
|
+
}
|
|
296
|
+
// Fail fast on non-retryable client errors (e.g. 400 Bad Request, 401 Unauthorized, 403 Forbidden)
|
|
297
|
+
throw Object.assign(new Error(lastError), { fatal: true });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const data: unknown = await response.json();
|
|
301
|
+
const cand = firstUnknownItem(candidateList(data));
|
|
302
|
+
|
|
303
|
+
const candidateText =
|
|
304
|
+
isRecord(cand) && isRecord(cand.content) && Array.isArray(cand.content.parts)
|
|
305
|
+
? (cand.content.parts[0] as { text?: string })?.text
|
|
306
|
+
: undefined;
|
|
307
|
+
|
|
308
|
+
// Skip responses indicating deprecated model and move to next fallback model
|
|
309
|
+
if (candidateText && candidateText.includes("is no longer available. Please switch")) {
|
|
310
|
+
lastError = `Model ${model} deprecated: ${candidateText.slice(0, 100)}`;
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const parsed = parseSearchResponse(data);
|
|
315
|
+
return formatSearchResult(parsed);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (options.signal?.aborted) throw error;
|
|
318
|
+
if (error instanceof Error && (error as { fatal?: boolean }).fatal) throw error;
|
|
319
|
+
lastError = safeError(error).slice(0, 400);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
throw new Error(`Antigravity Google Search failed: ${lastError}`);
|
|
325
|
+
}
|
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
type Tool,
|
|
11
11
|
type ToolCall,
|
|
12
12
|
} from "@earendil-works/pi-ai";
|
|
13
|
+
// Namespace import: transcript helpers exist on pi-ai >= 0.86; resolved at runtime.
|
|
14
|
+
import * as piAi from "@earendil-works/pi-ai";
|
|
15
|
+
import { failoverToNextAccount } from "../auth/accounts.js";
|
|
13
16
|
import {
|
|
14
17
|
antigravityHeaders,
|
|
15
18
|
endpointCandidates,
|
|
@@ -71,7 +74,7 @@ import {
|
|
|
71
74
|
resolveSessionTrajectory,
|
|
72
75
|
sanitizeText,
|
|
73
76
|
} from "../utils/util.js";
|
|
74
|
-
import { antigravityFetch } from "../utils/http.js";
|
|
77
|
+
import { antigravityFetch, prewarmConnection } from "../utils/http.js";
|
|
75
78
|
|
|
76
79
|
export { ANTIGRAVITY_API };
|
|
77
80
|
|
|
@@ -82,6 +85,7 @@ const ANTIGRAVITY_SYSTEM_INSTRUCTION =
|
|
|
82
85
|
const ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION =
|
|
83
86
|
'CRITICAL: NEVER output rule checks, formatting guidelines, constraint checklists (e.g. "No emdashes"), or your thinking/personality preambles in the final response. Output only the final response.';
|
|
84
87
|
|
|
88
|
+
const CONTINUATION_TEXT = "Continue the active task using the available instructions and context.";
|
|
85
89
|
let toolCallCounter = 0;
|
|
86
90
|
|
|
87
91
|
function sanitizeToolCallId(id: string, fallbackName?: string): string {
|
|
@@ -99,6 +103,103 @@ function toolCallIdNeeded(modelId: string, runtimeModel: string): boolean {
|
|
|
99
103
|
);
|
|
100
104
|
}
|
|
101
105
|
|
|
106
|
+
interface SystemMessageLike {
|
|
107
|
+
role?: string;
|
|
108
|
+
content?: string | Array<{ type?: string; text?: string }>;
|
|
109
|
+
sections?: Record<string, unknown>;
|
|
110
|
+
toolsAdded?: Tool[];
|
|
111
|
+
toolsRemoved?: Array<{ name: string }>;
|
|
112
|
+
tools?: Tool[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface PiAiTranscriptModule {
|
|
116
|
+
getCurrentTools?: (messages: Context["messages"]) => Tool[];
|
|
117
|
+
getCurrentSystemPrompt?: (messages: Context["messages"]) => string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function transcriptModule(): PiAiTranscriptModule {
|
|
121
|
+
return piAi as unknown as PiAiTranscriptModule;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function asSystemMessages(messages: Context["messages"] | undefined): SystemMessageLike[] {
|
|
125
|
+
return [...(messages ?? [])] as SystemMessageLike[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function extractTranscriptSystemPrompt(messages: readonly SystemMessageLike[]): string {
|
|
129
|
+
const parts: string[] = [];
|
|
130
|
+
for (const message of messages) {
|
|
131
|
+
if (message?.role !== "system") continue;
|
|
132
|
+
if (message.sections && typeof message.sections === "object") {
|
|
133
|
+
const sectionParts = Object.values(message.sections).filter(
|
|
134
|
+
(value): value is string => typeof value === "string" && value.trim().length > 0,
|
|
135
|
+
);
|
|
136
|
+
if (sectionParts.length > 0) {
|
|
137
|
+
parts.push(sectionParts.join("\n\n"));
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (typeof message.content === "string" && message.content.trim()) {
|
|
142
|
+
parts.push(message.content);
|
|
143
|
+
} else if (Array.isArray(message.content)) {
|
|
144
|
+
const texts = message.content
|
|
145
|
+
.map((block) => (typeof block === "string" ? block : block?.text || ""))
|
|
146
|
+
.filter((text) => text.trim().length > 0);
|
|
147
|
+
if (texts.length > 0) parts.push(texts.join("\n\n"));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return parts.join("\n\n");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function extractTranscriptTools(messages: readonly SystemMessageLike[]): Tool[] {
|
|
154
|
+
const tools = new Map<string, Tool>();
|
|
155
|
+
for (const message of messages) {
|
|
156
|
+
if (message?.role !== "system") continue;
|
|
157
|
+
for (const tool of message.toolsRemoved ?? []) {
|
|
158
|
+
tools.delete(tool.name);
|
|
159
|
+
}
|
|
160
|
+
for (const tool of [...(message.toolsAdded ?? []), ...(message.tools ?? [])]) {
|
|
161
|
+
if (tool && typeof tool.name === "string") tools.set(tool.name, tool);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return [...tools.values()];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* pi >= 0.86 hands providers a normalized TranscriptContext: the system prompt and
|
|
169
|
+
* tool declarations live in system messages and must be read with
|
|
170
|
+
* `getCurrentSystemPrompt()` / `getCurrentTools()`. Older releases still pass the
|
|
171
|
+
* flat Context fields. Resolve both so requests are not sent without tools.
|
|
172
|
+
*/
|
|
173
|
+
export function resolveCurrentSystemPrompt(context: Context): string | undefined {
|
|
174
|
+
const helper = transcriptModule().getCurrentSystemPrompt;
|
|
175
|
+
if (typeof helper === "function") {
|
|
176
|
+
try {
|
|
177
|
+
const fromHelper = helper(context.messages ?? []);
|
|
178
|
+
if (fromHelper?.trim()) return fromHelper;
|
|
179
|
+
} catch {
|
|
180
|
+
// Fall through to local replay / legacy fields.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const fromTranscript = extractTranscriptSystemPrompt(asSystemMessages(context.messages));
|
|
184
|
+
if (fromTranscript.trim()) return fromTranscript;
|
|
185
|
+
return context.systemPrompt;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function resolveCurrentTools(context: Context): Tool[] | undefined {
|
|
189
|
+
const helper = transcriptModule().getCurrentTools;
|
|
190
|
+
if (typeof helper === "function") {
|
|
191
|
+
try {
|
|
192
|
+
const fromHelper = helper(context.messages ?? []);
|
|
193
|
+
if (fromHelper.length > 0) return fromHelper;
|
|
194
|
+
} catch {
|
|
195
|
+
// Fall through to local replay / legacy fields.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const fromTranscript = extractTranscriptTools(asSystemMessages(context.messages));
|
|
199
|
+
if (fromTranscript.length > 0) return fromTranscript;
|
|
200
|
+
return context.tools;
|
|
201
|
+
}
|
|
202
|
+
|
|
102
203
|
const base64SignaturePattern = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
103
204
|
function isValidThoughtSignature(signature?: string): boolean {
|
|
104
205
|
if (!signature || typeof signature !== "string" || signature.length === 0) return false;
|
|
@@ -208,6 +309,7 @@ export function convertMessages(
|
|
|
208
309
|
const requiresSig = geminiRequiresThoughtSignature(runtimeModel);
|
|
209
310
|
const droppedToolCallIds = new Map<string, string>();
|
|
210
311
|
for (const msg of context.messages) {
|
|
312
|
+
if ((msg.role as string) === "system") continue;
|
|
211
313
|
if (msg.role === "user") {
|
|
212
314
|
const parts = asTextParts(msg.content);
|
|
213
315
|
appendTurn(contents, GeminiRole.User, parts);
|
|
@@ -314,6 +416,24 @@ export function convertMessages(
|
|
|
314
416
|
}
|
|
315
417
|
}
|
|
316
418
|
|
|
419
|
+
// A function-call model turn is only valid immediately after a user turn
|
|
420
|
+
// (including a function-response turn). Compacted history can drop that
|
|
421
|
+
// boundary, so restore it before applying the natural-language bridge.
|
|
422
|
+
for (let index = 0; index < contents.length; index += 1) {
|
|
423
|
+
const turn = contents[index];
|
|
424
|
+
if (
|
|
425
|
+
turn?.role === GeminiRole.Model &&
|
|
426
|
+
turn.parts.some((part) => "functionCall" in part) &&
|
|
427
|
+
contents[index - 1]?.role !== GeminiRole.User
|
|
428
|
+
) {
|
|
429
|
+
contents.splice(index, 0, {
|
|
430
|
+
role: GeminiRole.User,
|
|
431
|
+
parts: [{ text: CONTINUATION_TEXT }],
|
|
432
|
+
});
|
|
433
|
+
index += 1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
317
437
|
// Google Antigravity requires a natural-language user part in the request,
|
|
318
438
|
// including tool-only continuation turns. Keep injected Skills in the system
|
|
319
439
|
// instruction, then add this protocol bridge only when existing context gives
|
|
@@ -325,13 +445,23 @@ export function convertMessages(
|
|
|
325
445
|
);
|
|
326
446
|
if (!hasUserText && contents.length > 0) {
|
|
327
447
|
const bridge = {
|
|
328
|
-
text:
|
|
448
|
+
text: CONTINUATION_TEXT,
|
|
329
449
|
};
|
|
330
450
|
const userTurn = contents.find((turn) => turn.role === GeminiRole.User);
|
|
331
451
|
if (userTurn) userTurn.parts.push(bridge);
|
|
332
452
|
else contents.unshift({ role: GeminiRole.User, parts: [bridge] });
|
|
333
453
|
}
|
|
334
454
|
|
|
455
|
+
const lastTurn = contents.at(-1);
|
|
456
|
+
if (lastTurn?.role === GeminiRole.Model) {
|
|
457
|
+
if (lastTurn.parts.some((part) => "functionCall" in part)) {
|
|
458
|
+
throw new Error(
|
|
459
|
+
"Antigravity request is missing tool result(s) for the final assistant tool call. Provide the corresponding tool result before continuing.",
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
appendTurn(contents, GeminiRole.User, [{ text: CONTINUATION_TEXT }]);
|
|
463
|
+
}
|
|
464
|
+
|
|
335
465
|
return contents;
|
|
336
466
|
}
|
|
337
467
|
|
|
@@ -756,8 +886,10 @@ export function buildRequest(
|
|
|
756
886
|
const injectedSkills = context.messages.flatMap((msg) =>
|
|
757
887
|
msg.role === "user" ? skillBlocks(msg.content) : [],
|
|
758
888
|
);
|
|
759
|
-
const
|
|
760
|
-
|
|
889
|
+
const systemPromptText = resolveCurrentSystemPrompt(context);
|
|
890
|
+
const declaredTools = resolveCurrentTools(context);
|
|
891
|
+
const systemParts = systemPromptText
|
|
892
|
+
? [{ text: sanitizeText(systemPromptText) }]
|
|
761
893
|
: [{ text: ANTIGRAVITY_SYSTEM_INSTRUCTION }, { text: ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION }];
|
|
762
894
|
systemParts.push(...injectedSkills.map((skill) => ({ text: sanitizeText(skill) })));
|
|
763
895
|
|
|
@@ -767,7 +899,7 @@ export function buildRequest(
|
|
|
767
899
|
turn.role === GeminiRole.User &&
|
|
768
900
|
turn.parts.some((part) => "text" in part && Boolean(part.text.trim())),
|
|
769
901
|
);
|
|
770
|
-
if (!hasUserText && (injectedSkills.length > 0 || Boolean(
|
|
902
|
+
if (!hasUserText && (injectedSkills.length > 0 || Boolean(systemPromptText))) {
|
|
771
903
|
contents.unshift({
|
|
772
904
|
role: GeminiRole.User,
|
|
773
905
|
parts: [{ text: "Apply the active system instructions." }],
|
|
@@ -795,7 +927,7 @@ export function buildRequest(
|
|
|
795
927
|
if (Object.keys(generationConfig).length) request.generationConfig = generationConfig;
|
|
796
928
|
|
|
797
929
|
const isClaude = model.id.startsWith("claude-") || runtimeModel.startsWith("claude-");
|
|
798
|
-
const tools = convertTools(
|
|
930
|
+
const tools = convertTools(declaredTools, isClaude || model.id.startsWith("gpt-oss-"));
|
|
799
931
|
if (tools) {
|
|
800
932
|
request.tools = tools;
|
|
801
933
|
}
|
|
@@ -825,7 +957,7 @@ export function buildRequest(
|
|
|
825
957
|
(m) => m.role === "assistant" && m.stopReason !== "error" && m.stopReason !== "aborted",
|
|
826
958
|
).length ?? 0;
|
|
827
959
|
|
|
828
|
-
const { conversationId, trajectoryId } = resolveSessionTrajectory(context);
|
|
960
|
+
const { conversationId, trajectoryId } = resolveSessionTrajectory(context, options.sessionId);
|
|
829
961
|
|
|
830
962
|
const envelope = antigravityRequestEnvelope(runtimeModel, {
|
|
831
963
|
isClaude,
|
|
@@ -860,6 +992,16 @@ export function mapStopReason(reason: string | undefined): StopReason {
|
|
|
860
992
|
export function friendlyAntigravityError(status: number | undefined, text: string): string {
|
|
861
993
|
const msg = redactSecrets(jsonOrTextError(text)).slice(0, 500);
|
|
862
994
|
if (status === 400) {
|
|
995
|
+
if (/Requests ending with a model turn are not supported/i.test(msg)) {
|
|
996
|
+
return "Antigravity rejected an invalid conversation message boundary. Next: update the extension or add a user message / start a new session, then retry.";
|
|
997
|
+
}
|
|
998
|
+
if (
|
|
999
|
+
/function call turn comes immediately after a user turn or after a function response turn/i.test(
|
|
1000
|
+
msg,
|
|
1001
|
+
)
|
|
1002
|
+
) {
|
|
1003
|
+
return "Antigravity rejected an invalid function-call message boundary. Next: update the extension or start a new session, then retry; re-login is not required.";
|
|
1004
|
+
}
|
|
863
1005
|
if (/API key not valid|API_KEY_INVALID/i.test(msg)) {
|
|
864
1006
|
return "Antigravity login expired or credentials are invalid. Next: run /login antigravity, then retry.";
|
|
865
1007
|
}
|
|
@@ -895,10 +1037,20 @@ export function friendlyAntigravityError(status: number | undefined, text: strin
|
|
|
895
1037
|
if (/Individual quota reached/i.test(msg)) {
|
|
896
1038
|
return `Quota reached. Please wait ${wait || "for reset"}. Next: switch models or try again after reset.`;
|
|
897
1039
|
}
|
|
898
|
-
|
|
1040
|
+
// Google answers a real quota wall with a "Resets in …" hint, but uses generic
|
|
1041
|
+
// RESOURCE_EXHAUSTED ("Resource has been exhausted (e.g. check quota).") for
|
|
1042
|
+
// transient throttling and capacity pressure. Classifying on the word "quota"
|
|
1043
|
+
// alone wrongly marked transient throttling as a hard quota wall, disabling
|
|
1044
|
+
// Pi's automatic retry backoff. Keep real quota walls non-retryable, and
|
|
1045
|
+
// format transient throttling so Pi's retry mechanism engages.
|
|
1046
|
+
const hardLimit =
|
|
1047
|
+
Boolean(wait) ||
|
|
1048
|
+
(!/rate.?limit/i.test(msg) &&
|
|
1049
|
+
/quota exceeded|exceeded your|limit reached|reached your|daily limit/i.test(msg));
|
|
1050
|
+
if (hardLimit) {
|
|
899
1051
|
return `Quota reached.${wait ? ` Please wait ${wait}.` : ""} Next: switch models or retry later.`;
|
|
900
1052
|
}
|
|
901
|
-
return
|
|
1053
|
+
return "Rate limited by Antigravity (429 ResourceExhausted). Next: retrying automatically; if it persists, switch models.";
|
|
902
1054
|
}
|
|
903
1055
|
if (status === 500) {
|
|
904
1056
|
return "Antigravity had an internal server error. Next: retry in a moment or switch models.";
|
|
@@ -1003,7 +1155,6 @@ function guardResponseBody(
|
|
|
1003
1155
|
if (stallMs <= 0) return;
|
|
1004
1156
|
if (timer) clearTimeout(timer);
|
|
1005
1157
|
timer = setTimeout(() => controller.abort(stallError(stallMs)), stallMs);
|
|
1006
|
-
timer.unref?.();
|
|
1007
1158
|
};
|
|
1008
1159
|
let streamController: ReadableStreamDefaultController<Uint8Array> | undefined;
|
|
1009
1160
|
const abortBody = () => {
|
|
@@ -1278,10 +1429,13 @@ export function streamAntigravity(
|
|
|
1278
1429
|
const startTime = Date.now();
|
|
1279
1430
|
const output = createOutput(model);
|
|
1280
1431
|
try {
|
|
1281
|
-
|
|
1432
|
+
let creds = parseApiKey(opts.apiKey);
|
|
1433
|
+
const triedAccessTokens = new Set<string>([creds.token]);
|
|
1434
|
+
const primaryEndpoint = endpointCandidates()[0];
|
|
1435
|
+
if (primaryEndpoint) prewarmConnection(primaryEndpoint);
|
|
1282
1436
|
// Skip loadCodeAssist roundtrip when credentials already carry a projectId.
|
|
1283
1437
|
const warmedProject = creds.projectId ? null : await loadCodeAssist(creds.token);
|
|
1284
|
-
|
|
1438
|
+
let projectId = resolveProjectId({
|
|
1285
1439
|
token: creds.token,
|
|
1286
1440
|
warmedProject,
|
|
1287
1441
|
credentialProjectId: creds.projectId,
|
|
@@ -1309,7 +1463,7 @@ export function streamAntigravity(
|
|
|
1309
1463
|
runtimeCandidates.push(fallback);
|
|
1310
1464
|
}
|
|
1311
1465
|
|
|
1312
|
-
|
|
1466
|
+
let requestHeaders = antigravityHeaders(creds.token);
|
|
1313
1467
|
|
|
1314
1468
|
let response: Response | undefined;
|
|
1315
1469
|
let lastText = "";
|
|
@@ -1344,7 +1498,15 @@ export function streamAntigravity(
|
|
|
1344
1498
|
setLastStatus(response.status);
|
|
1345
1499
|
if (response.ok) break;
|
|
1346
1500
|
lastText = await response.text();
|
|
1347
|
-
if (
|
|
1501
|
+
if (
|
|
1502
|
+
response.status === 429 &&
|
|
1503
|
+
(/Individual quota reached/i.test(lastText) ||
|
|
1504
|
+
/Resets? in /i.test(lastText) ||
|
|
1505
|
+
(!/rate.?limit/i.test(lastText) &&
|
|
1506
|
+
/quota exceeded|exceeded your|daily limit/i.test(lastText)))
|
|
1507
|
+
) {
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1348
1510
|
if (![403, 404, 429, 500, 502, 503, 504].includes(response.status)) break;
|
|
1349
1511
|
}
|
|
1350
1512
|
|
|
@@ -1405,6 +1567,21 @@ export function streamAntigravity(
|
|
|
1405
1567
|
}
|
|
1406
1568
|
const friendly = friendlyAntigravityError(response?.status, lastText);
|
|
1407
1569
|
if (response?.status === 429 && /Quota reached\./i.test(friendly)) {
|
|
1570
|
+
const next = await failoverToNextAccount(triedAccessTokens);
|
|
1571
|
+
if (next) {
|
|
1572
|
+
triedAccessTokens.add(next.token);
|
|
1573
|
+
creds = next;
|
|
1574
|
+
requestHeaders = antigravityHeaders(creds.token);
|
|
1575
|
+
const switchedProject = creds.projectId ? null : await loadCodeAssist(creds.token);
|
|
1576
|
+
projectId = resolveProjectId({
|
|
1577
|
+
token: creds.token,
|
|
1578
|
+
warmedProject: switchedProject,
|
|
1579
|
+
credentialProjectId: creds.projectId,
|
|
1580
|
+
});
|
|
1581
|
+
setLastProjectId(projectId);
|
|
1582
|
+
emptyAttempt = -1;
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1408
1585
|
throw new Error(friendly);
|
|
1409
1586
|
}
|
|
1410
1587
|
throw new Error(
|