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
|
@@ -37,10 +37,18 @@ import {
|
|
|
37
37
|
DEFAULT_ANTIGRAVITY_MODEL,
|
|
38
38
|
antigravityHeaders,
|
|
39
39
|
ensureAccessToken,
|
|
40
|
+
ensureAntigravityVersion,
|
|
40
41
|
hasAntigravityOAuthCredentials,
|
|
41
42
|
loadTokens,
|
|
42
43
|
_scrubTokens,
|
|
43
44
|
} from './antigravity-oauth-tokens.mjs';
|
|
45
|
+
import {
|
|
46
|
+
antigravityModelCache,
|
|
47
|
+
antigravityQuotaWindows,
|
|
48
|
+
fetchAvailableModels,
|
|
49
|
+
normalizeAntigravityCatalog,
|
|
50
|
+
resolveAntigravityWireModel,
|
|
51
|
+
} from './antigravity-oauth-catalog.mjs';
|
|
44
52
|
|
|
45
53
|
const CLAUDE_THINKING_BETA = 'interleaved-thinking-2025-05-14';
|
|
46
54
|
|
|
@@ -61,9 +69,16 @@ function antigravityError(res, text, endpoint) {
|
|
|
61
69
|
}
|
|
62
70
|
// Account verification is terminal and actionable: surface the URL Google
|
|
63
71
|
// returns instead of a raw API body the user cannot act on.
|
|
64
|
-
|
|
72
|
+
// Google puts the link in the error details, not the message text.
|
|
73
|
+
const validationDetail = Array.isArray(detail?.details)
|
|
74
|
+
? detail.details.find((entry) => entry?.reason === 'VALIDATION_REQUIRED' && typeof entry.metadata?.validation_url === 'string')
|
|
75
|
+
: null;
|
|
76
|
+
const validationUrl = validationDetail?.metadata.validation_url
|
|
77
|
+
|| /https:\/\/\S*(?:accounts|console)\.google\.com\/\S+/.exec(message || '')?.[0]
|
|
78
|
+
|| '';
|
|
65
79
|
if (res.status === 403 && /VALIDATION_REQUIRED/i.test(text || '')) {
|
|
66
|
-
err.message = `Antigravity requires account verification${validationUrl ? `: ${validationUrl
|
|
80
|
+
err.message = `Antigravity requires account verification${validationUrl ? `: open ${validationUrl} , complete the check, then retry` : ''}`;
|
|
81
|
+
err.validationUrl = validationUrl || undefined;
|
|
67
82
|
err.unsafeToRetry = true;
|
|
68
83
|
}
|
|
69
84
|
return err;
|
|
@@ -90,6 +105,28 @@ function antigravityFailoverEligible(err) {
|
|
|
90
105
|
return status === 0;
|
|
91
106
|
}
|
|
92
107
|
|
|
108
|
+
async function storedAuth(options) {
|
|
109
|
+
const tokens = await ensureAccessToken(options);
|
|
110
|
+
return { accessToken: tokens.access_token, projectId: tokens.project_id, email: tokens.email || '' };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// A retired wire id answers with one plain-text notice and no finishReason.
|
|
114
|
+
// That is a terminal answer about the model, not a truncated stream.
|
|
115
|
+
const RETIRED_MODEL_NOTICE = /\bno longer (?:available|supported)\b/i;
|
|
116
|
+
|
|
117
|
+
function retiredModelError(err, streamedText, model) {
|
|
118
|
+
if (!(err?.code === 'TRUNCATED_STREAM' && /no finishReason/.test(String(err?.message || '')))) return null;
|
|
119
|
+
const text = String(streamedText || '').trim();
|
|
120
|
+
if (!RETIRED_MODEL_NOTICE.test(text)) return null;
|
|
121
|
+
return Object.assign(new Error(`Antigravity retired ${model}: ${text}`), {
|
|
122
|
+
code: 'MODEL_RETIRED',
|
|
123
|
+
status: 404,
|
|
124
|
+
httpStatus: 404,
|
|
125
|
+
unsafeToRetry: true,
|
|
126
|
+
modelRetired: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
93
130
|
export class AntigravityOAuthProvider {
|
|
94
131
|
// usageMetadata.promptTokenCount is the total, cached tokens included.
|
|
95
132
|
static inputExcludesCache = false;
|
|
@@ -101,7 +138,12 @@ export class AntigravityOAuthProvider {
|
|
|
101
138
|
this._preconnect = typeof config.preconnectFn === 'function' ? config.preconnectFn : preconnect;
|
|
102
139
|
// AuthStorage equivalent: refreshes the token and resolves the project.
|
|
103
140
|
// Injectable so wire-shape tests do not need a real credential store.
|
|
104
|
-
|
|
141
|
+
// The store keeps snake_case fields; requests read the camelCase view.
|
|
142
|
+
this._ensureAuth = typeof config.ensureAuthFn === 'function' ? config.ensureAuthFn : storedAuth;
|
|
143
|
+
// Client version for the hub identity header, discovered once per process.
|
|
144
|
+
this._ensureVersion = typeof config.ensureVersionFn === 'function'
|
|
145
|
+
? config.ensureVersionFn
|
|
146
|
+
: () => ensureAntigravityVersion({ fetchFn: this._fetch });
|
|
105
147
|
// Remember the endpoint that last answered so a session stops paying the
|
|
106
148
|
// failover cost on every turn.
|
|
107
149
|
this._lastGoodEndpoint = null;
|
|
@@ -123,20 +165,37 @@ export class AntigravityOAuthProvider {
|
|
|
123
165
|
}
|
|
124
166
|
|
|
125
167
|
async send(messages, model, tools, sendOpts = {}) {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
const
|
|
168
|
+
const signal = sendOpts?.signal || null;
|
|
169
|
+
// Picker ids name a tier family; the wire id carries the chosen effort.
|
|
170
|
+
const route = resolveAntigravityWireModel(model || DEFAULT_ANTIGRAVITY_MODEL, sendOpts?.effort);
|
|
171
|
+
const useModel = route.model;
|
|
172
|
+
const opts = route.effort === sendOpts?.effort
|
|
173
|
+
? (sendOpts || {})
|
|
174
|
+
: { ...sendOpts, effort: route.effort, thinkingLevel: route.effort == null ? null : sendOpts?.thinkingLevel };
|
|
129
175
|
const onToolCall = typeof opts.onToolCall === 'function' ? opts.onToolCall : null;
|
|
130
|
-
|
|
176
|
+
// Streamed text is kept so a retirement notice can be told apart from
|
|
177
|
+
// a truncated stream when the gateway omits the finishReason.
|
|
178
|
+
let streamedText = '';
|
|
179
|
+
const onTextDelta = typeof opts.onTextDelta === 'function'
|
|
180
|
+
? (text) => { if (typeof text === 'string') streamedText += text; opts.onTextDelta(text); }
|
|
181
|
+
: null;
|
|
131
182
|
const onStreamDelta = typeof opts.onStreamDelta === 'function' ? opts.onStreamDelta : null;
|
|
132
183
|
if (signal?.aborted) {
|
|
133
184
|
const reason = signal.reason;
|
|
134
185
|
throw reason instanceof Error ? reason : new Error('Antigravity request aborted');
|
|
135
186
|
}
|
|
136
187
|
|
|
137
|
-
|
|
188
|
+
this._preconnect(this._endpointOrder()[0]);
|
|
189
|
+
// Normalize history while version discovery and token refresh run.
|
|
190
|
+
// The authenticated project is bound only after those tasks finish.
|
|
191
|
+
const [, auth, request] = await Promise.all([
|
|
192
|
+
this._ensureVersion(),
|
|
193
|
+
this._ensureAuth({ fetchFn: this._fetch }),
|
|
194
|
+
Promise.resolve().then(() => this._buildBody(messages, useModel, tools, opts)),
|
|
195
|
+
]);
|
|
138
196
|
this._projectId = auth.projectId;
|
|
139
|
-
|
|
197
|
+
request.project = auth.projectId;
|
|
198
|
+
const body = JSON.stringify(request);
|
|
140
199
|
const headers = {
|
|
141
200
|
Authorization: `Bearer ${auth.accessToken}`,
|
|
142
201
|
'Content-Type': 'application/json',
|
|
@@ -146,6 +205,42 @@ export class AntigravityOAuthProvider {
|
|
|
146
205
|
};
|
|
147
206
|
|
|
148
207
|
let textLeakGuard = null;
|
|
208
|
+
let terminalFailure = null;
|
|
209
|
+
let streamedParts = [];
|
|
210
|
+
let streamedNativeToolCalls = [];
|
|
211
|
+
const seenNativeToolIds = new Set();
|
|
212
|
+
const emittedToolIds = new Set();
|
|
213
|
+
const dispatchToolCall = onToolCall ? (call) => {
|
|
214
|
+
// A failure already observed in this chunk must win over both
|
|
215
|
+
// native calls and calls recovered from its text.
|
|
216
|
+
if (terminalFailure || emittedToolIds.has(call.id)) return;
|
|
217
|
+
emittedToolIds.add(call.id);
|
|
218
|
+
onToolCall(call);
|
|
219
|
+
} : null;
|
|
220
|
+
const onChunk = (chunk) => {
|
|
221
|
+
const candidate = chunk?.candidates?.[0];
|
|
222
|
+
const finishReason = candidate?.finishReason
|
|
223
|
+
|| (chunk?.promptFeedback?.blockReason ? `PROMPT_${chunk.promptFeedback.blockReason}` : null);
|
|
224
|
+
if (finishReason && String(finishReason).replace(/^FINISH_REASON_/, '') !== 'STOP') {
|
|
225
|
+
terminalFailure ||= finishReason;
|
|
226
|
+
}
|
|
227
|
+
if (!onToolCall) return;
|
|
228
|
+
const parts = candidate?.content?.parts ?? [];
|
|
229
|
+
streamedParts.push(...parts);
|
|
230
|
+
if (terminalFailure || !parts.some((part) => part?.functionCall)) return;
|
|
231
|
+
// Parse against the turn's parts so anonymous call IDs keep the
|
|
232
|
+
// same ordinal as final parsing, even across separate SSE chunks.
|
|
233
|
+
const fresh = (parseToolCalls(streamedParts) || []).filter((call) => {
|
|
234
|
+
if (seenNativeToolIds.has(call.id)) return false;
|
|
235
|
+
seenNativeToolIds.add(call.id);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
const calls = textLeakGuard?.enabled
|
|
239
|
+
? textLeakGuard.filterNativeToolCalls(fresh)
|
|
240
|
+
: fresh;
|
|
241
|
+
if (calls?.length) streamedNativeToolCalls.push(...calls);
|
|
242
|
+
emitGeminiToolCalls(calls, dispatchToolCall);
|
|
243
|
+
};
|
|
149
244
|
const passthrough = createPassthroughSignal(signal);
|
|
150
245
|
const endpoints = this._endpointOrder();
|
|
151
246
|
let lastErr = null;
|
|
@@ -185,21 +280,45 @@ export class AntigravityOAuthProvider {
|
|
|
185
280
|
textLeakGuard = createGeminiTextLeakGuard({
|
|
186
281
|
knownToolNames: tools?.map((t) => t.name).filter(Boolean) ?? [],
|
|
187
282
|
onTextDelta,
|
|
188
|
-
onToolCall,
|
|
283
|
+
onToolCall: dispatchToolCall,
|
|
189
284
|
onStreamDelta,
|
|
190
285
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
286
|
+
streamedText = '';
|
|
287
|
+
terminalFailure = null;
|
|
288
|
+
streamedParts = [];
|
|
289
|
+
streamedNativeToolCalls = [];
|
|
290
|
+
seenNativeToolIds.clear();
|
|
291
|
+
emittedToolIds.clear();
|
|
292
|
+
try {
|
|
293
|
+
return await consumeGeminiRestStreamResponse(res, {
|
|
294
|
+
signal: attemptSignal,
|
|
295
|
+
onStreamDelta,
|
|
296
|
+
onTextDelta,
|
|
297
|
+
onChunk,
|
|
298
|
+
textLeakGuard,
|
|
299
|
+
label: 'Antigravity streamGenerateContent',
|
|
300
|
+
// Cloud Code Assist nests the Gemini payload under
|
|
301
|
+
// `response`; in-band error events stay top level.
|
|
302
|
+
unwrapChunk: (chunk) => (chunk && typeof chunk === 'object' && chunk.response
|
|
303
|
+
? chunk.response
|
|
304
|
+
: chunk),
|
|
305
|
+
});
|
|
306
|
+
} catch (streamErr) {
|
|
307
|
+
const error = retiredModelError(streamErr, streamedText, useModel) || streamErr;
|
|
308
|
+
// Native calls now run before EOF. Preserve
|
|
309
|
+
// their history and prohibit resampling after
|
|
310
|
+
// a tool callback, including on another host.
|
|
311
|
+
if (emittedToolIds.size) {
|
|
312
|
+
error.emittedToolCall = true;
|
|
313
|
+
error.unsafeToRetry = true;
|
|
314
|
+
const leaked = textLeakGuard.getLeakedToolCalls();
|
|
315
|
+
error.partialToolCalls = [...streamedNativeToolCalls, ...leaked];
|
|
316
|
+
const replay = createProviderReplay('antigravity', leaked.length ? [] : streamedParts);
|
|
317
|
+
if (replay) replay.requestContext = { model: useModel };
|
|
318
|
+
if (replay) error.partialProviderReplay = replay;
|
|
319
|
+
}
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
203
322
|
},
|
|
204
323
|
{
|
|
205
324
|
signal: passthrough.signal,
|
|
@@ -273,19 +392,23 @@ export class AntigravityOAuthProvider {
|
|
|
273
392
|
const leakedToolCalls = textLeakGuard?.getLeakedToolCalls() ?? [];
|
|
274
393
|
const providerReplay = createProviderReplay(
|
|
275
394
|
'antigravity',
|
|
276
|
-
leakedToolCalls.length ? [] : responseParts,
|
|
395
|
+
leakedToolCalls.length || rawContent !== content ? [] : responseParts,
|
|
277
396
|
);
|
|
278
|
-
|
|
279
|
-
|
|
397
|
+
// Thought signatures are only valid for the model family that minted
|
|
398
|
+
// them; the request builder consults this when the route changes.
|
|
399
|
+
if (providerReplay) providerReplay.requestContext = { model: useModel };
|
|
400
|
+
let nativeToolCalls = onToolCall
|
|
401
|
+
? (streamedNativeToolCalls.length ? streamedNativeToolCalls : undefined)
|
|
402
|
+
: parseToolCalls(responseParts);
|
|
403
|
+
if (!onToolCall && textLeakGuard?.enabled) nativeToolCalls = textLeakGuard.filterNativeToolCalls(nativeToolCalls);
|
|
280
404
|
let toolCalls = nativeToolCalls;
|
|
281
405
|
if (leakedToolCalls.length) {
|
|
282
406
|
toolCalls = toolCalls?.length ? [...toolCalls, ...leakedToolCalls] : leakedToolCalls;
|
|
283
407
|
}
|
|
284
408
|
const citations = collectGeminiGroundingSources(candidate);
|
|
285
|
-
emitGeminiToolCalls(nativeToolCalls, onToolCall);
|
|
286
409
|
|
|
287
410
|
const promptBlockReason = response.promptFeedback?.blockReason || null;
|
|
288
|
-
const finishReason = candidate?.finishReason || (promptBlockReason ? `PROMPT_${promptBlockReason}` : null);
|
|
411
|
+
const finishReason = terminalFailure || candidate?.finishReason || (promptBlockReason ? `PROMPT_${promptBlockReason}` : null);
|
|
289
412
|
const normalizedFinish = String(finishReason || '').replace(/^FINISH_REASON_/, '');
|
|
290
413
|
if (finishReason && normalizedFinish !== 'STOP') {
|
|
291
414
|
throw Object.assign(new Error(`Antigravity response incomplete: finishReason=${finishReason}`), {
|
|
@@ -299,6 +422,7 @@ export class AntigravityOAuthProvider {
|
|
|
299
422
|
providerMetadata,
|
|
300
423
|
model: useModel,
|
|
301
424
|
rawUsage: response.usageMetadata || null,
|
|
425
|
+
...(emittedToolIds.size ? { emittedToolCall: true, unsafeToRetry: true } : {}),
|
|
302
426
|
});
|
|
303
427
|
}
|
|
304
428
|
|
|
@@ -337,12 +461,50 @@ export class AntigravityOAuthProvider {
|
|
|
337
461
|
};
|
|
338
462
|
}
|
|
339
463
|
|
|
340
|
-
|
|
341
|
-
|
|
464
|
+
async _fetchRawModels(signal = null) {
|
|
465
|
+
await this._ensureVersion();
|
|
466
|
+
const auth = await this._ensureAuth({ fetchFn: this._fetch });
|
|
467
|
+
return fetchAvailableModels({
|
|
468
|
+
accessToken: auth.accessToken,
|
|
469
|
+
projectId: auth.projectId,
|
|
470
|
+
fetchFn: this._fetch,
|
|
471
|
+
signal,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Live catalog with a disk cache; the curated list covers an offline or
|
|
476
|
+
// signed-out daemon so the picker never goes empty.
|
|
342
477
|
async listModels() {
|
|
478
|
+
const cached = antigravityModelCache.loadSync();
|
|
479
|
+
if (cached) return cached;
|
|
480
|
+
try {
|
|
481
|
+
const models = normalizeAntigravityCatalog(await this._fetchRawModels());
|
|
482
|
+
if (models.length) {
|
|
483
|
+
antigravityModelCache.save(models);
|
|
484
|
+
return models;
|
|
485
|
+
}
|
|
486
|
+
} catch (err) {
|
|
487
|
+
console.warn(`[antigravity-oauth] catalog refresh failed, using the curated list: ${err?.message || err}`);
|
|
488
|
+
}
|
|
343
489
|
return ANTIGRAVITY_MODELS;
|
|
344
490
|
}
|
|
345
491
|
|
|
492
|
+
async _refreshModelCache() {
|
|
493
|
+
const models = normalizeAntigravityCatalog(await this._fetchRawModels());
|
|
494
|
+
if (!models.length) throw new Error('[antigravity-oauth] fetchAvailableModels listed no chat models');
|
|
495
|
+
antigravityModelCache.save(models);
|
|
496
|
+
return models;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async getUsageSnapshot() {
|
|
500
|
+
return {
|
|
501
|
+
provider: this.name,
|
|
502
|
+
model: null,
|
|
503
|
+
source: 'antigravity-models',
|
|
504
|
+
quotaWindows: antigravityQuotaWindows(await this._fetchRawModels()),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
346
508
|
async isAvailable() {
|
|
347
509
|
try {
|
|
348
510
|
if (!hasAntigravityOAuthCredentials()) return false;
|
|
@@ -10,10 +10,19 @@ export function isAntigravityClaude(model) {
|
|
|
10
10
|
return /^claude-/i.test(String(model || ''));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// A replay recorded by the other model family (Claude vs Gemini) carries
|
|
14
|
+
// signatures the target model rejects; rebuild it from the plain history
|
|
15
|
+
// with the recovery sentinel instead.
|
|
16
|
+
function replayFamilyMatches(message, model) {
|
|
17
|
+
const recorded = message?.providerReplay?.requestContext?.model;
|
|
18
|
+
if (typeof recorded !== 'string' || !recorded) return true;
|
|
19
|
+
return isAntigravityClaude(recorded) === isAntigravityClaude(model);
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
function signatureSafeMessages(messages, model) {
|
|
14
23
|
return messages.map(message => {
|
|
15
24
|
if (message?.role !== 'assistant') return message;
|
|
16
|
-
const ownParts = providerReplayItems(message, 'antigravity');
|
|
25
|
+
const ownParts = replayFamilyMatches(message, model) ? providerReplayItems(message, 'antigravity') : undefined;
|
|
17
26
|
// Repair tool signatures only after the complete history establishes
|
|
18
27
|
// the active turn, not while normalizing an isolated older message.
|
|
19
28
|
const parts = ownParts || toGeminiContents([message], model, { repairToolSignatures: false })[0]?.parts;
|
|
@@ -60,20 +69,12 @@ export function buildAntigravityRequest(messages, model, tools, opts = {}, proje
|
|
|
60
69
|
thinkingConfig.thinkingBudget = value;
|
|
61
70
|
}
|
|
62
71
|
} else {
|
|
72
|
+
// Tiered wire ids (gemini-3.8-flash-high) already encode the thinking
|
|
73
|
+
// level; the provider resolves them from the catalog and clears the
|
|
74
|
+
// effort, so only bare ids reach this field.
|
|
63
75
|
thinkingConfig = geminiThinkingConfig(model, opts, {
|
|
64
76
|
includeThoughts: /^gemini-3/i.test(model) ? true : undefined,
|
|
65
77
|
});
|
|
66
|
-
// This gateway selects Gemini Pro thinking tiers by model ID; Flash
|
|
67
|
-
// uses the bare model ID plus the ordinary thinkingLevel field.
|
|
68
|
-
if (thinkingConfig?.thinkingLevel && /^gemini-3(?:\.\d+)?-pro(?:-|$)/i.test(model)) {
|
|
69
|
-
const level = thinkingConfig.thinkingLevel;
|
|
70
|
-
if (!['low', 'high'].includes(level)) {
|
|
71
|
-
throw new TypeError('Antigravity Gemini Pro supports low/high model tiers.');
|
|
72
|
-
}
|
|
73
|
-
model = `${model.replace(/-(?:low|medium|high)$/i, '')}-${level}`;
|
|
74
|
-
} else if (thinkingConfig?.thinkingLevel && /^gemini-3(?:\.\d+)?-flash(?:-|$)/i.test(model)) {
|
|
75
|
-
model = model.replace(/-(?:minimal|low|medium|high)$/i, '');
|
|
76
|
-
}
|
|
77
78
|
}
|
|
78
79
|
const generationConfig = {
|
|
79
80
|
...(claude ? { maxOutputTokens: CLAUDE_MAX_OUTPUT_TOKENS } : {}),
|
|
@@ -4,7 +4,6 @@ const SCHEMA_ANNOTATIONS = new Set([
|
|
|
4
4
|
'$schema',
|
|
5
5
|
'default',
|
|
6
6
|
'deprecated',
|
|
7
|
-
'description',
|
|
8
7
|
'examples',
|
|
9
8
|
'readOnly',
|
|
10
9
|
'title',
|
|
@@ -105,13 +104,6 @@ function flattenCursorTopLevelCompoundSchema(schema) {
|
|
|
105
104
|
};
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
function conciseDescription(value) {
|
|
109
|
-
const normalized = String(value || '').replace(/\s+/g, ' ').trim();
|
|
110
|
-
if (normalized.length <= 120) return normalized;
|
|
111
|
-
const sentence = normalized.match(/^.{24,117}?[.!?](?:\s|$)/)?.[0]?.trim();
|
|
112
|
-
return sentence || `${normalized.slice(0, 117)}...`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
107
|
export function prepareCursorToolDefinition(tool) {
|
|
116
108
|
const fn = tool?.function || tool || {};
|
|
117
109
|
const inputSchema = fn.parameters && typeof fn.parameters === 'object'
|
|
@@ -119,7 +111,7 @@ export function prepareCursorToolDefinition(tool) {
|
|
|
119
111
|
: { type: 'object', properties: {} };
|
|
120
112
|
return {
|
|
121
113
|
name: fn.name,
|
|
122
|
-
description:
|
|
114
|
+
description: String(fn.description || ''),
|
|
123
115
|
inputSchema,
|
|
124
116
|
};
|
|
125
117
|
}
|
|
@@ -30,8 +30,11 @@ const SCHEMAS = {
|
|
|
30
30
|
['mimeType', 7, 'string'],
|
|
31
31
|
['data', 8, 'bytes'],
|
|
32
32
|
]),
|
|
33
|
-
UserMessageAction: fields([
|
|
34
|
-
|
|
33
|
+
UserMessageAction: fields([
|
|
34
|
+
['userMessage', 1, 'UserMessage'],
|
|
35
|
+
['requestContext', 2, 'RequestContext'],
|
|
36
|
+
]),
|
|
37
|
+
ResumeAction: fields([['requestContext', 2, 'RequestContext']]),
|
|
35
38
|
ConversationAction: fields([
|
|
36
39
|
['userMessageAction', 1, 'UserMessageAction'],
|
|
37
40
|
['resumeAction', 2, 'ResumeAction'],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto, { createHash } from 'node:crypto';
|
|
2
2
|
import http2 from 'node:http2';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
4
|
import {
|
|
4
5
|
AUTO_MODEL,
|
|
5
6
|
FALLBACK_MODELS,
|
|
@@ -380,6 +381,7 @@ function buildToolDefinitions(tools = []) {
|
|
|
380
381
|
description: prepared.description,
|
|
381
382
|
inputSchema: encodeJsonValue(prepared.inputSchema),
|
|
382
383
|
inputSchemaJson: JSON.stringify(prepared.inputSchema),
|
|
384
|
+
inputSchemaObject: prepared.inputSchema,
|
|
383
385
|
providerIdentifier: 'mixdog',
|
|
384
386
|
toolName: prepared.name,
|
|
385
387
|
};
|
|
@@ -405,6 +407,33 @@ function requestModelParameters(body) {
|
|
|
405
407
|
.filter((entry) => entry.id);
|
|
406
408
|
}
|
|
407
409
|
|
|
410
|
+
function buildRequestContext(tools, cloudRule) {
|
|
411
|
+
return {
|
|
412
|
+
tools,
|
|
413
|
+
mcpInstructions: tools.length ? [{
|
|
414
|
+
serverName: 'mixdog',
|
|
415
|
+
instructions: 'Use the tools provided by the mixdog MCP server for this task. '
|
|
416
|
+
+ 'Follow their descriptions and input schemas. Prefer them over Cursor native tools; '
|
|
417
|
+
+ 'native tools do not directly access the Mixdog environment. '
|
|
418
|
+
+ 'If a needed tool is not available, use the provided tool discovery mechanism rather than inventing a tool.',
|
|
419
|
+
}] : [],
|
|
420
|
+
...(cloudRule ? { cloudRule } : {}),
|
|
421
|
+
fileContents: {},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function canReuseRun(active, { tools, cloudRule, modelParameters, maxMode }) {
|
|
426
|
+
const toolContract = (definitions) => new Map(definitions.map((tool) => [
|
|
427
|
+
tool.name,
|
|
428
|
+
{ description: tool.description, inputSchema: tool.inputSchemaObject },
|
|
429
|
+
]));
|
|
430
|
+
const parameters = (values) => new Map(values.map(({ id, value }) => [id, value]));
|
|
431
|
+
return (active.cloudRule || '') === (cloudRule || '')
|
|
432
|
+
&& isDeepStrictEqual(toolContract(active.tools), toolContract(tools))
|
|
433
|
+
&& isDeepStrictEqual(parameters(active.modelParameters || []), parameters(modelParameters))
|
|
434
|
+
&& (active.maxMode === true) === maxMode;
|
|
435
|
+
}
|
|
436
|
+
|
|
408
437
|
const conversations = new Map();
|
|
409
438
|
const activeRuns = new Map();
|
|
410
439
|
const MEMORY_TTL_MS = 30 * 60_000;
|
|
@@ -522,11 +551,8 @@ function storeBlob(conversation, bytes) {
|
|
|
522
551
|
|
|
523
552
|
function buildRunRequest({ model, modelParameters = [], maxMode = false, systems, history, userText, userImages = [], tools, conversation }) {
|
|
524
553
|
assertCursorUserImages(userImages);
|
|
525
|
-
const
|
|
554
|
+
const requestContext = buildRequestContext(tools, systems.join('\n\n') || undefined);
|
|
526
555
|
const rootPromptMessagesJson = [];
|
|
527
|
-
for (const content of prompts) {
|
|
528
|
-
rootPromptMessagesJson.push(storeBlob(conversation, textEncoder.encode(JSON.stringify({ role: 'system', content }))));
|
|
529
|
-
}
|
|
530
556
|
for (const entry of history) {
|
|
531
557
|
rootPromptMessagesJson.push(storeBlob(conversation, textEncoder.encode(JSON.stringify(entry))));
|
|
532
558
|
}
|
|
@@ -535,6 +561,7 @@ function buildRunRequest({ model, modelParameters = [], maxMode = false, systems
|
|
|
535
561
|
const action = userText || userImages.length
|
|
536
562
|
? {
|
|
537
563
|
userMessageAction: {
|
|
564
|
+
requestContext,
|
|
538
565
|
userMessage: {
|
|
539
566
|
text: userText,
|
|
540
567
|
messageId: crypto.randomUUID(),
|
|
@@ -550,7 +577,7 @@ function buildRunRequest({ model, modelParameters = [], maxMode = false, systems
|
|
|
550
577
|
},
|
|
551
578
|
},
|
|
552
579
|
}
|
|
553
|
-
: { resumeAction: {} };
|
|
580
|
+
: { resumeAction: { requestContext } };
|
|
554
581
|
return encodeMessage('AgentClientMessage', {
|
|
555
582
|
runRequest: {
|
|
556
583
|
conversationState: stateBytes,
|
|
@@ -560,13 +587,12 @@ function buildRunRequest({ model, modelParameters = [], maxMode = false, systems
|
|
|
560
587
|
maxMode: maxMode === true,
|
|
561
588
|
parameters: modelParameters,
|
|
562
589
|
},
|
|
563
|
-
mcpTools: { mcpTools: tools },
|
|
564
590
|
conversationId: conversation.id,
|
|
565
591
|
// NOTE: AgentRunRequest.customSystemPrompt (field 8) is a dead
|
|
566
592
|
// channel on the current cloud endpoint: the server maps it to an
|
|
567
593
|
// internal `--system-prompt` agent flag its binary rejects with
|
|
568
|
-
// invalid_argument (400).
|
|
569
|
-
// requestContext
|
|
594
|
+
// invalid_argument (400). Send the harness once through the
|
|
595
|
+
// action's requestContext.cloudRule, not through root history too.
|
|
570
596
|
},
|
|
571
597
|
});
|
|
572
598
|
}
|
|
@@ -671,15 +697,7 @@ function handleExecMessage(bridge, exec, tools, cloudRule, onToolCall) {
|
|
|
671
697
|
if (exec.requestContextArgs) {
|
|
672
698
|
sendExecResult(bridge, exec, 'requestContextResult', {
|
|
673
699
|
success: {
|
|
674
|
-
requestContext:
|
|
675
|
-
tools,
|
|
676
|
-
mcpInstructions: [],
|
|
677
|
-
// Cursor's rules channel: without it the server-side agent
|
|
678
|
-
// treats the client as ruleless and applies only its own
|
|
679
|
-
// harness policy (user report: policies ignored vs direct).
|
|
680
|
-
...(cloudRule ? { cloudRule } : {}),
|
|
681
|
-
fileContents: {},
|
|
682
|
-
},
|
|
700
|
+
requestContext: buildRequestContext(tools, cloudRule),
|
|
683
701
|
},
|
|
684
702
|
});
|
|
685
703
|
return;
|
|
@@ -989,6 +1007,8 @@ function createStreamResponse({
|
|
|
989
1007
|
tools,
|
|
990
1008
|
cloudRule,
|
|
991
1009
|
model,
|
|
1010
|
+
modelParameters = [],
|
|
1011
|
+
maxMode = false,
|
|
992
1012
|
key,
|
|
993
1013
|
// Owning session, carried into the stored run so a session close can find
|
|
994
1014
|
// and tear down the pending batch's bridge/heartbeat.
|
|
@@ -1072,6 +1092,8 @@ function createStreamResponse({
|
|
|
1072
1092
|
conversation,
|
|
1073
1093
|
tools,
|
|
1074
1094
|
cloudRule,
|
|
1095
|
+
modelParameters,
|
|
1096
|
+
maxMode,
|
|
1075
1097
|
sessionId,
|
|
1076
1098
|
pending: state.pending,
|
|
1077
1099
|
sawTurnEnded: state.sawTurnEnded,
|
|
@@ -1361,8 +1383,12 @@ export async function handleChatCompletion(body, accessToken) {
|
|
|
1361
1383
|
const maxMode = body.mixdog_max_mode === true;
|
|
1362
1384
|
const sessionId = String(body.mixdog_session_id || '');
|
|
1363
1385
|
const key = runKey(model, body.messages, sessionId);
|
|
1386
|
+
const selectedTools = selectToolsForChoice(body.tools, body.tool_choice);
|
|
1387
|
+
const toolDefinitions = buildToolDefinitions(selectedTools);
|
|
1388
|
+
const cloudRule = parsed.systems.join('\n\n') || undefined;
|
|
1364
1389
|
const active = activeRuns.get(key);
|
|
1365
|
-
if (active && parsed.toolResults.length && active.bridge.alive
|
|
1390
|
+
if (active && parsed.toolResults.length && active.bridge.alive
|
|
1391
|
+
&& canReuseRun(active, { tools: toolDefinitions, cloudRule, modelParameters, maxMode })) {
|
|
1366
1392
|
forgetActiveRun(key, active);
|
|
1367
1393
|
return resumeRun(active, parsed.toolResults, parsed.userText, model, key);
|
|
1368
1394
|
}
|
|
@@ -1373,8 +1399,6 @@ export async function handleChatCompletion(body, accessToken) {
|
|
|
1373
1399
|
}
|
|
1374
1400
|
const convKey = conversationKey(body.messages, sessionId);
|
|
1375
1401
|
const conversation = getConversation(convKey);
|
|
1376
|
-
const selectedTools = selectToolsForChoice(body.tools, body.tool_choice);
|
|
1377
|
-
const toolDefinitions = buildToolDefinitions(selectedTools);
|
|
1378
1402
|
const runInput = {
|
|
1379
1403
|
model,
|
|
1380
1404
|
modelParameters,
|
|
@@ -1392,12 +1416,11 @@ export async function handleChatCompletion(body, accessToken) {
|
|
|
1392
1416
|
bridge,
|
|
1393
1417
|
heartbeat,
|
|
1394
1418
|
conversation,
|
|
1395
|
-
tools: toolDefinitions
|
|
1396
|
-
|
|
1397
|
-
inputSchemaObject: selectedTools[index]?.function?.parameters || {},
|
|
1398
|
-
})),
|
|
1399
|
-
cloudRule: parsed.systems.join('\n\n') || undefined,
|
|
1419
|
+
tools: toolDefinitions,
|
|
1420
|
+
cloudRule,
|
|
1400
1421
|
model,
|
|
1422
|
+
modelParameters,
|
|
1423
|
+
maxMode,
|
|
1401
1424
|
key,
|
|
1402
1425
|
sessionId,
|
|
1403
1426
|
restart: ({ fromCheckpoint }) => startRun(
|
|
@@ -526,12 +526,36 @@ function convertSchema(schema) {
|
|
|
526
526
|
return result;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
// Gemini's legacy Schema requires typed values. An unconstrained JSON value
|
|
530
|
+
// (Office operation values, for example) cannot be replaced with a string or
|
|
531
|
+
// an untyped Schema node. FunctionDeclaration.parametersJsonSchema is the
|
|
532
|
+
// documented, mutually exclusive representation for the original JSON Schema.
|
|
533
|
+
function needsJsonParameters(schema) {
|
|
534
|
+
if (typeof schema === 'boolean') return true;
|
|
535
|
+
if (!schema || typeof schema !== 'object') return false;
|
|
536
|
+
// The legacy dialect has no reference vocabulary; preserve both pointers
|
|
537
|
+
// and their definitions together, including recursive MCP contracts.
|
|
538
|
+
if (['$ref', '$defs', 'definitions'].some((key) => Object.hasOwn(schema, key))) return true;
|
|
539
|
+
const constraints = Object.keys(schema)
|
|
540
|
+
.filter((key) => !['description', 'title', 'default', 'examples', '$comment'].includes(key));
|
|
541
|
+
if (!constraints.length || (schema.type === 'array' && !schema.items)) return true;
|
|
542
|
+
const children = [
|
|
543
|
+
...Object.values(schema.properties || {}),
|
|
544
|
+
...(schema.items ? [schema.items] : []),
|
|
545
|
+
...(typeof schema.additionalProperties === 'object' ? [schema.additionalProperties] : []),
|
|
546
|
+
...(schema.anyOf || []), ...(schema.oneOf || []), ...(schema.allOf || []),
|
|
547
|
+
];
|
|
548
|
+
return children.some(needsJsonParameters);
|
|
549
|
+
}
|
|
550
|
+
|
|
529
551
|
export function toGeminiTools(tools) {
|
|
530
552
|
return {
|
|
531
553
|
functionDeclarations: tools.map((t) => ({
|
|
532
554
|
name: t.name,
|
|
533
555
|
description: t.description,
|
|
534
|
-
|
|
556
|
+
...(needsJsonParameters(t.inputSchema)
|
|
557
|
+
? { parametersJsonSchema: structuredClone(t.inputSchema) }
|
|
558
|
+
: { parameters: convertSchema(t.inputSchema) }),
|
|
535
559
|
})),
|
|
536
560
|
};
|
|
537
561
|
}
|
|
@@ -832,7 +856,9 @@ export function parseToolCalls(parts) {
|
|
|
832
856
|
// deterministic id only for older responses that omit it.
|
|
833
857
|
id: typeof fc.id === 'string' && fc.id ? fc.id : `gemini_${idHash}`,
|
|
834
858
|
name: fc.name,
|
|
835
|
-
arguments
|
|
859
|
+
// Execution may normalize nested arguments in place. Keep native
|
|
860
|
+
// replay and anonymous ID inputs independent of that working copy.
|
|
861
|
+
arguments: structuredClone(fc.args ?? {}),
|
|
836
862
|
};
|
|
837
863
|
if (typeof sig === 'string' && sig && sig.length <= 16_384) {
|
|
838
864
|
call.thoughtSignature = sig;
|
|
@@ -352,7 +352,7 @@ export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolC
|
|
|
352
352
|
// deeper. Cloud Code Assist (Antigravity) streams `{ response: { candidates … } }`
|
|
353
353
|
// while the public API streams the candidates directly; everything downstream —
|
|
354
354
|
// aggregation, leak guard, watchdogs — stays shared.
|
|
355
|
-
export async function consumeGeminiRestStreamResponse(response, { signal, onStreamDelta, onTextDelta, textLeakGuard, label, unwrapChunk = null }) {
|
|
355
|
+
export async function consumeGeminiRestStreamResponse(response, { signal, onStreamDelta, onTextDelta, textLeakGuard, label, unwrapChunk = null, onChunk = null }) {
|
|
356
356
|
const unwrap = typeof unwrapChunk === 'function'
|
|
357
357
|
? (chunk) => { try { return unwrapChunk(chunk); } catch { return chunk; } }
|
|
358
358
|
: (chunk) => chunk;
|
|
@@ -467,6 +467,7 @@ export async function consumeGeminiRestStreamResponse(response, { signal, onStre
|
|
|
467
467
|
clearFirstByteTimer();
|
|
468
468
|
}
|
|
469
469
|
allChunks.push(parsed);
|
|
470
|
+
onChunk?.(parsed);
|
|
470
471
|
try { onStreamDelta?.(geminiChunkProgressKind(parsed)); } catch {}
|
|
471
472
|
if (!sawFunctionCall && geminiChunkHasFunctionCall(parsed)) sawFunctionCall = true;
|
|
472
473
|
if (onTextDelta || textLeakGuard) {
|
|
@@ -494,6 +495,7 @@ export async function consumeGeminiRestStreamResponse(response, { signal, onStre
|
|
|
494
495
|
clearFirstByteTimer();
|
|
495
496
|
}
|
|
496
497
|
allChunks.push(parsed);
|
|
498
|
+
onChunk?.(parsed);
|
|
497
499
|
try { onStreamDelta?.(geminiChunkProgressKind(parsed)); } catch {}
|
|
498
500
|
if (!sawFunctionCall && geminiChunkHasFunctionCall(parsed)) sawFunctionCall = true;
|
|
499
501
|
if (onTextDelta || textLeakGuard) {
|