mixdog 0.9.166 → 0.9.167
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/package.json +1 -1
- package/scripts/audit-usage-ledger.mjs +86 -0
- package/scripts/generate-voice-runtime-manifest.mjs +22 -17
- package/scripts/memory-chunk-quality.mjs +128 -0
- package/scripts/memory-layer2-check.mjs +133 -0
- package/scripts/model-catalog-audit.mjs +6 -0
- package/scripts/native-tool-download.mjs +14 -0
- package/scripts/prepare-native-assets.mjs +2 -7
- package/scripts/tool-failures.mjs +7 -2
- package/scripts/verify-release-assets.mjs +48 -90
- package/src/defaults/skills/browser-use/SKILL.md +33 -18
- package/src/defaults/skills/computer-use/SKILL.md +16 -8
- package/src/lib/keychain-cjs.cjs +0 -1
- package/src/lib/rules-builder.cjs +4 -1
- package/src/rules/agent/40-cycle1-agent.md +21 -13
- package/src/rules/lead/01-general.md +8 -3
- package/src/rules/shared/10-tool-workflow.md +20 -11
- package/src/rules/shared/30-exploration.md +11 -4
- package/src/rules/shared/40-editing.md +2 -3
- package/src/rules/shared/50-execution.md +6 -3
- package/src/rules/shared/60-verification.md +3 -2
- package/src/rules/shared/70-delivery.md +0 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +51 -45
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +3 -4
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -7
- package/src/runtime/agent/orchestrator/agent-trace.mjs +9 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +25 -1055
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +272 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +296 -0
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +523 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +13 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +214 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +220 -116
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +74 -44
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +191 -29
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +13 -12
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +1 -9
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +48 -25
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +28 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +1 -8
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/lib/grok-tool-schema.mjs +61 -13
- package/src/runtime/agent/orchestrator/providers/model-catalog-projection.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +123 -118
- package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-codex-endpoints.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +154 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +322 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +243 -702
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-startup-prewarm.mjs +221 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-model-identities.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +120 -710
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +36 -15
- package/src/runtime/agent/orchestrator/session/cache/util.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +36 -31
- package/src/runtime/agent/orchestrator/session/compact.mjs +4 -5
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +6 -5
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +121 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/loop/diagnostics.mjs +74 -0
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +44 -17
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +266 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +111 -0
- package/src/runtime/agent/orchestrator/session/loop/request-projection.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +91 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +20 -11
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +3 -5
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +39 -0
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -25
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +62 -10
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +17 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +61 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +13 -9
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +69 -6
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +14 -17
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/bridge-discovery.mjs +8 -0
- package/src/runtime/browser-bridge/action-schema.mjs +37 -43
- package/src/runtime/browser-bridge/client.mjs +111 -99
- package/src/runtime/browser-bridge/input-fields.mjs +187 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +12 -227
- package/src/runtime/computer-bridge/action-schema.mjs +2 -46
- package/src/runtime/computer-bridge/client.mjs +69 -77
- package/src/runtime/computer-bridge/core-actions.mjs +7 -5
- package/src/runtime/computer-bridge/error-recovery.mjs +41 -37
- package/src/runtime/computer-bridge/tool-defs.mjs +1 -1
- package/src/runtime/github/commands.mjs +4 -1
- package/src/runtime/github/contract.mjs +1 -1
- package/src/runtime/github/tool.mjs +2 -2
- package/src/runtime/media/adapters/antigravity-image.mjs +72 -0
- package/src/runtime/media/adapters/gemini-image.mjs +20 -11
- package/src/runtime/media/auth.mjs +12 -0
- package/src/runtime/media/catalog.mjs +30 -10
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +40 -31
- package/src/runtime/memory/index.mjs +2 -9
- package/src/runtime/memory/lib/compact-handoff.mjs +26 -18
- package/src/runtime/memory/lib/memory-chunk-audit.mjs +41 -0
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +388 -0
- package/src/runtime/memory/lib/memory-cycle1.mjs +95 -394
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +29 -4
- package/src/runtime/memory/lib/memory-recall-store.mjs +8 -12
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -11
- package/src/runtime/memory/lib/memory.mjs +2 -0
- package/src/runtime/memory/lib/query-handlers.mjs +98 -144
- package/src/runtime/memory/lib/query-ranking.mjs +23 -9
- package/src/runtime/memory/lib/recall-format.mjs +37 -27
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +14 -8
- package/src/runtime/memory/lib/session-ingest.mjs +45 -84
- package/src/runtime/memory/lib/transcript-ingest.mjs +19 -21
- package/src/runtime/memory/tool-defs.mjs +2 -2
- package/src/runtime/office/authoring/pptx-author-session.mjs +3 -7
- package/src/runtime/office/core/office-actions-batch.mjs +2 -8
- package/src/runtime/office/core/office-actions-open.mjs +124 -0
- package/src/runtime/office/core/office-actions-read.mjs +127 -0
- package/src/runtime/office/core/office-core.mjs +80 -1
- package/src/runtime/office/core/office-sessions.mjs +116 -138
- package/src/runtime/office/index.mjs +18 -271
- package/src/runtime/office/pdf/pdf-analysis.mjs +2 -2
- package/src/runtime/office/pdf/pdf-limits.mjs +2 -0
- package/src/runtime/office/tool-defs.mjs +7 -6
- package/src/runtime/shared/agent-route-config.mjs +8 -8
- package/src/runtime/shared/child-spawn-gate.mjs +10 -13
- package/src/runtime/shared/config.mjs +2 -7
- package/src/runtime/shared/env.mjs +3 -3
- package/src/runtime/shared/err-text.mjs +1 -1
- package/src/runtime/shared/extension-scopes.mjs +3 -2
- package/src/runtime/shared/heap-cap.mjs +10 -6
- package/src/runtime/shared/llm/cost.mjs +35 -10
- package/src/runtime/shared/llm/usage-accounting.mjs +11 -1
- package/src/runtime/shared/llm/usage-context.mjs +6 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +5 -0
- package/src/runtime/shared/llm/usage-ledger-repair.mjs +109 -0
- package/src/runtime/shared/llm/usage-ledger.mjs +59 -20
- package/src/runtime/shared/llm/usage-pricing-refresh.mjs +26 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +17 -15
- package/src/runtime/shared/native-asset.mjs +3 -4
- package/src/runtime/shared/object.mjs +4 -0
- package/src/runtime/shared/pg-schema-db.mjs +21 -0
- package/src/runtime/shared/plugin-metadata.mjs +3 -1
- package/src/runtime/shared/resource-admission.mjs +1 -6
- package/src/runtime/shared/schedules-db.mjs +8 -24
- package/src/runtime/shared/schema-value-error.d.mts +2 -0
- package/src/runtime/shared/schema-value-error.mjs +68 -0
- package/src/runtime/shared/singleton-owner.mjs +11 -24
- package/src/runtime/shared/staged-update.mjs +1 -10
- package/src/runtime/shared/tool-card-model.mjs +8 -5
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +1 -5
- package/src/runtime/shared/user-data-guard.mjs +1 -4
- package/src/runtime/shared/webhooks-db.mjs +8 -24
- package/src/runtime/web-search/index.mjs +32 -8
- package/src/runtime/web-search/tool-defs.mjs +4 -4
- package/src/session-runtime/config-lifecycle.mjs +34 -0
- package/src/session-runtime/context-status.mjs +19 -0
- package/src/session-runtime/cwd-plugins.mjs +12 -29
- package/src/session-runtime/goal-tasks.mjs +2 -2
- package/src/session-runtime/goal-tool-defs.mjs +3 -2
- package/src/session-runtime/internal-tool-executor.mjs +7 -0
- package/src/session-runtime/lifecycle-api.mjs +5 -11
- package/src/session-runtime/notification-bus.mjs +41 -0
- package/src/session-runtime/prewarm.mjs +22 -0
- package/src/session-runtime/runtime-bootstrap.mjs +90 -0
- package/src/session-runtime/runtime-core.mjs +88 -202
- package/src/session-runtime/runtime-feature-gates.mjs +37 -0
- package/src/session-runtime/session-lifecycle.mjs +5 -5
- package/src/session-runtime/session-turn-api.mjs +1 -12
- package/src/session-runtime/setup-tool/executor.mjs +13 -5
- package/src/session-runtime/setup-tool/tool-defs.mjs +52 -46
- package/src/session-runtime/tool-defs.mjs +73 -4
- package/src/session-runtime/usage-stats-api.mjs +4 -1
- package/src/session-runtime/warmup-schedulers.mjs +8 -2
- package/src/session-runtime/workflow.mjs +14 -0
- package/src/standalone/channel-session-router.mjs +3 -1
- package/src/standalone/channel-worker.mjs +18 -9
- package/src/standalone/daemon-crash-capture.mjs +534 -0
- package/src/standalone/daemon-telemetry.mjs +175 -0
- package/src/standalone/daemon.mjs +31 -21
- package/src/standalone/session-client.mjs +31 -23
- package/src/standalone/usage-dashboard-model.mjs +2 -0
- package/src/standalone/usage-stats-hours.mjs +18 -11
- package/src/standalone/usage-stats-model.mjs +39 -28
- package/src/standalone/usage-stats-period.mjs +43 -45
- package/src/tui/app/use-transcript-window.mjs +1 -1
- package/src/tui/components/StatusLine.jsx +13 -42
- package/src/tui/dist/index.mjs +256 -239
- package/src/tui/session/session-api-ext.mjs +44 -41
- package/src/tui/session/session-api.mjs +4 -0
- package/src/tui/session/tool-card-results.mjs +25 -78
- package/src/tui/session/tool-result-status.mjs +94 -1
- package/src/tui/session/turn-aggregate-cards.mjs +17 -54
- package/src/ui/statusline-format.mjs +6 -26
- package/src/workflows/default/WORKFLOW.md +5 -0
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Flow: PKCE authorization on a loopback callback (port 51121, the port the
|
|
5
5
|
* registered client is bound to) -> token exchange -> account email ->
|
|
6
|
-
* `loadCodeAssist` for
|
|
7
|
-
*
|
|
8
|
-
* request carries `project`.
|
|
6
|
+
* `loadCodeAssist` for account status -> free-tier onboarding when needed ->
|
|
7
|
+
* refreshed Cloud project. A login without a project id is useless: every
|
|
8
|
+
* content request carries `project`.
|
|
9
9
|
*/
|
|
10
10
|
import { createServer } from 'http';
|
|
11
11
|
import { randomBytes } from 'crypto';
|
|
12
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
12
13
|
import { createOAuthPkce, parseOAuthCodeInput } from './lib/oauth-pkce.mjs';
|
|
13
14
|
import {
|
|
14
15
|
AUTH_URL,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
CLIENT_ID,
|
|
19
20
|
CLIENT_SECRET,
|
|
20
21
|
LOGIN_TIMEOUT_MS,
|
|
21
|
-
|
|
22
|
+
PROJECT_ENDPOINT,
|
|
22
23
|
PROJECT_TIMEOUT_MS,
|
|
23
24
|
REDIRECT_URI,
|
|
24
25
|
SCOPES,
|
|
@@ -31,128 +32,182 @@ import {
|
|
|
31
32
|
saveTokens,
|
|
32
33
|
} from './antigravity-oauth-tokens.mjs';
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
-
const
|
|
35
|
+
const ONBOARD_INTERVAL_MS = 1_000;
|
|
36
|
+
const CALLBACK_FAILURE_HTML = '<html><body><h2>Antigravity sign-in was not completed.</h2>'
|
|
37
|
+
+ '<p>Return to Mixdog for the error details and any account verification link.</p></body></html>';
|
|
36
38
|
|
|
37
39
|
export function generatePKCE() {
|
|
38
40
|
return createOAuthPkce();
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
function readProjectId(value) {
|
|
42
|
-
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
43
|
-
if (value && typeof value === 'object' && typeof value.id === 'string' && value.id.trim()) {
|
|
44
|
-
return value.id.trim();
|
|
45
|
-
}
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
function extractProjectId(payload) {
|
|
50
|
-
|
|
51
|
-
for (const key of ['cloudaicompanionProject', 'projectId', 'project']) {
|
|
52
|
-
const id = readProjectId(payload[key]);
|
|
53
|
-
if (id) return id;
|
|
54
|
-
}
|
|
55
|
-
return '';
|
|
44
|
+
return typeof payload?.cloudaicompanionProject === 'string' ? payload.cloudaicompanionProject : '';
|
|
56
45
|
}
|
|
57
46
|
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return current || 'free-tier';
|
|
47
|
+
function accountVerificationError(validationUrl, reason, email) {
|
|
48
|
+
const account = email ? ` for ${email}` : '';
|
|
49
|
+
const detail = reason ? `\n${reason}` : '';
|
|
50
|
+
const error = new Error(_scrubTokens(`[antigravity-oauth] Account verification required${account}.${detail}`
|
|
51
|
+
+ `\nVisit ${validationUrl} to continue, then sign in again.`));
|
|
52
|
+
error.code = 'VALIDATION_REQUIRED';
|
|
53
|
+
error.validationUrl = validationUrl;
|
|
54
|
+
return error;
|
|
67
55
|
}
|
|
68
56
|
|
|
69
|
-
async function
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
async function requestCodeAssist(action, body, context, timeoutMs = PROJECT_TIMEOUT_MS) {
|
|
58
|
+
const { accessToken, fetchFn, signal } = context;
|
|
59
|
+
signal?.throwIfAborted();
|
|
60
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
61
|
+
const isPost = body !== undefined;
|
|
62
|
+
const res = await fetchFn(`${PROJECT_ENDPOINT}/v1internal${isPost ? ':' : '/'}${action}`, {
|
|
63
|
+
method: isPost ? 'POST' : 'GET',
|
|
72
64
|
headers: {
|
|
73
65
|
'Content-Type': 'application/json',
|
|
74
66
|
Authorization: `Bearer ${accessToken}`,
|
|
75
67
|
...antigravityHeaders(),
|
|
76
68
|
},
|
|
77
|
-
body: JSON.stringify(body),
|
|
78
|
-
|
|
69
|
+
...(isPost ? { body: JSON.stringify(body) } : {}),
|
|
70
|
+
redirect: 'error',
|
|
71
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
79
72
|
});
|
|
73
|
+
if (res.status !== 200) {
|
|
74
|
+
const text = await res.text();
|
|
75
|
+
let failure = null;
|
|
76
|
+
try { failure = JSON.parse(text); } catch { /* Non-JSON errors retain their original detail below. */ }
|
|
77
|
+
const details = failure?.error?.details;
|
|
78
|
+
const validation = Array.isArray(details) ? details.find((entry) =>
|
|
79
|
+
entry?.reason === 'VALIDATION_REQUIRED'
|
|
80
|
+
&& typeof entry.metadata?.validation_url === 'string'
|
|
81
|
+
&& entry.metadata.validation_url.length > 0) : null;
|
|
82
|
+
if (validation) {
|
|
83
|
+
throw accountVerificationError(validation.metadata.validation_url, failure.error.message, context.email);
|
|
84
|
+
}
|
|
85
|
+
const detail = _scrubTokens(text);
|
|
86
|
+
throw new Error(`[antigravity-oauth] ${action} failed: ${res.status} ${res.statusText}: ${detail}`);
|
|
87
|
+
}
|
|
88
|
+
const payload = await res.json();
|
|
89
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
90
|
+
throw new Error(`[antigravity-oauth] invalid ${action} response`);
|
|
91
|
+
}
|
|
92
|
+
return payload;
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
async function fetchAccountEmail(accessToken, { fetchFn = fetch } = {}) {
|
|
95
|
+
async function fetchAccountEmail(accessToken, { fetchFn = fetch, signal = null } = {}) {
|
|
83
96
|
try {
|
|
84
97
|
const res = await fetchFn(USERINFO_URL, {
|
|
85
98
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
86
|
-
|
|
99
|
+
redirect: 'error',
|
|
100
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(TOKEN_TIMEOUT_MS)]) : AbortSignal.timeout(TOKEN_TIMEOUT_MS),
|
|
87
101
|
});
|
|
88
102
|
if (!res.ok) return '';
|
|
89
103
|
const json = await res.json();
|
|
90
104
|
return String(json?.email || '');
|
|
91
|
-
} catch {
|
|
105
|
+
} catch {
|
|
106
|
+
signal?.throwIfAborted();
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function loadProjectState(context) {
|
|
112
|
+
const body = { metadata: codeAssistMetadata() };
|
|
113
|
+
let payload = await requestCodeAssist('loadCodeAssist', body, context);
|
|
114
|
+
const projectId = extractProjectId(payload);
|
|
115
|
+
if (payload.paidTier == null && projectId) {
|
|
116
|
+
payload = await requestCodeAssist('loadCodeAssist', {
|
|
117
|
+
...body,
|
|
118
|
+
cloudaicompanionProject: projectId,
|
|
119
|
+
}, context);
|
|
120
|
+
}
|
|
121
|
+
return payload;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function tierIds(list, key) {
|
|
125
|
+
return Array.isArray(list) ? list.map((tier) => tier?.[key]).filter((id) => typeof id === 'string' && id) : [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function tierSummary(payload) {
|
|
129
|
+
return `allowed tiers: [${tierIds(payload.allowedTiers, 'id').join(', ')}]; `
|
|
130
|
+
+ `ineligible tiers: [${tierIds(payload.ineligibleTiers, 'tierId').join(', ')}]`;
|
|
92
131
|
}
|
|
93
132
|
|
|
94
133
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
134
|
+
* Tier to onboard with. Free tier when Google allows it (or reports no tier
|
|
135
|
+
* list at all); otherwise the default allowed tier that Google can provision
|
|
136
|
+
* without a user-supplied Cloud project, e.g. a paid subscription tier.
|
|
98
137
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const payload = await res.json();
|
|
118
|
-
const existing = extractProjectId(payload);
|
|
119
|
-
if (existing) return existing;
|
|
120
|
-
tierId = defaultTierId(payload?.allowedTiers, payload?.currentTier);
|
|
121
|
-
break;
|
|
122
|
-
} catch (err) {
|
|
123
|
-
lastError = String(err?.message || err).slice(0, 200);
|
|
138
|
+
function onboardingTier(payload) {
|
|
139
|
+
const allowed = tierIds(payload.allowedTiers, 'id');
|
|
140
|
+
const ineligible = new Set(tierIds(payload.ineligibleTiers, 'tierId'));
|
|
141
|
+
if (allowed.includes('free-tier')) return 'free-tier';
|
|
142
|
+
if (!Array.isArray(payload.allowedTiers) && !ineligible.has('free-tier')) return 'free-tier';
|
|
143
|
+
const managed = (payload.allowedTiers ?? []).filter((tier) => typeof tier?.id === 'string' && tier.id
|
|
144
|
+
&& tier.userDefinedCloudaicompanionProject !== true && !ineligible.has(tier.id));
|
|
145
|
+
return (managed.find((tier) => tier.isDefault) ?? managed[0])?.id ?? '';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function noTierError(payload, email) {
|
|
149
|
+
const summary = tierSummary(payload);
|
|
150
|
+
const blocked = payload.ineligibleTiers?.find((tier) => tier?.tierId === 'free-tier' && tier.reasonMessage)
|
|
151
|
+
?? payload.ineligibleTiers?.find((tier) => tier?.reasonMessage);
|
|
152
|
+
if (blocked) {
|
|
153
|
+
const reason = `${blocked.reasonMessage}\n(${summary})`;
|
|
154
|
+
if (typeof blocked.validationUrl === 'string' && blocked.validationUrl) {
|
|
155
|
+
return accountVerificationError(blocked.validationUrl, reason, email);
|
|
124
156
|
}
|
|
157
|
+
return new Error(`[antigravity-oauth] ${_scrubTokens(reason)}`);
|
|
125
158
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
159
|
+
return new Error(`[antigravity-oauth] loadCodeAssist allowed no tier to onboard (${summary})`);
|
|
160
|
+
}
|
|
129
161
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
await new Promise((r) => setTimeout(r, ONBOARD_INTERVAL_MS));
|
|
147
|
-
} catch {
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
162
|
+
async function provisionProject(context, tierId) {
|
|
163
|
+
const deadline = Date.now() + PROJECT_TIMEOUT_MS;
|
|
164
|
+
const remaining = () => {
|
|
165
|
+
const ms = deadline - Date.now();
|
|
166
|
+
if (ms <= 0) throw new Error(`[antigravity-oauth] onboardUser timed out after ${PROJECT_TIMEOUT_MS}ms`);
|
|
167
|
+
return ms;
|
|
168
|
+
};
|
|
169
|
+
let operation = await requestCodeAssist('onboardUser', {
|
|
170
|
+
tierId,
|
|
171
|
+
metadata: codeAssistMetadata(),
|
|
172
|
+
}, context, remaining());
|
|
173
|
+
while (operation.done !== true) {
|
|
174
|
+
await delay(Math.min(ONBOARD_INTERVAL_MS, remaining()), undefined, { signal: context.signal ?? undefined });
|
|
175
|
+
const timeoutMs = remaining();
|
|
176
|
+
if (typeof operation.name !== 'string' || !operation.name) {
|
|
177
|
+
throw new Error('[antigravity-oauth] onboardUser returned an operation without a name');
|
|
150
178
|
}
|
|
179
|
+
operation = await requestCodeAssist(operation.name, undefined, context, timeoutMs);
|
|
180
|
+
}
|
|
181
|
+
if (operation.error != null) {
|
|
182
|
+
const { code, message } = operation.error;
|
|
183
|
+
const detail = message ? `${typeof code === 'number' ? `${code}: ` : ''}${message}` : JSON.stringify(operation.error);
|
|
184
|
+
throw new Error(`[antigravity-oauth] onboardUser operation failed: ${_scrubTokens(detail)}`);
|
|
185
|
+
}
|
|
186
|
+
if (!operation.response || typeof operation.response['@type'] !== 'string') {
|
|
187
|
+
throw new Error('[antigravity-oauth] invalid onboardUser response');
|
|
151
188
|
}
|
|
152
|
-
throw new Error('[antigravity-oauth] could not resolve a Cloud project for this account');
|
|
153
189
|
}
|
|
154
190
|
|
|
155
|
-
|
|
191
|
+
/** Resolve account eligibility and return the project from a fresh status load. */
|
|
192
|
+
export async function discoverProject(accessToken, { fetchFn = fetch, onProgress = null, signal = null, email = '' } = {}) {
|
|
193
|
+
const context = { accessToken, fetchFn, signal, email };
|
|
194
|
+
onProgress?.('Checking Cloud Code Assist account status...');
|
|
195
|
+
const initial = await loadProjectState(context);
|
|
196
|
+
if (initial.currentTier == null) {
|
|
197
|
+
const tierId = onboardingTier(initial);
|
|
198
|
+
if (!tierId) throw noTierError(initial, email);
|
|
199
|
+
onProgress?.(`Provisioning the Antigravity ${tierId}...`);
|
|
200
|
+
await provisionProject(context, tierId);
|
|
201
|
+
}
|
|
202
|
+
onProgress?.('Refreshing Cloud Code Assist project...');
|
|
203
|
+
const refreshed = await loadProjectState(context);
|
|
204
|
+
const projectId = extractProjectId(refreshed);
|
|
205
|
+
if (!projectId) throw new Error('[antigravity-oauth] loadCodeAssist did not return a cloudaicompanionProject');
|
|
206
|
+
return projectId;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function exchangeAuthorizationCode({ code, verifier, fetchFn = fetch, onProgress = null, signal = null }) {
|
|
210
|
+
signal?.throwIfAborted();
|
|
156
211
|
const cleanCode = String(code || '').trim();
|
|
157
212
|
if (!cleanCode) throw new Error('[antigravity-oauth] authorization code is required');
|
|
158
213
|
const res = await fetchFn(TOKEN_URL, {
|
|
@@ -169,7 +224,7 @@ export async function exchangeAuthorizationCode({ code, verifier, fetchFn = fetc
|
|
|
169
224
|
// Secret-bearing (code + verifier): refuse redirects so neither can be
|
|
170
225
|
// replayed against an untrusted host.
|
|
171
226
|
redirect: 'error',
|
|
172
|
-
signal: AbortSignal.timeout(TOKEN_TIMEOUT_MS),
|
|
227
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(TOKEN_TIMEOUT_MS)]) : AbortSignal.timeout(TOKEN_TIMEOUT_MS),
|
|
173
228
|
});
|
|
174
229
|
if (!res.ok) {
|
|
175
230
|
const text = await res.text().catch(() => '');
|
|
@@ -179,8 +234,8 @@ export async function exchangeAuthorizationCode({ code, verifier, fetchFn = fetc
|
|
|
179
234
|
if (!json?.access_token || !json?.refresh_token) {
|
|
180
235
|
throw new Error('[antigravity-oauth] token exchange response missing access_token or refresh_token');
|
|
181
236
|
}
|
|
182
|
-
const email = await fetchAccountEmail(json.access_token, { fetchFn });
|
|
183
|
-
const projectId = await discoverProject(json.access_token, { fetchFn, onProgress });
|
|
237
|
+
const email = await fetchAccountEmail(json.access_token, { fetchFn, signal });
|
|
238
|
+
const projectId = await discoverProject(json.access_token, { fetchFn, onProgress, signal, email });
|
|
184
239
|
const tokens = {
|
|
185
240
|
access_token: json.access_token,
|
|
186
241
|
refresh_token: json.refresh_token,
|
|
@@ -188,11 +243,18 @@ export async function exchangeAuthorizationCode({ code, verifier, fetchFn = fetc
|
|
|
188
243
|
project_id: projectId,
|
|
189
244
|
email,
|
|
190
245
|
};
|
|
246
|
+
signal?.throwIfAborted();
|
|
191
247
|
saveTokens(tokens);
|
|
192
248
|
return tokens;
|
|
193
249
|
}
|
|
194
250
|
|
|
195
|
-
export async function beginOAuthLogin({
|
|
251
|
+
export async function beginOAuthLogin({
|
|
252
|
+
fetchFn = fetch,
|
|
253
|
+
onProgress = null,
|
|
254
|
+
createServerFn = createServer,
|
|
255
|
+
openBrowserFn = null,
|
|
256
|
+
} = {}) {
|
|
257
|
+
const controller = new AbortController();
|
|
196
258
|
const pkce = generatePKCE();
|
|
197
259
|
const state = randomBytes(16).toString('hex');
|
|
198
260
|
const url = new URL(AUTH_URL);
|
|
@@ -210,53 +272,96 @@ export async function beginOAuthLogin({ fetchFn = fetch } = {}) {
|
|
|
210
272
|
let server = null;
|
|
211
273
|
let timeout = null;
|
|
212
274
|
let finish = null;
|
|
275
|
+
let settled = false;
|
|
276
|
+
let exchangePromise = null;
|
|
277
|
+
const acceptCode = (code) => {
|
|
278
|
+
if (exchangePromise) return exchangePromise;
|
|
279
|
+
controller.signal.throwIfAborted();
|
|
280
|
+
// The browser deadline ends when a code arrives; network requests have
|
|
281
|
+
// their own deadlines. Browser and manual callbacks share one exchange.
|
|
282
|
+
if (timeout) clearTimeout(timeout);
|
|
283
|
+
timeout = null;
|
|
284
|
+
exchangePromise = exchangeAuthorizationCode({
|
|
285
|
+
code, verifier: pkce.verifier, fetchFn, onProgress, signal: controller.signal,
|
|
286
|
+
});
|
|
287
|
+
exchangePromise.then(
|
|
288
|
+
(tokens) => finish(tokens),
|
|
289
|
+
(error) => finish(null, error instanceof Error ? error : new Error(String(error))),
|
|
290
|
+
);
|
|
291
|
+
return exchangePromise;
|
|
292
|
+
};
|
|
213
293
|
const waitForCallback = new Promise((resolvePromise, reject) => {
|
|
214
|
-
let settled = false;
|
|
215
294
|
finish = (value, error = null) => {
|
|
216
295
|
if (settled) return;
|
|
217
296
|
settled = true;
|
|
297
|
+
controller.abort();
|
|
218
298
|
if (timeout) clearTimeout(timeout);
|
|
219
299
|
try { server?.close(); } catch { /* already closed */ }
|
|
220
300
|
if (error) reject(error);
|
|
221
301
|
else resolvePromise(value);
|
|
222
302
|
};
|
|
223
|
-
server =
|
|
303
|
+
server = createServerFn(async (req, res) => {
|
|
224
304
|
const requestUrl = new URL(req.url || '/', `http://${CALLBACK_HOST}:${CALLBACK_PORT}`);
|
|
225
305
|
if (requestUrl.pathname !== CALLBACK_PATH) {
|
|
226
306
|
res.writeHead(404);
|
|
227
307
|
res.end();
|
|
228
308
|
return;
|
|
229
309
|
}
|
|
230
|
-
|
|
231
|
-
|
|
310
|
+
if (requestUrl.searchParams.get('state') !== state) {
|
|
311
|
+
res.writeHead(400);
|
|
312
|
+
res.end('Invalid OAuth state');
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const authorizationError = requestUrl.searchParams.get('error');
|
|
316
|
+
if (authorizationError) {
|
|
317
|
+
const detail = requestUrl.searchParams.get('error_description') || authorizationError;
|
|
318
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
319
|
+
res.end(CALLBACK_FAILURE_HTML);
|
|
320
|
+
// A late denial must not cancel an already accepted code.
|
|
321
|
+
if (!exchangePromise) {
|
|
322
|
+
finish(null, new Error(`[antigravity-oauth] authorization failed: ${_scrubTokens(detail)}`));
|
|
323
|
+
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const code = requestUrl.searchParams.get('code')?.trim();
|
|
327
|
+
if (!code) {
|
|
232
328
|
res.writeHead(400);
|
|
233
|
-
res.end('
|
|
234
|
-
finish(null);
|
|
329
|
+
res.end('Missing authorization code');
|
|
235
330
|
return;
|
|
236
331
|
}
|
|
237
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
238
|
-
res.end('<html><body><h2>Antigravity login successful! You can close this tab.</h2></body></html>');
|
|
239
332
|
try {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
333
|
+
await acceptCode(code);
|
|
334
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
335
|
+
res.end('<html><body><h2>Antigravity connected.</h2><p>You can close this tab.</p></body></html>');
|
|
336
|
+
} catch {
|
|
337
|
+
// The shared exchange reports its error through waitForCallback.
|
|
338
|
+
// Do not interpolate provider-controlled text into browser HTML.
|
|
339
|
+
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
340
|
+
res.end(CALLBACK_FAILURE_HTML);
|
|
243
341
|
}
|
|
244
342
|
});
|
|
245
|
-
timeout = setTimeout(() => finish(
|
|
343
|
+
timeout = setTimeout(() => finish(
|
|
344
|
+
null,
|
|
345
|
+
new Error(`[antigravity-oauth] browser authentication timed out after ${LOGIN_TIMEOUT_MS}ms`),
|
|
346
|
+
), LOGIN_TIMEOUT_MS);
|
|
246
347
|
if (timeout.unref) timeout.unref();
|
|
348
|
+
server.on('error', (err) => finish(
|
|
349
|
+
null,
|
|
350
|
+
new Error(`[antigravity-oauth] callback server failed on ${CALLBACK_HOST}:${CALLBACK_PORT}: ${err?.message || err}`),
|
|
351
|
+
));
|
|
247
352
|
server.listen(CALLBACK_PORT, CALLBACK_HOST, async () => {
|
|
353
|
+
if (settled) return;
|
|
248
354
|
process.stderr.write(`\n[antigravity-oauth] Open this URL to log in:\n${url.toString()}\n\n`);
|
|
249
355
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
356
|
+
if (openBrowserFn) await openBrowserFn(url.toString());
|
|
357
|
+
else {
|
|
358
|
+
const { openInBrowser } = await import('../../../shared/open-url.mjs');
|
|
359
|
+
if (!settled) await openInBrowser(url.toString());
|
|
360
|
+
}
|
|
252
361
|
} catch (err) {
|
|
253
362
|
process.stderr.write(`[antigravity-oauth] browser open failed: ${String(err?.message || err).slice(0, 200)}\n`);
|
|
254
363
|
}
|
|
255
364
|
});
|
|
256
|
-
server.on('error', (err) => finish(
|
|
257
|
-
null,
|
|
258
|
-
new Error(`[antigravity-oauth] callback server failed on ${CALLBACK_HOST}:${CALLBACK_PORT}: ${err?.message || err}`),
|
|
259
|
-
));
|
|
260
365
|
});
|
|
261
366
|
|
|
262
367
|
return {
|
|
@@ -264,11 +369,10 @@ export async function beginOAuthLogin({ fetchFn = fetch } = {}) {
|
|
|
264
369
|
url: url.toString(),
|
|
265
370
|
waitForCallback,
|
|
266
371
|
completeCode: async (input) => {
|
|
267
|
-
const parsed = parseOAuthCodeInput(input);
|
|
372
|
+
const parsed = parseOAuthCodeInput(input, { allowHashState: true });
|
|
268
373
|
if (parsed.state && parsed.state !== state) throw new Error('[antigravity-oauth] OAuth state mismatch');
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return tokens;
|
|
374
|
+
if (!parsed.code) throw new Error('[antigravity-oauth] authorization code is required');
|
|
375
|
+
return await acceptCode(parsed.code);
|
|
272
376
|
},
|
|
273
377
|
cancel: () => { finish?.(null); },
|
|
274
378
|
};
|
|
@@ -19,6 +19,8 @@ import { getPluginData } from '../config.mjs';
|
|
|
19
19
|
import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
20
20
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
21
21
|
import { scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
|
|
22
|
+
import { ANTIGRAVITY_MODELS } from './provider-model-identities.mjs';
|
|
23
|
+
export { ANTIGRAVITY_MODELS } from './provider-model-identities.mjs';
|
|
22
24
|
|
|
23
25
|
// The Antigravity IDE's installed-app OAuth client, which every copy of that
|
|
24
26
|
// IDE ships (a native-app client is not a confidential credential). It is
|
|
@@ -35,7 +37,7 @@ export const CLIENT_SECRET = decode('R09DU1BYLUs1', 'OEZXUjQ4NkxkTEoxbUxCOHNYQzR
|
|
|
35
37
|
|
|
36
38
|
export const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
37
39
|
export const TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
38
|
-
export const USERINFO_URL = 'https://www.googleapis.com/oauth2/
|
|
40
|
+
export const USERINFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json';
|
|
39
41
|
export const SCOPES = [
|
|
40
42
|
'https://www.googleapis.com/auth/cloud-platform',
|
|
41
43
|
'https://www.googleapis.com/auth/userinfo.email',
|
|
@@ -47,74 +49,102 @@ export const SCOPES = [
|
|
|
47
49
|
export const CALLBACK_HOST = '127.0.0.1';
|
|
48
50
|
export const CALLBACK_PORT = 51121;
|
|
49
51
|
export const CALLBACK_PATH = '/oauth-callback';
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
export const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
52
|
+
// Use the same literal loopback address in authorization and token exchange.
|
|
53
|
+
export const REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
53
54
|
|
|
54
|
-
// Content requests ride the IDE's daily
|
|
55
|
-
//
|
|
56
|
-
|
|
55
|
+
// Content requests ride the IDE's own daily channel first (the host the
|
|
56
|
+
// shipped client is launched with). The sandbox daily host is an older
|
|
57
|
+
// channel that has answered with version-gating stubs; autopush is the
|
|
58
|
+
// fastest alternate when daily is saturated. Production is kept last: it
|
|
59
|
+
// answers, but not for every Antigravity-only model.
|
|
60
|
+
const ENDPOINT_DAILY = 'https://daily-cloudcode-pa.googleapis.com';
|
|
61
|
+
const ENDPOINT_DAILY_SANDBOX = 'https://daily-cloudcode-pa.sandbox.googleapis.com';
|
|
57
62
|
const ENDPOINT_AUTOPUSH = 'https://autopush-cloudcode-pa.sandbox.googleapis.com';
|
|
58
63
|
const ENDPOINT_PROD = 'https://cloudcode-pa.googleapis.com';
|
|
59
|
-
export const CONTENT_ENDPOINTS = Object.freeze([ENDPOINT_DAILY, ENDPOINT_AUTOPUSH, ENDPOINT_PROD]);
|
|
60
|
-
//
|
|
61
|
-
export const
|
|
64
|
+
export const CONTENT_ENDPOINTS = Object.freeze([ENDPOINT_DAILY, ENDPOINT_AUTOPUSH, ENDPOINT_DAILY_SANDBOX, ENDPOINT_PROD]);
|
|
65
|
+
// Account provisioning uses the hub control plane, not the content sandboxes.
|
|
66
|
+
export const PROJECT_ENDPOINT = ENDPOINT_DAILY;
|
|
62
67
|
|
|
63
|
-
// The gateway exposes no public catalog endpoint, so the model list is curated.
|
|
64
|
-
// Wire ids are the ones the backend accepts verbatim; Gemini 3 Pro and the
|
|
65
|
-
// Claude family carry their effort tier in the id rather than a parameter.
|
|
66
|
-
export const ANTIGRAVITY_MODELS = Object.freeze([
|
|
67
|
-
{ id: 'gemini-3-pro-high', name: 'Gemini 3 Pro High', provider: 'antigravity-oauth', contextWindow: 1048576 },
|
|
68
|
-
{ id: 'gemini-3-pro-low', name: 'Gemini 3 Pro Low', provider: 'antigravity-oauth', contextWindow: 1048576 },
|
|
69
|
-
{ id: 'gemini-3-flash-agent', name: 'Gemini 3 Flash', provider: 'antigravity-oauth', contextWindow: 1048576 },
|
|
70
|
-
{ id: 'claude-opus-4-6-thinking', name: 'Claude Opus 4.6 Thinking', provider: 'antigravity-oauth', contextWindow: 250000 },
|
|
71
|
-
{ id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6', provider: 'antigravity-oauth', contextWindow: 250000 },
|
|
72
|
-
{ id: 'claude-opus-4-5-thinking', name: 'Claude Opus 4.5 Thinking', provider: 'antigravity-oauth', contextWindow: 200000 },
|
|
73
|
-
{ id: 'claude-sonnet-4-5', name: 'Claude Sonnet 4.5', provider: 'antigravity-oauth', contextWindow: 1000000 },
|
|
74
|
-
]);
|
|
75
68
|
export const DEFAULT_ANTIGRAVITY_MODEL = ANTIGRAVITY_MODELS[0].id;
|
|
76
69
|
|
|
77
|
-
const ANTIGRAVITY_VERSION_FALLBACK = '
|
|
70
|
+
const ANTIGRAVITY_VERSION_FALLBACK = '2.8.0';
|
|
78
71
|
export const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
79
72
|
export const TOKEN_TIMEOUT_MS = 30_000;
|
|
80
73
|
export const PROJECT_TIMEOUT_MS = 30_000;
|
|
81
74
|
// Refresh ahead of expiry so an in-flight turn never posts a stale token.
|
|
82
75
|
export const TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1000;
|
|
83
76
|
|
|
77
|
+
// The backend gates models on the client version in the User-Agent, so the
|
|
78
|
+
// version tracks the latest Antigravity release via its update manifest.
|
|
79
|
+
// The pinned value is the offline fallback; the env override always wins.
|
|
80
|
+
const VERSION_MANIFEST_URL = 'https://antigravity-hub-auto-updater-974169037036.us-central1.run.app/manifest/latest-arm64-mac.yml';
|
|
81
|
+
const VERSION_FETCH_TIMEOUT_MS = 5_000;
|
|
82
|
+
const VERSION_RETRY_AFTER_MS = 10 * 60 * 1000;
|
|
83
|
+
let _discoveredVersion = null;
|
|
84
|
+
let _versionFetch = null;
|
|
85
|
+
let _versionFailedAt = 0;
|
|
86
|
+
|
|
84
87
|
function antigravityVersion() {
|
|
85
|
-
return String(process.env.MIXDOG_ANTIGRAVITY_VERSION || '').trim() || ANTIGRAVITY_VERSION_FALLBACK;
|
|
88
|
+
return String(process.env.MIXDOG_ANTIGRAVITY_VERSION || '').trim() || _discoveredVersion || ANTIGRAVITY_VERSION_FALLBACK;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Version from an electron-builder update manifest, or null when absent. */
|
|
92
|
+
export function parseAntigravityManifestVersion(text) {
|
|
93
|
+
for (const line of String(text || '').split(/\r?\n/)) {
|
|
94
|
+
const match = /^\s*version\s*:\s*(?:"([^"]*)"|'([^']*)'|([^\s#]+))\s*(?:#.*)?$/.exec(line);
|
|
95
|
+
if (!match) continue;
|
|
96
|
+
const version = (match[1] ?? match[2] ?? match[3] ?? '').trim();
|
|
97
|
+
return /^\d+\.\d+\.\d+$/.test(version) ? version : null;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Resolve the current client version once per process. A failed lookup keeps
|
|
104
|
+
* the pinned fallback and is not retried for a while, so an unreachable
|
|
105
|
+
* manifest host never delays every request.
|
|
106
|
+
*/
|
|
107
|
+
export function ensureAntigravityVersion({ fetchFn = fetch } = {}) {
|
|
108
|
+
if (process.env.MIXDOG_ANTIGRAVITY_VERSION || _discoveredVersion) return Promise.resolve(antigravityVersion());
|
|
109
|
+
if (_versionFetch) return _versionFetch;
|
|
110
|
+
if (Date.now() - _versionFailedAt < VERSION_RETRY_AFTER_MS) return Promise.resolve(antigravityVersion());
|
|
111
|
+
_versionFetch = (async () => {
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetchFn(VERSION_MANIFEST_URL, {
|
|
114
|
+
headers: { 'Cache-Control': 'no-cache', 'User-Agent': 'electron-builder' },
|
|
115
|
+
signal: AbortSignal.timeout(VERSION_FETCH_TIMEOUT_MS),
|
|
116
|
+
});
|
|
117
|
+
if (res.ok) _discoveredVersion = parseAntigravityManifestVersion(await res.text());
|
|
118
|
+
} catch {
|
|
119
|
+
// The pinned fallback stays valid when discovery fails.
|
|
120
|
+
} finally {
|
|
121
|
+
if (!_discoveredVersion) _versionFailedAt = Date.now();
|
|
122
|
+
_versionFetch = null;
|
|
123
|
+
}
|
|
124
|
+
return antigravityVersion();
|
|
125
|
+
})();
|
|
126
|
+
return _versionFetch;
|
|
86
127
|
}
|
|
87
128
|
|
|
88
|
-
function
|
|
89
|
-
|
|
129
|
+
export function _resetAntigravityVersionForTest() {
|
|
130
|
+
_discoveredVersion = null;
|
|
131
|
+
_versionFetch = null;
|
|
132
|
+
_versionFailedAt = 0;
|
|
90
133
|
}
|
|
91
134
|
|
|
92
135
|
/**
|
|
93
|
-
*
|
|
94
|
-
* client
|
|
136
|
+
* Cloud Code Assist expects the hub client identity rather than Electron's
|
|
137
|
+
* browser headers. Keep the captured client platform independent of the host.
|
|
95
138
|
*/
|
|
96
139
|
export function antigravityHeaders() {
|
|
97
140
|
return {
|
|
98
|
-
'User-Agent': `
|
|
99
|
-
+ `Antigravity/${antigravityVersion()} Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36`,
|
|
100
|
-
'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',
|
|
101
|
-
'Client-Metadata': JSON.stringify({
|
|
102
|
-
ideType: 'ANTIGRAVITY',
|
|
103
|
-
platform: osPlatformTag(),
|
|
104
|
-
pluginType: 'GEMINI',
|
|
105
|
-
}),
|
|
141
|
+
'User-Agent': `antigravity/hub/${antigravityVersion()} (aidev_client; os_type=darwin; arch=arm64; cl=963137146)`,
|
|
106
142
|
};
|
|
107
143
|
}
|
|
108
144
|
|
|
109
145
|
/** `loadCodeAssist` / `onboardUser` metadata block. */
|
|
110
|
-
export function codeAssistMetadata(
|
|
111
|
-
|
|
112
|
-
ideType: 'ANTIGRAVITY',
|
|
113
|
-
platform: osPlatformTag(),
|
|
114
|
-
pluginType: 'GEMINI',
|
|
115
|
-
};
|
|
116
|
-
if (projectId) metadata.duetProject = projectId;
|
|
117
|
-
return metadata;
|
|
146
|
+
export function codeAssistMetadata() {
|
|
147
|
+
return { ideType: 'ANTIGRAVITY' };
|
|
118
148
|
}
|
|
119
149
|
|
|
120
150
|
// --- Token store ---
|