Repository navigation
Releases
v1.20.0
Second of the two PRs that replace #91 (docs/DESIGN-attachments.md holds the model this code keeps).
Added
asktakesattachments: up to 8 image or PDF files (png, jpg, jpeg, webp, pdf; 10 MB each, 14 MB together —
this server's cap, under every figure Google publishes; base64 adds a third), sent asinlineDataparts before the
question in the user turn, with or without a Context Cache (a cache hit without attachments still sends the bare
prompt). They belong to one question, never to the workspace cache. A path must be inside the workspace (the same
realpath jail asask_agentic, its secret rules included), a symlink is refused, every file is checked (type, size,
readability) bystatbefore any is read, read through an O_NOFOLLOW descriptor whose device and inode must be the
ones recorded at inspection (a file swapped, replaced, deleted or grown in between is refused), and its first bytes
must match the declared type. Their tokens are what Gemini counts: one freecountTokenscall on the parts, whose
total enters the size preflight, the budget reservation and the TPM throttle (measured 2026-10-01: a 1×1 PNG is
1090 tokens on gemini-3-flash and 259 on 2.5-flash-lite, so no local figure would do); a count that cannot be
obtained refuses the call (ATTACHMENT_TOKENS_UNCOUNTED, retryable). Inspection, read and count happen before the
preflight and every reservation, so a bad file costs nothing: a model without vision isATTACHMENTS_UNSUPPORTED,
a bad fileATTACHMENT_INVALID(retryable: false), a request whose conservative size (the attachments encoded, the
workspace bodies with their markers, the prompt, the system instruction, the JSON framing) would cross Google's
20 MB inline limitREQUEST_TOO_LARGE, theask_agenticfallback cannot carry them
and says so.
structuredContent.attachmentslists what was sent andattachmentTokenswhat Gemini counted. Thelatest-vision
alias finally has an input to see.
Changed
- The Flash-Lite models count as vision-capable (Google lists image and PDF input on them;
countTokenson
gemini-2.5-flash-litecounted an inline PNG and PDF on 2026-10-01), soattachmentswork on a Flash-Lite model
named explicitly or throughlatest-lite(latest-visionkeeps resolving among the reasoning and fast tiers).
v1.19.0
From the 2026-10-01 comparison with Google's own tools and the community (docs/COMPETITION-2026-10-01.md).
Added
serviceTieronaskandcode(standard|flex), with the operator defaultGEMINI_CODE_CONTEXT_SERVICE_TIER
(trimmed, case-insensitive; an invalid value warns on stderr and is standard):flexis Google's half-price tier
(longer latency). The cost estimate, the usage ledger and the daily budget use the flex price. A request flex refuses
under load (429/503) is NOT retried by this server — the network retry covers connection failures only; the result
saysRATE_LIMIT/OVERLOADED,retryable: truewith Google's retry hint when it sent one (a 429/503 on the
stale-cache retry is reported as itself), and the client retries; a flex request is sent exactly once — the server's
own network retry is off for it, every send being billed. Reported asserviceTieron every result of a call that reached model resolution, errors and
timeouts included (the requested tier until the model is resolved; the earliest validation errors — workspace,
thinking budget — carry no tier); a failure before any response (a reset, the
300 s header wait on a queued flex request) isNETWORK_ERROR,retryable: true; theask_agenticfallback runs standard and says so (progress line, warning,
serviceTierDowngraded). Not available on the Vertex AI backend (the SDK sends no tier there, js-genai#1468): a
per-callflexis refused by name, a default offlexruns standard with a startup warning. Registered follow-ups:
DEF-FLEX-01 (the fetch headers timeout on flex), DEF-FLEX-02 (the usage rows do not record the tier yet).
v1.18.0
Fixed
codereads the configured default model (GEMINI_CODE_CONTEXT_DEFAULT_MODEL, then the credentials profile), asaskandask_agenticalways did; before, acodecall withoutmodelusedlatest-pro-thinkingwhatever the operator configured. A configured default that resolves but cannot reason or think (a flash or lite tier set for cheaperaskcalls, or a model without thinking) is replaced bylatest-pro-thinkingforcode, with a warning in the log, a progress line, andconfiguredModelReplacedin the response, so an audit can tell a fallback from a caller's choice; a configured default that does not resolve at all failscodeas it failsask. A per-callmodelstill wins and is never replaced; an emptymodelis a schema error. Themodeldescriptions ofcodeandask_agenticsay so. Five tests pin it.
v1.17.1
Changed
- Every public claim brought to what the code does (audit of the npm page, 2026-10-01): the context window is 1M tokens, not 2M; the default caching mode is
implicit(since v1.14.0) and the README, the caching page, theaskdescription and the MCP registry manifest say so, with the explicit Context Cache as the documented option (GEMINI_CODE_CONTEXT_CACHING_MODE,cachingMode); the default model islatest-pro-thinkingeverywhere (--help, theinitprompt, README,server.json, getting-started); one measurement with its date and mode replaces the five repeat-query figures; the cost-model numbers are labelled as explicit-mode measurements and the estimator's 25 % fallback is named; the secret denylist is listed as the sandbox checks it (basenames, extensions and directories);codeis said to take only a per-callmodel(it does not readGEMINI_CODE_CONTEXT_DEFAULT_MODEL— registered as a follow-up);onWorkspaceTooLargeisask's alone; the no-progress threshold is 5; per-tool parameters are listed; the comparison withgemini-mcp-toolis re-measured on 2026-10-01 (its 1.1.8 release and answered issues) and the dead-dependency row is gone; the release-cadence and "production-deployed / daily" sentences are gone; the 48-hour SLA is an aim; one contact domain. - README: a
claude mcp addone-liner, a supported-platforms note (better-sqlite3prebuilt bindings), absolute links to the docs so the npm page renders them;docs/configuration.mddocumentsGEMINI_CODE_CONTEXT_AGENTIC_STALL_MSandGEMINI_CODE_CONTEXT_AGENTIC_TOOL_CONCURRENCY;docs/getting-started.mdhas the one-liner, six tools and no stale timings;docs/cost-model.md,docs/security.md,docs/architecture.mdanddocs/migration-from-jamubc.mdsay which mode their figures and uploads belong to. package.jsondescription and keywords describe 1.17 (no2m-context, nofiles-api); names thebinpath asdist/index.js— the form npm publishes, so a publish prints no auto-correction.
v1.17.0
Changed
better-sqlite313 (was 12.11.1). Its native binding uses N-API and ships inside the package for macOS (arm64, x64),
Linux glibc and musl (arm64, x64) and Windows (arm64, x64): installing no longer runsprebuild-installor downloads
a binary, installs with--ignore-scriptswork, and a Node.js upgrade no longer breaks the binding ("compiled against
a different Node.js version"). On any other platform the package still installs, but no binding is compiled any more:
the server stops when it opens its database, with an error that names the platform and how to build the binding
(npm run build-releasein the better-sqlite3 package directory). The unpacked dependency is 27.3 MB (12.11.1:
10.4 MB plus the one binary it downloaded). Bundled SQLite 3.53.4.- Contributors add an entry to
CHANGELOG.mdfor a user-visible change; the unused@changesets/clidev
dependency is gone (the repository never had a.changeset/setup, and releases take their notes from this file). - Releases are staged on npm instead of published directly: the release workflow runs
npm stage publish
(Trusted Publishing, provenance), and a version becomes public only when a maintainer approves it on
npmjs.com with two-factor authentication; the workflow pins npm 11.20.0 for it (staged publishing needs 11.15.0+). - The README opens with the Quantum Media Technologies wordmark and closes with a "Made by" line, both linking to www.qmediat.io/open-source;
package.jsonhomepagepoints there andauthoris the company line every qmediat package uses. SECURITY.mdanddocs/security.mdname the company security mailboxsecurity@qmediat.io(one address across
every published qmediat package) next to GitHub's private reporting form.README.mdgains a "Trademarks and affiliation" section: Gemini is Google's trademark; this integration is
independent and not endorsed by Google; use of the Gemini API through it is subject to Google's terms.- Development: the project builds with TypeScript 7 (the native compiler). The published JavaScript is byte-identical
to 1.16.4 exceptdist/manifest/db.js(the better-sqlite3 error above); the type declarations describe the same types (only the order of union members and properties and the
quote style differ) and the source maps are regenerated. The TypeScript 7 npm package ships onlytsc— no
tsserverand no JavaScript API — so an editor set to use the workspace TypeScript version cannot load it from
node_modules; use the editor's bundled one. - The release workflow asks npm once whether the version is already public and stops on any answer other than yes
or no (a registry error used to read as "not public"); a new GitHub Release is a draft until the version is public,
and a published one is never withdrawn on npm's answer (the run warns); the npm it runs is pinned by version and by
the sha512 of its tarball; a failed stage is named in the run summary.
v1.16.4
Fixed
latest-flashresolved to an Interactions-API-only model and every call failed with HTTP 400. Google's
gemini-omni-*family (gemini-omni-flash-preview,gemini-omni-1.1-flash) is served only by the Interactions
API, yetListModelsadvertisesgenerateContentfor it, and the registry's version-descending sort places
omniahead of everygemini-N.M-flash. The taxonomy now classifiesomniasagent(not drop-in replaceable)
with an anchored rule, andlatest-flashresolves togemini-flash-latest
again (Google's rolling alias —gemini-3.8-flashon 2026-09-23). Regression tests cover both IDs.gemini-pro-latestandgemini-flash-latest(Google's rolling aliases) were
verified against the live API and are unaffected.
Changed
server.json(MCP registry manifest) is bumped with the package, and the release workflow refuses a tag whose
manifest versions differ frompackage.json.CONTRIBUTING.mdand the PR template asked for a changeset; the repository never had a.changeset/setup and
the CHANGELOG is the release record, so both now ask for a CHANGELOG entry.
Security
- Dependency refresh to close every open Dependabot advisory (69 alerts, 25 high — all transitive):
@modelcontextprotocol/sdk1.30.0,@google/genai1.52.0,hono4.13.8,fast-uri3.1.8,qs6.16.0,
body-parser2.3.0,ip-address10.7.2,protobufjs7.6.6,ws8.21.3.npm auditreports 0 vulnerabilities.
The published floors are raised to the versions this release was tested with (@modelcontextprotocol/sdk^1.30.0,
@google/genai^1.52.0). The advisories are in transitive dependencies under caret ranges, so a project that
already has this package in its lockfile keeps its old tree until it runsnpm update(ornpm audit fix);
a fresh install gets the refreshed tree. - CI and the release workflow refuse a build with a known high-severity advisory in the shipped dependency tree
(npm audit --omit=dev --audit-level=high; the dev toolchain stays covered by Dependabot alerts);
the CI token is read-only (permissions: contents: read). - Dependabot security updates are enabled on the repository.
SECURITY.md: the supported-versions table still described the pre-1.0 state; it now names the latest 1.x
release as the supported one and points to GitHub's private vulnerability reporting form first.
v1.16.3 — ask_agentic streaming hotfix (collectStream parts accumulation)
Fixed — ask_agentic streaming hotfix (combined: empty-text terminator + multi-chunk parts fragmentation, HOTFIX)
v1.16.3 ships TWO related fixes for the v1.16.2 streaming regression, folded into a single release because the second was discovered during end-to-end MCP smoke-test of the first.
Fix #1 — empty-text-terminator regression
Reproduction: v1.16.2 ask_agentic returned (model returned empty response) on every tool-using prompt against live Gemini Pro / Flash. Symptoms surfaced minutes after v1.16.2 published, on the first end-to-end smoke test from the global install.
Root cause: Gemini's actual stream protocol for tool-using turns emits TWO chunks:
- Chunk 1:
{ candidates: [{ content: { parts: [{ functionCall: {...} }] } }] }(the actual tool call) - Chunk 2:
{ candidates: [{ content: { parts: [{ text: '' }] }, finishReason: 'STOP' }] }(terminator)
v1.16.2's collectStream gate required parts.length > 0 to preserve a chunk's lastCandidates. Chunk 2 has parts.length === 1 (one element with empty text + finishReason), so the gate ACCEPTED it and overwrote chunk 1's functionCall. The agentic loop then saw zero functionCallParts and routed to the final-text path, returning the empty string fallback.
Fix #2 — multi-chunk parts fragmentation (T35)
Reproduction: during end-to-end MCP smoke-test of Fix #1 against live Gemini Pro, single-FC prompts (Tests 1–4) all passed. Test 5 (multi-file prompt: "read package.json, server.json, CHANGELOG.md") failed with Gemini 400: Function call default_api:read_file ... missing thought_signature, position 2.
Root cause: Fix #1 used a content-bearing-parts gate over lastCandidates = chunk.candidates last-write-wins semantics. When Gemini emits PARALLEL functionCall Parts across separate chunks, each chunk's parts overwrite the prior chunk's. The Gemini-3 contract (https://ai.google.dev/gemini-api/docs/thought-signatures) mandates that the thought signature on the FIRST functionCall part be returned in the next-turn replay. Last-write-wins drops chunk-1's signature; iteration 2's generateContent sees a model turn whose first FC has no signature; Gemini 400s. Pre-existing fragility — masked by Fix #1's complete failure mode in v1.16.2 (no FCs ever made it through the loop, so multi-FC was never reached).
Fix: collectStream no longer gates or overwrites — it ACCUMULATES parts across every chunk verbatim. Empty-text terminator parts, parallel functionCall Parts, and standalone signature-bearing parts are all preserved exactly as Gemini emitted them. The candidate scaffold (finishReason, safetyRatings, groundingMetadata, citationMetadata) stays last-write-wins; on stream exit we synthesise the final candidates shape by overlaying the accumulated parts onto the last seen scaffold.
This also closes the documented code tool fragility (T35 secondary): executableCode in chunk 1 + codeExecutionResult in chunk 2 now both reach code.tool.ts's extractor.
Pre-existing in v1.16.2 only. v1.16.1 and earlier used non-streaming generateContent for ask_agentic — never affected by either fix.
Why v1.16.2's review cycle missed Fix #1
The v1.16.2 PR went through the standard review cycle: 3-way Round-1 (gemini + grok + codex), 3-way Round-2 verification, Copilot review, all CI checks green. Gemini Round-1 specifically called out this CLASS of bug (terminator-only chunks dropping functionCall) and a fix was folded — but the fix targeted the SHAPE parts: [] (empty array) and missed the empirical SHAPE parts: [{ text: '' }] (length-1 array with empty content). Unit tests pinned the [] shape but not the [{ text: '' }] shape because no one captured a raw stream from live Gemini before merging.
Why v1.16.3 hotfix-A's first MCP smoke pass missed Fix #2
A single-FC smoke (Test 1) was sufficient to confirm Fix #1 against live Gemini. Multi-FC parallel was filed as T35 (deferred-with-documentation) on the explicit reasoning "currently zero empirical reports". Tests 1–4 of the documented end-to-end smoke plan passed; Test 5 (multi-file) was the first empirical report — fold it in same release.
Process update: the new test/integration/real-gemini.smoke.test.ts (v1.15.3 P10) will be extended in a follow-up PR with env-gated streaming + tool-calling smokes for BOTH single-FC and parallel-multi-FC prompts. A pre-merge live smoke covering both would have caught Fix #2 in seconds.
Coverage
773 tests pass (766 → 773 across the full release, including the R1 + R2 fold-cycle pins documented below — net +7 over v1.16.2). The 5 T35-specific regression pins cover the multi-chunk parts fragmentation cases: parallel-FC across 3 chunks pinning thoughtSignature on FC1, executableCode + codeExecutionResult cross-chunk, standalone signature on empty-text terminator, content.role synthesis, candidates-undefined preservation for naked-text streams.
R1 fold-cycle (4-way: GPT + Gemini + Grok + Copilot)
- Fold A (GPT F1 / Gemini F1 / Grok F2 — 3-way consensus, /6step PARTIAL Medium): synthesised candidates collapsed to single-element array, narrowing the pre-T35
lastCandidates = chunk.candidatescontract. Per-indexMap<number, Part[]>accumulation +Map<number, Candidate>scaffold tracking restore multi-candidate preservation for any future caller that setscandidateCount > 1(today none does —code.tool.ts:727already iterates ALL candidates so the type narrowing was a real regression-in-waiting). - Fold B (GPT F2 — TP Medium): added
it('preserves multi-candidate streams (candidateCount>1) with per-index parts accumulation')regression pin. - Fold D (Grok F1 — PARTIAL Low): retitled the empty-text-terminator regression test to mention both the hotfix-A history and the T35 strengthening explicitly.
- Fold G (Copilot — TP Medium): reconciled CHANGELOG T36 status with
docs/FOLLOW-UP-PRS.md(T36 is RESOLVED-BY-T35, not "remains tracked"). - Fold H (Copilot — TP Low): updated
ask-agentic.test.ts:2469comment narrating "content-bearing gate" to describe the actual T35 accumulation behaviour. - Accept-deferred C (Grok F3 — TP Medium): end-to-end multi-FC + thoughtSignature test through
askAgenticTool.execute. Covered at the collector contract level bystream-collector.test.ts:189. Live-API integration smoke is the documented next-PR scope (see Fix #2 closing notes above). - Accept E (Grok F4 — Low): test count "766 → 771" verified empirically post-R1 fold becomes 766 → 772; the "5 regression pins" wording in the original Coverage section refers to T35-specific pins — accurate at that scope.
- Accept F (Grok F5 — Low): Keep-a-Changelog Unreleased + per-version compare-link not added; project's prior versions (v1.0.0 → v1.16.2) follow the same convention of only emitting the
[1.0.0]link.
R2 fold-cycle (3-way: GPT + Gemini + Grok)
- Fold I (GPT R2 F1 / Gemini R2 F1 — 2-way HIGH consensus, /6step TP Medium): the R1-fold per-index Maps were keyed by the loop variable
i(array position) rather thancand.index ?? i. Per@google/genaiSDKCandidate.indexdoc: "The 0-based index of this candidate in the list of generated responses. Useful for distinguishing between multiple candidates when candidate_count > 1." Sparse stream emission likecandidates: [{ index: 1, content: ... }](only index 1, not 0) would have cross-wired that part into bucket 0. Fixed: keys now usecand.index ?? i, and the synth.sort()is now load-bearing because Map insertion order can diverge from ordinal index under sparse emission. - Fold J (GPT R2 F2 / Grok R2 F1 — TP Low): added
it('keys multi-candidate buckets by Candidate.index, not array position (R2 fold — sparse emission)')regression pin that emits sparse + reverse-Map-insertion-order chunks and asserts ordinal output ordering with no cross-bucket bleed. - Accept K (Grok R2 F2 — Nit): the
.sort()onArray.from(Map.entries())was flagged as redundant under R1's keying (Map preserves insertion order from the ascending-i loop). After Fold I the sort BECOMES load-bearing for sparse emission, so it stays — comment added explaining why.
Notes
- Anyone on v1.16.2 with
ask_agenticcalls in production should upgrade IMMEDIATELY. Tool-calling iterations were silently failing on every prompt. - T35 (true parts accumulation) — closed in this release. T36 (
toolCallpredicate completeness for future Gemini server-side tools) is also resolved by T35 in v1.16.3 — the accumulation contract preserves any Part shape verbatim (functionCall, toolCall, executableCode, codeExecutionResult, thoughtSignature) without per-shape gating. Seedocs/FOLLOW-UP-PRS.md"T36" for the full rationale. - v1.6/v1.7 streaming refactor track still considered shipped — both fixes are defects in the v1.16.2 agentic-loop refactor, fixed in place.
v1.16.2 — ask_agentic loop streaming + stallMs heartbeat watchdog (T33 TRIM)
Changed — ask_agentic loop streaming + heartbeat-aware stallMs watchdog (T33 TRIM)
ask_agentic's main loop now uses generateContentStream + collectStream instead of non-streaming generateContent. Closes T33 — the last open item in the v1.6/v1.7 streaming refactor track. The post-loop forced-finalization rescue keeps the v1.15.0 P3 apiCallSucceeded non-streaming contract unchanged.
Why now: ask_agentic was the last tool in the codebase still using non-streaming dispatch. Operator consequences:
- No heartbeat-aware liveness watchdog. Only wall-clock
iterationTimeoutMswas available. A dead socket at second 5 of a 120s-budgeted iter wasted 115 seconds before the wall-clock fired.stallMs(the right knob perask/codev1.12.0 conventions) closes that gap. - No live thought-chunk progress visibility. During the 30-120s thinking window each iter, operators saw only iter-counter notifications between iters.
ask+codealready stream thought previews live; this brings parity. - UX inconsistency — every other long-running tool in the codebase streams.
ask_agenticwas the outlier.
What's new
stallMsper-call schema field (1s–10min, optional) — heartbeat-aware stall watchdog that resets on every chunk yielded bygenerateContentStream(text or thought). Fires ONLY when the stream goes silent for this long. Does NOT fire while the model is actively thinking. Documented as the right knob for "kill dead sockets quickly without penalising deep reasoning."GEMINI_CODE_CONTEXT_AGENTIC_STALL_MSenv var — fallback path identical toask/code(GEMINI_CODE_CONTEXT_ASK_STALL_MS,GEMINI_CODE_CONTEXT_CODE_STALL_MS). Recommended:60_000(60s).structuredContent.timeoutKindonerrorCode: 'TIMEOUT'—'stall' | 'total'discriminates which watchdog fired.timeoutMsreports the configured TOTAL wall-clock cap (ornullwhen disabled) — verbatim mirror ofask.tool.ts:1132; the newstallMssibling field reports the configured stall watchdog (ornullwhen disabled). Wrappers can apply different retry policies based ontimeoutKind—'stall'is usually safe to retry (dead socket);'total'means raiseiterationTimeoutMsor narrow the prompt.- Live
thinking: …progress events — same UX as ask/code, throttled to ~1.5s bycollectStreamso the MCP host's progress channel doesn't get flooded on long thinking bursts.
What's preserved verbatim (TRIM scope)
The v1.6/v1.7 plan recommended converting BOTH the loop AND the rescue to streaming. After cross-reviewer consultation (gcc-ask: GO full scope; grok: TRIM), TRIM was adopted:
- Rescue keeps
generateContent(not streaming) — see the dispatch site atsrc/tools/ask-agentic.tool.ts(search for the rescue's// Each agentic iteration is its owncomment). The v1.15.0 P3+P4+P5apiCallSucceededcontract — Google billed → finalize budget; not billed → cancel — was stabilized through 4 review rounds +/6stepcross-corroboration. Streaming the rescue would re-open that recently-stabilized surface for marginal liveness gain on a once-per-call path. - Mid-stream failures cannot be retried (Gemini's
generateContentStreamhas no resume) — the v1.6/v1.7 plan §11 explicitly cites this. The rescue's transient-failure budget routing depends on knowing whether the API was billed; partial-stream-then-fail blurs that signal. The loop tolerates a discarded mid-stream failure (next iteration retries the question); the rescue is the last shot before failing the whole call. - Content-aware
signatureCountsdedupe (v1.16.0) unchanged —collectStreamreassembles into the sameGenerateContentResponseshape;iterResult.signaturesextraction is identical. withNetworkRetryouter wrap unchanged — wraps stream OPENING only, mirroring ask/code.iterDispatchedflag (v1.14.x H2) unchanged — set immediately beforegenerateContentStream(which IS the dispatch).createTimeoutControllerrescue site explicitly keepsstallEnvVar: ''— proves rescue is non-streaming and stall is intentionally disabled there.
Backward compatibility
- No breaking change.
stallMsis an optional new schema field; existing callers that don't set it see identical behavior to v1.16.1. structuredContentshape:timeoutMscontinues to report the configured TOTAL wall-clock cap (ornullwhen disabled) — verbatim mirror ofask.tool.ts:1132. New sibling fieldstallMsreports the configured stall watchdog (ornullwhen disabled). The discriminator for which one fired is the newtimeoutKind: 'stall' | 'total'field. Wrappers that branched ontimeoutMs > Xpre-v1.16.2 see a stable contract — onlynull(disabled) is a new case, and existing branches handle it correctly becausenull > Xis alwaysfalse.- Rescue path completely unchanged. Operators who hit the rescue see byte-identical behavior to v1.16.1 (
convergenceForced,rescueErrorCode, etc.).
PR-Round-1 review folds (gemini Finding #1 + #2)
Round-1 cross-review (gemini-code-context + grok + codex) flagged two empirically verifiable issues. Both folded same PR per workflow:
- Finding #1 (HIGH) —
collectStreamcandidate-preservation defensive gap (src/tools/shared/stream-collector.ts:174-176). Pre-fix gate waschunk.candidates.length > 0(outer-array length only). If Gemini's stream protocol ever emits a final terminator shape like{ candidates: [{ content: { parts: [] }, finishReason: 'STOP' }] }, the previous chunk'sfunctionCallPart would be silently overwritten — the agentic loop would treat the iteration as final-text and never dispatch the tool. Fixed by adding aparts.length > 0gate on the inner array. Empirical current-protocol behaviour (functionCall + finishReason packed into one chunk) is preserved either way; this hardens against fragmentation patterns that future Gemini SDK / thinking-mode variants could legitimately introduce. Side-effect onask/code:lastCandidates.finishReasonmay now come from a non-final chunk if the final chunk has no parts — observably equivalent for both, since they read accumulatedresponse.text, notcandidates[0].finishReason. - Finding #2 (MEDIUM) —
timeoutMspayload contract divergence from ask/code (src/tools/ask-agentic.tool.ts). Initial commit collapsedtimeoutMsto "the limit that fired" (stall value when stall fired).ask.tool.ts:1132always reports the configured total wall-clock cap regardless of which watchdog fired — the discriminator istimeoutKind. Reverted to ask/code parity:timeoutMs= configured total (or null),stallMs= configured stall (or null),timeoutKind= which one fired.
Two new regression tests pin the fixes:
test/unit/stream-collector.test.ts—does not overwrite with terminator-only candidates that have empty parts (v1.16.2 fragmentation guard)— exercises the multi-chunk fragmentation pattern at the collector level.test/unit/ask-agentic.test.ts—dispatches functionCall correctly when stream fragments fc into one chunk and terminator into the next (v1.16.2 PR-Round-1)— exercises the same pattern at the agentic-loop level, asserting thatread_fileactually dispatches and the loop reaches organic final-text.
Coverage
764 passed | 9 skipped (759 → 762 in initial commit; 762 → 764 after Round-1 folds: added 2 regression tests for the fragmentation guard + adjusted 2 stallMs tests for the corrected timeoutMs semantics). All v1.15.0 rescue tests + v1.16.0 content-aware NO_PROGRESS tests still green — proves the TRIM scope-exclusion held.
Notes
- The v1.6/v1.7 streaming refactor track is now FULLY SHIPPED across v1.6.0 (T19 timeout + AbortController), v1.7.0 (T20 ask/code streaming + T18 budget + D#7 status UX), and v1.16.2 (T33 ask_agentic loop streaming).
- T34+ items in
docs/FOLLOW-UP-PRS.mdare smaller / unrelated.
v1.16.1 — AGENTIC_NO_PROGRESS message enrichment (R2 close)
Highlights
Closes the v1.14.4-cycle followup R2 (structuredContent dropped on MCP error channel).
/6step verdict: PARTIAL TP — consumer-side rendering, not our bug. Wire format correctly preserves both structuredContent and isError per MCP spec (verified empirically — CallToolResultSchema defines them as independent optional fields). The "loss" operators saw on 2026-04-29 + 2026-04-30 benchmarks is consumer-side: Claude Code displays message-only on isError results.
Workaround we control: enrich the AGENTIC_NO_PROGRESS message string with iterations + filesRead so they're visible regardless of consumer rendering. structuredContent payload stays intact for any consumer that reads it.
Format
Before (v1.16.0):
... was repeated N times without new file reads between repeats. Returning partial state.
After (v1.16.1):
... was repeated N times without new file reads between repeats (after K iter, X files read). Returning partial state.
Tests
759 passed | 9 skipped (no count change). Regex-based pin assertions added to existing test.
Compatibility
No API/schema/code change beyond the one message-format string + 2 test assertions. structuredContent fields completely unchanged.
Install
npm install -g @qmediat.io/gemini-code-context-mcp@1.16.1v1.16.0 — content-aware NO_PROGRESS dedupe (P2 Phase B)
Highlights
The v1.14.4 threshold bump (3 → 5) was a stop-gap. v1.16.0 ships the planned Phase B content-aware refactor:
Same call signature counts toward the no-progress threshold ONLY if no
read_fileagainst an unseen path happened between repeats. IffilesReadSetgrew between identical signatures, count resets to 1 — the model is exploring, not stuck.
Closes today's empirical regression where Gemini 3 Pro under HIGH thinking re-runs the same grep while ALSO reading new files between iterations. That IS progress; the simple counter (v1.14.4) tripped before maxIterations. Phase B distinguishes "exploring with repeated lookups" from "genuinely stuck".
Changes
signatureCountsMap type changed<string, number>→<string, { count: number; lastFilesReadSize: number }>.- Per-iter snapshot of
filesReadSet.sizetaken once after tool execution; used to decide reset-vs-increment per signature. - AA1 fix folded (3-of-3 cross-corroborated TP HIGH, grok escalated): within-iter parallel duplicates dedupe via
SetBEFORE counting. Pathological 5× same signature in one batch counts as +1, not +5. - Threshold (5) unchanged. Hard caps unchanged.
- Error message updated:
"... repeated N times without new file reads between repeats".
Tests
759 passed | 9 skipped (was 756 in v1.15.3 — +3 net new test cases). Lint + typecheck green on Node 22 + Node 24.
Compatibility
No API/schema change. Behaviour change observable only in dedupe-trip decision: agentic calls that previously bailed at NO_PROGRESS while reading new files now run to completion (or to maxIterations + rescue).
Future-proof note
read_file is the sole progress oracle because ask_agentic's tool surface is intentionally read-only. When a future PR adds workspace-mutating tools (write_file, terminal exec), the oracle MUST be expanded — guidance documented in code.
Install
npm install -g @qmediat.io/gemini-code-context-mcp@1.16.0