@qwen-code/qwen-code 0.16.0 → 0.16.1-nightly.20260524.84f408017
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/chunks/{agent-K6OWOMBN.js → agent-QFQGDV7B.js} +2 -2
- package/chunks/{chunk-3T4ZT63H.js → chunk-6JQC5PYU.js} +359 -49
- package/chunks/{chunk-WCZWAKFG.js → chunk-JUAMIT3V.js} +440 -523
- package/chunks/{chunk-NOAHME6A.js → chunk-LKSMYCUK.js} +1 -1
- package/chunks/{chunk-VMOAQVBP.js → chunk-M2TQETH7.js} +1 -1
- package/chunks/chunk-TEES5V4K.js +956 -0
- package/chunks/{chunk-L34E6AGL.js → chunk-UL4NCM6X.js} +5 -5
- package/chunks/{contextCommand-7CPNXBLO.js → contextCommand-3FI5VRHY.js} +4 -4
- package/chunks/{edit-CBM5NDVK.js → edit-JXJILHZW.js} +2 -2
- package/chunks/{enter-worktree-XABKPLO6.js → enter-worktree-HQ3RBRIA.js} +2 -2
- package/chunks/{exit-worktree-56MN2PCL.js → exit-worktree-73TYZY5X.js} +2 -2
- package/chunks/{exitPlanMode-YDNPCSCJ.js → exitPlanMode-WEETA2GM.js} +2 -2
- package/chunks/{geminiContentGenerator-ZGPNBFDS.js → geminiContentGenerator-TVVMIHRE.js} +1 -1
- package/chunks/{glob-ZHA35VO5.js → glob-HZYBHMPB.js} +2 -2
- package/chunks/{grep-RV6V6T52.js → grep-RO7NJDWP.js} +2 -2
- package/chunks/{monitor-5G2OBGE5.js → monitor-IOU376UD.js} +2 -2
- package/chunks/{notebook-edit-XUBTCT6L.js → notebook-edit-7NXIUSLW.js} +2 -2
- package/chunks/{openaiContentGenerator-POYAZQ6I.js → openaiContentGenerator-BXS7CQJ2.js} +2 -2
- package/chunks/{qwenContentGenerator-2E4H56DK.js → qwenContentGenerator-7VOJGPBD.js} +3 -3
- package/chunks/{read-file-3JIOOXFT.js → read-file-G4TFJFZ2.js} +1 -1
- package/chunks/{ripGrep-LEI3L6PM.js → ripGrep-IMMYV3HQ.js} +2 -2
- package/chunks/{serve-CFVRMD4W.js → serve-5EHHVHBV.js} +4440 -360
- package/chunks/{shell-3B5DZ437.js → shell-AXZQIDDU.js} +2 -2
- package/chunks/{skill-STSZUBXR.js → skill-IMUVSRRZ.js} +1 -1
- package/chunks/{src-ROFXAPEP.js → src-VYNNTRVU.js} +4 -2
- package/chunks/{tool-search-ARWOD3GD.js → tool-search-BTGLGBDQ.js} +1 -1
- package/chunks/{write-file-6MRT7TEW.js → write-file-BNUIFGU4.js} +2 -2
- package/cli.js +624 -16436
- package/package.json +2 -2
- package/chunks/chunk-4J63U5QO.js +0 -1974
|
@@ -159,7 +159,7 @@ import {
|
|
|
159
159
|
truncateSpanError,
|
|
160
160
|
truncateToolOutput,
|
|
161
161
|
uiTelemetryService
|
|
162
|
-
} from "./chunk-
|
|
162
|
+
} from "./chunk-JUAMIT3V.js";
|
|
163
163
|
import {
|
|
164
164
|
DEFAULT_QWEN_EMBEDDING_MODEL,
|
|
165
165
|
DEFAULT_QWEN_MODEL
|
|
@@ -35751,6 +35751,128 @@ var InvalidStreamError = class extends Error {
|
|
|
35751
35751
|
this.type = type;
|
|
35752
35752
|
}
|
|
35753
35753
|
};
|
|
35754
|
+
var ORPHAN_TOOL_USE_REPAIR_REASON = "Tool execution result was not recorded \u2014 likely interrupted by network failure, abort, or process exit. Treat as failure and retry if needed.";
|
|
35755
|
+
function scanModelTurn(history, modelIdx) {
|
|
35756
|
+
const expected = /* @__PURE__ */ new Map();
|
|
35757
|
+
for (const part of history[modelIdx]?.parts ?? []) {
|
|
35758
|
+
const fc = part.functionCall;
|
|
35759
|
+
if (fc?.id) expected.set(fc.id, fc.name ?? "unknown");
|
|
35760
|
+
}
|
|
35761
|
+
const matched = /* @__PURE__ */ new Map();
|
|
35762
|
+
let scanIdx = modelIdx + 1;
|
|
35763
|
+
while (scanIdx < history.length && history[scanIdx]?.role === "user") {
|
|
35764
|
+
const parts2 = history[scanIdx].parts ?? [];
|
|
35765
|
+
for (let pIdx = 0; pIdx < parts2.length; pIdx++) {
|
|
35766
|
+
const part = parts2[pIdx];
|
|
35767
|
+
const id = part.functionResponse?.id;
|
|
35768
|
+
if (id) {
|
|
35769
|
+
const list3 = matched.get(id);
|
|
35770
|
+
if (list3) list3.push({ turnIdx: scanIdx, partIdx: pIdx, part });
|
|
35771
|
+
else matched.set(id, [{ turnIdx: scanIdx, partIdx: pIdx, part }]);
|
|
35772
|
+
}
|
|
35773
|
+
}
|
|
35774
|
+
scanIdx++;
|
|
35775
|
+
}
|
|
35776
|
+
return { modelIdx, expected, matched, scanEnd: scanIdx };
|
|
35777
|
+
}
|
|
35778
|
+
__name(scanModelTurn, "scanModelTurn");
|
|
35779
|
+
function planRepair(scan) {
|
|
35780
|
+
const synthesizeIds = [];
|
|
35781
|
+
const hoistedParts = [];
|
|
35782
|
+
const removalTargets = [];
|
|
35783
|
+
const droppedDuplicates = [];
|
|
35784
|
+
const adjacentIdx = scan.modelIdx + 1;
|
|
35785
|
+
for (const [id, name3] of scan.expected) {
|
|
35786
|
+
const locations = scan.matched.get(id);
|
|
35787
|
+
if (!locations || locations.length === 0) {
|
|
35788
|
+
synthesizeIds.push([id, name3]);
|
|
35789
|
+
continue;
|
|
35790
|
+
}
|
|
35791
|
+
const survivor = locations[0];
|
|
35792
|
+
if (survivor.turnIdx !== adjacentIdx) {
|
|
35793
|
+
hoistedParts.push(survivor.part);
|
|
35794
|
+
removalTargets.push({
|
|
35795
|
+
turnIdx: survivor.turnIdx,
|
|
35796
|
+
partIdx: survivor.partIdx
|
|
35797
|
+
});
|
|
35798
|
+
}
|
|
35799
|
+
for (let k = 1; k < locations.length; k++) {
|
|
35800
|
+
removalTargets.push({
|
|
35801
|
+
turnIdx: locations[k].turnIdx,
|
|
35802
|
+
partIdx: locations[k].partIdx
|
|
35803
|
+
});
|
|
35804
|
+
droppedDuplicates.push({ callId: id, name: name3 });
|
|
35805
|
+
}
|
|
35806
|
+
}
|
|
35807
|
+
return {
|
|
35808
|
+
modelIdx: scan.modelIdx,
|
|
35809
|
+
scanEnd: scan.scanEnd,
|
|
35810
|
+
synthesizeIds,
|
|
35811
|
+
hoistedParts,
|
|
35812
|
+
removalTargets,
|
|
35813
|
+
droppedDuplicates
|
|
35814
|
+
};
|
|
35815
|
+
}
|
|
35816
|
+
__name(planRepair, "planRepair");
|
|
35817
|
+
function applyRepair(history, plan, reason) {
|
|
35818
|
+
if (plan.synthesizeIds.length === 0 && plan.removalTargets.length === 0) {
|
|
35819
|
+
return { insertedBefore: 0 };
|
|
35820
|
+
}
|
|
35821
|
+
const syntheticParts = plan.synthesizeIds.map(([callId, name3]) => ({
|
|
35822
|
+
functionResponse: { id: callId, name: name3, response: { error: reason } }
|
|
35823
|
+
}));
|
|
35824
|
+
const partsToInject = [...syntheticParts, ...plan.hoistedParts];
|
|
35825
|
+
const removals = [...plan.removalTargets].sort((a, b) => {
|
|
35826
|
+
if (a.turnIdx !== b.turnIdx) return b.turnIdx - a.turnIdx;
|
|
35827
|
+
return b.partIdx - a.partIdx;
|
|
35828
|
+
});
|
|
35829
|
+
for (const loc of removals) {
|
|
35830
|
+
const turnParts = history[loc.turnIdx].parts;
|
|
35831
|
+
if (turnParts) turnParts.splice(loc.partIdx, 1);
|
|
35832
|
+
}
|
|
35833
|
+
const adjacentIdx = plan.modelIdx + 1;
|
|
35834
|
+
for (let j = plan.scanEnd - 1; j > adjacentIdx; j--) {
|
|
35835
|
+
if (history[j]?.role === "user" && (history[j].parts?.length ?? 0) === 0) {
|
|
35836
|
+
history.splice(j, 1);
|
|
35837
|
+
}
|
|
35838
|
+
}
|
|
35839
|
+
const next = history[adjacentIdx];
|
|
35840
|
+
if (next?.role === "user") {
|
|
35841
|
+
const existing = next.parts ?? [];
|
|
35842
|
+
const firstNonFr = existing.findIndex((part) => !part.functionResponse);
|
|
35843
|
+
const insertAt = firstNonFr === -1 ? existing.length : firstNonFr;
|
|
35844
|
+
next.parts = [
|
|
35845
|
+
...existing.slice(0, insertAt),
|
|
35846
|
+
...partsToInject,
|
|
35847
|
+
...existing.slice(insertAt)
|
|
35848
|
+
];
|
|
35849
|
+
return { insertedBefore: 0 };
|
|
35850
|
+
}
|
|
35851
|
+
history.splice(adjacentIdx, 0, { role: "user", parts: partsToInject });
|
|
35852
|
+
return { insertedBefore: 1 };
|
|
35853
|
+
}
|
|
35854
|
+
__name(applyRepair, "applyRepair");
|
|
35855
|
+
function repairOrphanedToolUseTurns(history, reason = ORPHAN_TOOL_USE_REPAIR_REASON) {
|
|
35856
|
+
const injected = [];
|
|
35857
|
+
const droppedDuplicates = [];
|
|
35858
|
+
for (let i2 = 0; i2 < history.length; i2++) {
|
|
35859
|
+
if (history[i2].role !== "model") continue;
|
|
35860
|
+
const scan = scanModelTurn(history, i2);
|
|
35861
|
+
if (scan.expected.size === 0) continue;
|
|
35862
|
+
const plan = planRepair(scan);
|
|
35863
|
+
if (plan.synthesizeIds.length === 0 && plan.removalTargets.length === 0) {
|
|
35864
|
+
continue;
|
|
35865
|
+
}
|
|
35866
|
+
const { insertedBefore } = applyRepair(history, plan, reason);
|
|
35867
|
+
for (const [callId, name3] of plan.synthesizeIds) {
|
|
35868
|
+
injected.push({ callId, name: name3 });
|
|
35869
|
+
}
|
|
35870
|
+
droppedDuplicates.push(...plan.droppedDuplicates);
|
|
35871
|
+
i2 += insertedBefore;
|
|
35872
|
+
}
|
|
35873
|
+
return { injected, droppedDuplicates };
|
|
35874
|
+
}
|
|
35875
|
+
__name(repairOrphanedToolUseTurns, "repairOrphanedToolUseTurns");
|
|
35754
35876
|
var SESSION_START_CONTEXT_SENTINEL_START = '<qwen:session-start-context hidden="true">';
|
|
35755
35877
|
var SESSION_START_CONTEXT_SENTINEL_END = "</qwen:session-start-context>";
|
|
35756
35878
|
var SESSION_START_CONTEXT_HEADER = "SessionStart additional context";
|
|
@@ -35826,6 +35948,23 @@ var GeminiChat = class {
|
|
|
35826
35948
|
* in a loop. Manual `/compress` still works (it passes `force=true`).
|
|
35827
35949
|
*/
|
|
35828
35950
|
hasFailedCompressionAttempt = false;
|
|
35951
|
+
/**
|
|
35952
|
+
* Partial-push markers — index of the in-memory `model[partial fc]`
|
|
35953
|
+
* and the matching deferred JSONL record. See the canonical note
|
|
35954
|
+
* above `ORPHAN_TOOL_USE_REPAIR_REASON` for the lifecycle and the
|
|
35955
|
+
* wedge they prevent.
|
|
35956
|
+
*/
|
|
35957
|
+
pendingPartialAssistantTurnIndex = null;
|
|
35958
|
+
pendingPartialAssistantRecord = null;
|
|
35959
|
+
/**
|
|
35960
|
+
* Reset both partial-push markers in lockstep. Every history-mutation
|
|
35961
|
+
* site uses this — single-field resets are a bug because the fields
|
|
35962
|
+
* are always paired by lifecycle.
|
|
35963
|
+
*/
|
|
35964
|
+
clearPendingPartialState() {
|
|
35965
|
+
this.pendingPartialAssistantTurnIndex = null;
|
|
35966
|
+
this.pendingPartialAssistantRecord = null;
|
|
35967
|
+
}
|
|
35829
35968
|
/**
|
|
35830
35969
|
* Most recent prompt-token count reported by the model for *this* chat,
|
|
35831
35970
|
* mirroring the value in {@link UiTelemetryService} for the main session.
|
|
@@ -35951,6 +36090,7 @@ var GeminiChat = class {
|
|
|
35951
36090
|
streamDoneResolver = resolve30;
|
|
35952
36091
|
});
|
|
35953
36092
|
this.sendPromise = streamDonePromise;
|
|
36093
|
+
this.clearPendingPartialState();
|
|
35954
36094
|
let compressionInfo;
|
|
35955
36095
|
let requestContents;
|
|
35956
36096
|
let userContentAdded = false;
|
|
@@ -35964,6 +36104,17 @@ var GeminiChat = class {
|
|
|
35964
36104
|
const userContent = createUserContent(params.message);
|
|
35965
36105
|
this.history.push(userContent);
|
|
35966
36106
|
userContentAdded = true;
|
|
36107
|
+
const inlineRepair = repairOrphanedToolUseTurns(this.history);
|
|
36108
|
+
if (inlineRepair.injected.length > 0) {
|
|
36109
|
+
debugLogger15.warn(
|
|
36110
|
+
`[REPAIR] sendMessageStream inline pass synthesized ${inlineRepair.injected.length} functionResponse(s): ` + inlineRepair.injected.map((entry) => `${entry.name}(${entry.callId})`).join(", ")
|
|
36111
|
+
);
|
|
36112
|
+
}
|
|
36113
|
+
if (inlineRepair.droppedDuplicates.length > 0) {
|
|
36114
|
+
debugLogger15.warn(
|
|
36115
|
+
`[REPAIR] sendMessageStream inline pass dropped ${inlineRepair.droppedDuplicates.length} duplicate functionResponse(s): ` + inlineRepair.droppedDuplicates.map((entry) => `${entry.name}(${entry.callId})`).join(", ")
|
|
36116
|
+
);
|
|
36117
|
+
}
|
|
35967
36118
|
requestContents = this.getRequestHistory();
|
|
35968
36119
|
} catch (error) {
|
|
35969
36120
|
if (userContentAdded) {
|
|
@@ -36015,8 +36166,21 @@ var GeminiChat = class {
|
|
|
36015
36166
|
break;
|
|
36016
36167
|
} catch (error) {
|
|
36017
36168
|
lastError = error;
|
|
36169
|
+
const popPartialIfPushed = /* @__PURE__ */ __name(() => {
|
|
36170
|
+
const idx = self2.pendingPartialAssistantTurnIndex;
|
|
36171
|
+
if (idx === null) return;
|
|
36172
|
+
if (self2.history.length > idx && self2.history[idx]?.role === "model") {
|
|
36173
|
+
self2.history.splice(idx, 1);
|
|
36174
|
+
} else {
|
|
36175
|
+
debugLogger15.warn(
|
|
36176
|
+
`[PARTIAL_POP] Splice skipped: idx=${idx}, historyLength=${self2.history.length}, roleAtIdx=${self2.history[idx]?.role ?? "undefined"}`
|
|
36177
|
+
);
|
|
36178
|
+
}
|
|
36179
|
+
self2.clearPendingPartialState();
|
|
36180
|
+
}, "popPartialIfPushed");
|
|
36018
36181
|
const isRateLimit = isRateLimitError(error, extraRetryErrorCodes);
|
|
36019
36182
|
if (isRateLimit && rateLimitRetryCount < maxRateLimitRetries) {
|
|
36183
|
+
popPartialIfPushed();
|
|
36020
36184
|
rateLimitRetryCount++;
|
|
36021
36185
|
const delayMs = getRateLimitRetryDelayMs(rateLimitRetryCount, {
|
|
36022
36186
|
...RATE_LIMIT_RETRY_OPTIONS,
|
|
@@ -36081,6 +36245,7 @@ var GeminiChat = class {
|
|
|
36081
36245
|
}
|
|
36082
36246
|
);
|
|
36083
36247
|
if (reactiveInfo.compressionStatus === 1 /* COMPRESSED */) {
|
|
36248
|
+
popPartialIfPushed();
|
|
36084
36249
|
requestContents = self2.getRequestHistory();
|
|
36085
36250
|
debugLogger15.info(
|
|
36086
36251
|
`Reactive compression succeeded: ${reactiveInfo.originalTokenCount} -> ${reactiveInfo.newTokenCount} tokens.`
|
|
@@ -36118,6 +36283,7 @@ var GeminiChat = class {
|
|
|
36118
36283
|
}
|
|
36119
36284
|
const isTransientStreamError = error instanceof InvalidStreamError;
|
|
36120
36285
|
if (isTransientStreamError && invalidStreamRetryCount < INVALID_STREAM_RETRY_CONFIG.maxRetries) {
|
|
36286
|
+
popPartialIfPushed();
|
|
36121
36287
|
invalidStreamRetryCount++;
|
|
36122
36288
|
const delayMs = INVALID_STREAM_RETRY_CONFIG.initialDelayMs * invalidStreamRetryCount;
|
|
36123
36289
|
debugLogger15.warn(
|
|
@@ -36143,6 +36309,7 @@ var GeminiChat = class {
|
|
|
36143
36309
|
const isContentError = error instanceof InvalidStreamError;
|
|
36144
36310
|
if (isContentError) {
|
|
36145
36311
|
if (attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts - 1) {
|
|
36312
|
+
popPartialIfPushed();
|
|
36146
36313
|
logContentRetry(
|
|
36147
36314
|
self2.config,
|
|
36148
36315
|
new ContentRetryEvent(
|
|
@@ -36231,6 +36398,17 @@ var GeminiChat = class {
|
|
|
36231
36398
|
}
|
|
36232
36399
|
successfulRecoveries++;
|
|
36233
36400
|
} catch (recoveryError) {
|
|
36401
|
+
const expectedIdx = self2.pendingPartialAssistantTurnIndex;
|
|
36402
|
+
const lastIdx = self2.history.length - 1;
|
|
36403
|
+
if (expectedIdx !== null && self2.history.length > 0 && self2.history[lastIdx]?.role === "model") {
|
|
36404
|
+
if (expectedIdx !== lastIdx) {
|
|
36405
|
+
debugLogger15.warn(
|
|
36406
|
+
`[RECOVERY_POP] Marker/last-index mismatch: marker=${expectedIdx}, lastIdx=${lastIdx}, historyLength=${self2.history.length}. Popping last entry as best-effort rollback \u2014 investigate any history mutation between processStreamResponse's partial push and this catch.`
|
|
36407
|
+
);
|
|
36408
|
+
}
|
|
36409
|
+
self2.history.pop();
|
|
36410
|
+
self2.clearPendingPartialState();
|
|
36411
|
+
}
|
|
36234
36412
|
if (self2.history.length > 0 && self2.history[self2.history.length - 1].role === "user") {
|
|
36235
36413
|
self2.history.pop();
|
|
36236
36414
|
}
|
|
@@ -36271,6 +36449,18 @@ var GeminiChat = class {
|
|
|
36271
36449
|
}
|
|
36272
36450
|
} finally {
|
|
36273
36451
|
streamDoneResolver();
|
|
36452
|
+
if (self2.pendingPartialAssistantRecord) {
|
|
36453
|
+
try {
|
|
36454
|
+
self2.chatRecordingService?.recordAssistantTurn(
|
|
36455
|
+
self2.pendingPartialAssistantRecord
|
|
36456
|
+
);
|
|
36457
|
+
} catch (recordErr) {
|
|
36458
|
+
debugLogger15.error(
|
|
36459
|
+
"[PARTIAL_FLUSH] Failed to persist deferred JSONL record: " + (recordErr instanceof Error ? recordErr.message : String(recordErr))
|
|
36460
|
+
);
|
|
36461
|
+
}
|
|
36462
|
+
self2.clearPendingPartialState();
|
|
36463
|
+
}
|
|
36274
36464
|
}
|
|
36275
36465
|
}();
|
|
36276
36466
|
}
|
|
@@ -36399,26 +36589,53 @@ var GeminiChat = class {
|
|
|
36399
36589
|
getHistoryLength() {
|
|
36400
36590
|
return this.history.length;
|
|
36401
36591
|
}
|
|
36592
|
+
/**
|
|
36593
|
+
* Set of `functionResponse.id` strings in user turns. Walk-only,
|
|
36594
|
+
* no clone — `useGeminiStream.handleCompletedTools` calls this per
|
|
36595
|
+
* tool-completion batch, so {@link getHistory}'s `structuredClone`
|
|
36596
|
+
* would stall the UI on long sessions.
|
|
36597
|
+
*/
|
|
36598
|
+
getHistoryFunctionResponseIds() {
|
|
36599
|
+
const ids = /* @__PURE__ */ new Set();
|
|
36600
|
+
for (const entry of this.history) {
|
|
36601
|
+
if (entry.role !== "user") continue;
|
|
36602
|
+
for (const part of entry.parts ?? []) {
|
|
36603
|
+
const id = part.functionResponse?.id;
|
|
36604
|
+
if (id) ids.add(id);
|
|
36605
|
+
}
|
|
36606
|
+
}
|
|
36607
|
+
return ids;
|
|
36608
|
+
}
|
|
36402
36609
|
/**
|
|
36403
36610
|
* Clears the chat history.
|
|
36404
36611
|
*/
|
|
36405
36612
|
clearHistory() {
|
|
36406
36613
|
this.history = [];
|
|
36614
|
+
this.clearPendingPartialState();
|
|
36407
36615
|
}
|
|
36408
36616
|
/**
|
|
36409
36617
|
* Adds a new entry to the chat history.
|
|
36410
36618
|
*/
|
|
36411
36619
|
addHistory(content) {
|
|
36412
36620
|
this.history.push(content);
|
|
36621
|
+
if (this.pendingPartialAssistantTurnIndex !== null || this.pendingPartialAssistantRecord !== null) {
|
|
36622
|
+
debugLogger15.error(
|
|
36623
|
+
"[INVARIANT_VIOLATION] addHistory called while a partial-push marker is active \u2014 clearing it."
|
|
36624
|
+
);
|
|
36625
|
+
}
|
|
36626
|
+
this.clearPendingPartialState();
|
|
36413
36627
|
}
|
|
36414
36628
|
setHistory(history) {
|
|
36415
36629
|
this.history = history;
|
|
36630
|
+
this.clearPendingPartialState();
|
|
36416
36631
|
}
|
|
36417
36632
|
truncateHistory(keepCount) {
|
|
36418
36633
|
this.history = this.history.slice(0, keepCount);
|
|
36634
|
+
this.clearPendingPartialState();
|
|
36419
36635
|
}
|
|
36420
36636
|
stripThoughtsFromHistory() {
|
|
36421
36637
|
this.history = this.history.map(stripThoughtPartsFromContent).filter((content) => content !== null);
|
|
36638
|
+
this.clearPendingPartialState();
|
|
36422
36639
|
}
|
|
36423
36640
|
/**
|
|
36424
36641
|
* Pop all orphaned trailing user entries from chat history.
|
|
@@ -36429,6 +36646,14 @@ var GeminiChat = class {
|
|
|
36429
36646
|
while (this.history.length > 0 && this.history[this.history.length - 1].role === "user") {
|
|
36430
36647
|
this.history.pop();
|
|
36431
36648
|
}
|
|
36649
|
+
this.clearPendingPartialState();
|
|
36650
|
+
}
|
|
36651
|
+
/**
|
|
36652
|
+
* Instance wrapper around the free-function {@link repairOrphanedToolUseTurns}.
|
|
36653
|
+
* See the canonical note above `ORPHAN_TOOL_USE_REPAIR_REASON`.
|
|
36654
|
+
*/
|
|
36655
|
+
repairOrphanedToolUseTurns(reason) {
|
|
36656
|
+
return repairOrphanedToolUseTurns(this.history, reason);
|
|
36432
36657
|
}
|
|
36433
36658
|
setTools(tools) {
|
|
36434
36659
|
this.generationConfig.tools = tools;
|
|
@@ -36465,31 +36690,38 @@ This error was probably caused by cyclic schema references in one of the followi
|
|
|
36465
36690
|
let usageMetadata;
|
|
36466
36691
|
let hasToolCall = false;
|
|
36467
36692
|
let hasFinishReason = false;
|
|
36468
|
-
|
|
36469
|
-
|
|
36470
|
-
|
|
36471
|
-
|
|
36472
|
-
if (
|
|
36473
|
-
|
|
36474
|
-
|
|
36475
|
-
|
|
36476
|
-
|
|
36477
|
-
|
|
36478
|
-
|
|
36479
|
-
|
|
36480
|
-
|
|
36481
|
-
|
|
36482
|
-
|
|
36483
|
-
|
|
36484
|
-
|
|
36485
|
-
|
|
36486
|
-
|
|
36487
|
-
|
|
36488
|
-
|
|
36489
|
-
|
|
36693
|
+
let streamError = null;
|
|
36694
|
+
try {
|
|
36695
|
+
for await (const chunk of streamResponse) {
|
|
36696
|
+
hasFinishReason ||= chunk?.candidates?.some((candidate) => candidate.finishReason) ?? false;
|
|
36697
|
+
if (isValidResponse(chunk)) {
|
|
36698
|
+
const content = chunk.candidates?.[0]?.content;
|
|
36699
|
+
if (content?.parts) {
|
|
36700
|
+
if (content.parts.some((part) => part.functionCall)) {
|
|
36701
|
+
hasToolCall = true;
|
|
36702
|
+
}
|
|
36703
|
+
allModelParts.push(...content.parts);
|
|
36704
|
+
}
|
|
36705
|
+
}
|
|
36706
|
+
if (chunk.usageMetadata) {
|
|
36707
|
+
usageMetadata = chunk.usageMetadata;
|
|
36708
|
+
const lastPromptTokenCount = usageMetadata.promptTokenCount || usageMetadata.totalTokenCount;
|
|
36709
|
+
if (lastPromptTokenCount) {
|
|
36710
|
+
this.lastPromptTokenCount = lastPromptTokenCount;
|
|
36711
|
+
this.telemetryService?.setLastPromptTokenCount(
|
|
36712
|
+
lastPromptTokenCount
|
|
36713
|
+
);
|
|
36714
|
+
}
|
|
36715
|
+
if (usageMetadata.cachedContentTokenCount && this.telemetryService) {
|
|
36716
|
+
this.telemetryService.setLastCachedContentTokenCount(
|
|
36717
|
+
usageMetadata.cachedContentTokenCount
|
|
36718
|
+
);
|
|
36719
|
+
}
|
|
36490
36720
|
}
|
|
36721
|
+
yield chunk;
|
|
36491
36722
|
}
|
|
36492
|
-
|
|
36723
|
+
} catch (e) {
|
|
36724
|
+
streamError = e;
|
|
36493
36725
|
}
|
|
36494
36726
|
let thoughtContentPart;
|
|
36495
36727
|
const thoughtText = allModelParts.filter((part) => part.thought).map((part) => part.text).join("").trim();
|
|
@@ -36516,9 +36748,10 @@ This error was probably caused by cyclic schema references in one of the followi
|
|
|
36516
36748
|
}
|
|
36517
36749
|
}
|
|
36518
36750
|
const contentText = consolidatedHistoryParts.filter((part) => part.text).map((part) => part.text).join("").trim();
|
|
36519
|
-
|
|
36751
|
+
const willPersistToHistory = streamError === null || hasToolCall && (thoughtContentPart || consolidatedHistoryParts.length > 0);
|
|
36752
|
+
if (willPersistToHistory && (thoughtContentPart || contentText || hasToolCall || usageMetadata)) {
|
|
36520
36753
|
const contextWindowSize = this.config.getContentGeneratorConfig()?.contextWindowSize;
|
|
36521
|
-
|
|
36754
|
+
const recordArgs = {
|
|
36522
36755
|
model,
|
|
36523
36756
|
message: [
|
|
36524
36757
|
...thoughtContentPart ? [thoughtContentPart] : [],
|
|
@@ -36529,7 +36762,28 @@ This error was probably caused by cyclic schema references in one of the followi
|
|
|
36529
36762
|
],
|
|
36530
36763
|
tokens: usageMetadata,
|
|
36531
36764
|
contextWindowSize
|
|
36532
|
-
}
|
|
36765
|
+
};
|
|
36766
|
+
if (streamError !== null) {
|
|
36767
|
+
this.pendingPartialAssistantRecord = recordArgs;
|
|
36768
|
+
} else {
|
|
36769
|
+
this.chatRecordingService?.recordAssistantTurn(recordArgs);
|
|
36770
|
+
}
|
|
36771
|
+
}
|
|
36772
|
+
if (streamError !== null) {
|
|
36773
|
+
if (willPersistToHistory) {
|
|
36774
|
+
this.history.push({
|
|
36775
|
+
role: "model",
|
|
36776
|
+
parts: [
|
|
36777
|
+
...thoughtContentPart ? [thoughtContentPart] : [],
|
|
36778
|
+
...consolidatedHistoryParts
|
|
36779
|
+
]
|
|
36780
|
+
});
|
|
36781
|
+
this.pendingPartialAssistantTurnIndex = this.history.length - 1;
|
|
36782
|
+
debugLogger15.warn(
|
|
36783
|
+
`[PARTIAL_PUSH] Persisting partial assistant turn for mid-stream error recovery (will be rolled back if retry succeeds, kept if break is unretryable). pendingIndex=${this.pendingPartialAssistantTurnIndex} callIds=${consolidatedHistoryParts.map((p) => p.functionCall?.id).filter((id) => Boolean(id)).join(",")} error=${streamError instanceof Error ? streamError.message : String(streamError)}`
|
|
36784
|
+
);
|
|
36785
|
+
}
|
|
36786
|
+
throw streamError;
|
|
36533
36787
|
}
|
|
36534
36788
|
const hasAnyContent = contentText || thoughtText;
|
|
36535
36789
|
if (!hasToolCall && (!hasFinishReason || !hasAnyContent)) {
|
|
@@ -67484,6 +67738,20 @@ var GeminiClient = class {
|
|
|
67484
67738
|
}
|
|
67485
67739
|
return void 0;
|
|
67486
67740
|
}
|
|
67741
|
+
/**
|
|
67742
|
+
* Walk-only accessor for the set of `functionResponse.id` strings in
|
|
67743
|
+
* raw history. Callers that only need the dedup id set (notably
|
|
67744
|
+
* `useGeminiStream.handleCompletedTools`) MUST prefer this over
|
|
67745
|
+
* {@link getHistory}, which deep-clones the entire conversation via
|
|
67746
|
+
* `structuredClone` on every call. On long sessions with sizable
|
|
67747
|
+
* tool outputs the clone is a multi-millisecond hit on the React UI
|
|
67748
|
+
* thread; running it on every tool-completion batch caused visible
|
|
67749
|
+
* frame drops during streaming. See
|
|
67750
|
+
* `GeminiChat.getHistoryFunctionResponseIds` for the implementation.
|
|
67751
|
+
*/
|
|
67752
|
+
getHistoryFunctionResponseIds() {
|
|
67753
|
+
return this.getChat().getHistoryFunctionResponseIds();
|
|
67754
|
+
}
|
|
67487
67755
|
/**
|
|
67488
67756
|
* Pop orphaned trailing user entries from the in-memory chat history.
|
|
67489
67757
|
* Used by:
|
|
@@ -67511,6 +67779,46 @@ var GeminiClient = class {
|
|
|
67511
67779
|
this.config.getFileReadCache().clear();
|
|
67512
67780
|
this.forceFullIdeContext = true;
|
|
67513
67781
|
}
|
|
67782
|
+
/**
|
|
67783
|
+
* Synthesize a `functionResponse` for every dangling `model[functionCall]`
|
|
67784
|
+
* in chat history whose corresponding tool_result never landed. Inverse of
|
|
67785
|
+
* {@link stripOrphanedUserEntriesFromHistory}, which only handles trailing
|
|
67786
|
+
* `user` entries.
|
|
67787
|
+
*
|
|
67788
|
+
* This `GeminiClient` method is the resume-path entry point — called once
|
|
67789
|
+
* from {@link startChat} after the transcript loads, covering `--resume`
|
|
67790
|
+
* of a session that crashed between a partial-tool_use push and the
|
|
67791
|
+
* tool's eventual completion.
|
|
67792
|
+
*
|
|
67793
|
+
* The other two coverage points (Retry submit path after
|
|
67794
|
+
* `stripOrphanedUserEntriesFromHistory`, and the defensive pass at the
|
|
67795
|
+
* start of every UserQuery / Cron send) live one layer down inside
|
|
67796
|
+
* `GeminiChat.sendMessageStream` and call the standalone
|
|
67797
|
+
* `repairOrphanedToolUseTurns(history)` function directly — they don't
|
|
67798
|
+
* route through this wrapper. Anyone tracing the repair-pass coupling
|
|
67799
|
+
* between the client and chat layers should follow that path
|
|
67800
|
+
* separately rather than expect everything to funnel through here.
|
|
67801
|
+
*
|
|
67802
|
+
* Synthesizes an `error` `functionResponse`. The React tool scheduler
|
|
67803
|
+
* (`useGeminiStream.handleCompletedTools`) MUST dedupe by `callId` against
|
|
67804
|
+
* the live history before submitting its own `tool_result` — otherwise a
|
|
67805
|
+
* late real result lands as a second `user[tool_result]` block (orphan
|
|
67806
|
+
* because the synthetic already consumed the matching `tool_use`).
|
|
67807
|
+
*/
|
|
67808
|
+
repairOrphanedToolUseTurnsInHistory(reason) {
|
|
67809
|
+
const result = this.getChat().repairOrphanedToolUseTurns(reason);
|
|
67810
|
+
if (result.injected.length > 0) {
|
|
67811
|
+
debugLogger43.warn(
|
|
67812
|
+
`[REPAIR] Synthesized ${result.injected.length} functionResponse(s) for dangling tool_use(s): ${result.injected.map((e) => `${e.name}(${e.callId})`).join(", ")}`
|
|
67813
|
+
);
|
|
67814
|
+
}
|
|
67815
|
+
if (result.droppedDuplicates.length > 0) {
|
|
67816
|
+
debugLogger43.warn(
|
|
67817
|
+
`[REPAIR] Dropped ${result.droppedDuplicates.length} duplicate functionResponse(s) for callId(s): ${result.droppedDuplicates.map((e) => `${e.name}(${e.callId})`).join(", ")}`
|
|
67818
|
+
);
|
|
67819
|
+
}
|
|
67820
|
+
return result;
|
|
67821
|
+
}
|
|
67514
67822
|
setHistory(history) {
|
|
67515
67823
|
this.getChat().setHistory(history);
|
|
67516
67824
|
debugLogger43.debug("[FILE_READ_CACHE] clear after setHistory");
|
|
@@ -67776,6 +68084,7 @@ var GeminiClient = class {
|
|
|
67776
68084
|
this.config.getChatRecordingService(),
|
|
67777
68085
|
uiTelemetryService
|
|
67778
68086
|
);
|
|
68087
|
+
this.repairOrphanedToolUseTurnsInHistory();
|
|
67779
68088
|
const sessionStartAdditionalContext = await this.fireSessionStartHook(sessionStartSource);
|
|
67780
68089
|
this.lastSessionStartContext = sessionStartAdditionalContext;
|
|
67781
68090
|
this.lastSessionStartSource = sessionStartAdditionalContext ? sessionStartSource : void 0;
|
|
@@ -104180,7 +104489,7 @@ async function findProjectRoot2(startDir) {
|
|
|
104180
104489
|
}
|
|
104181
104490
|
} catch (error) {
|
|
104182
104491
|
const isENOENT2 = typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
104183
|
-
const isTestEnv = process.env["
|
|
104492
|
+
const isTestEnv = process.env["VITEST"];
|
|
104184
104493
|
if (!isENOENT2 && !isTestEnv) {
|
|
104185
104494
|
if (typeof error === "object" && error !== null && "code" in error) {
|
|
104186
104495
|
const fsError = error;
|
|
@@ -104343,7 +104652,7 @@ async function readGeminiMdFiles(filePaths, importFormat = "tree") {
|
|
|
104343
104652
|
);
|
|
104344
104653
|
return { filePath, content: processedResult.content };
|
|
104345
104654
|
} catch (error) {
|
|
104346
|
-
const isTestEnv = process.env["
|
|
104655
|
+
const isTestEnv = process.env["VITEST"];
|
|
104347
104656
|
if (!isTestEnv) {
|
|
104348
104657
|
const message = error instanceof Error ? error.message : String(error);
|
|
104349
104658
|
logger3.warn(
|
|
@@ -107055,19 +107364,19 @@ var Config = class {
|
|
|
107055
107364
|
}, "registerStructuredOutputIfRequested");
|
|
107056
107365
|
if (this.getBareMode()) {
|
|
107057
107366
|
await registerLazy(ToolNames.READ_FILE, async () => {
|
|
107058
|
-
const { ReadFileTool } = await import("./read-file-
|
|
107367
|
+
const { ReadFileTool } = await import("./read-file-G4TFJFZ2.js");
|
|
107059
107368
|
return new ReadFileTool(this);
|
|
107060
107369
|
});
|
|
107061
107370
|
await registerLazy(ToolNames.EDIT, async () => {
|
|
107062
|
-
const { EditTool } = await import("./edit-
|
|
107371
|
+
const { EditTool } = await import("./edit-JXJILHZW.js");
|
|
107063
107372
|
return new EditTool(this);
|
|
107064
107373
|
});
|
|
107065
107374
|
await registerLazy(ToolNames.NOTEBOOK_EDIT, async () => {
|
|
107066
|
-
const { NotebookEditTool } = await import("./notebook-edit-
|
|
107375
|
+
const { NotebookEditTool } = await import("./notebook-edit-7NXIUSLW.js");
|
|
107067
107376
|
return new NotebookEditTool(this);
|
|
107068
107377
|
});
|
|
107069
107378
|
await registerLazy(ToolNames.SHELL, async () => {
|
|
107070
|
-
const { ShellTool: ShellTool2 } = await import("./shell-
|
|
107379
|
+
const { ShellTool: ShellTool2 } = await import("./shell-AXZQIDDU.js");
|
|
107071
107380
|
return new ShellTool2(this);
|
|
107072
107381
|
});
|
|
107073
107382
|
await registerStructuredOutputIfRequested();
|
|
@@ -107077,11 +107386,11 @@ var Config = class {
|
|
|
107077
107386
|
return registry;
|
|
107078
107387
|
}
|
|
107079
107388
|
await registerLazy(ToolNames.TOOL_SEARCH, async () => {
|
|
107080
|
-
const { ToolSearchTool } = await import("./tool-search-
|
|
107389
|
+
const { ToolSearchTool } = await import("./tool-search-BTGLGBDQ.js");
|
|
107081
107390
|
return new ToolSearchTool(this);
|
|
107082
107391
|
});
|
|
107083
107392
|
await registerLazy(ToolNames.AGENT, async () => {
|
|
107084
|
-
const { AgentTool: AgentTool2 } = await import("./agent-
|
|
107393
|
+
const { AgentTool: AgentTool2 } = await import("./agent-QFQGDV7B.js");
|
|
107085
107394
|
return new AgentTool2(this);
|
|
107086
107395
|
});
|
|
107087
107396
|
await registerLazy(ToolNames.TASK_STOP, async () => {
|
|
@@ -107093,7 +107402,7 @@ var Config = class {
|
|
|
107093
107402
|
return new SendMessageTool(this);
|
|
107094
107403
|
});
|
|
107095
107404
|
await registerLazy(ToolNames.SKILL, async () => {
|
|
107096
|
-
const { SkillTool } = await import("./skill-
|
|
107405
|
+
const { SkillTool } = await import("./skill-IMUVSRRZ.js");
|
|
107097
107406
|
return new SkillTool(this);
|
|
107098
107407
|
});
|
|
107099
107408
|
await registerLazy(ToolNames.LS, async () => {
|
|
@@ -107101,7 +107410,7 @@ var Config = class {
|
|
|
107101
107410
|
return new LSTool(this);
|
|
107102
107411
|
});
|
|
107103
107412
|
await registerLazy(ToolNames.READ_FILE, async () => {
|
|
107104
|
-
const { ReadFileTool } = await import("./read-file-
|
|
107413
|
+
const { ReadFileTool } = await import("./read-file-G4TFJFZ2.js");
|
|
107105
107414
|
return new ReadFileTool(this);
|
|
107106
107415
|
});
|
|
107107
107416
|
if (this.getUseRipgrep()) {
|
|
@@ -107114,7 +107423,7 @@ var Config = class {
|
|
|
107114
107423
|
}
|
|
107115
107424
|
if (useRipgrep) {
|
|
107116
107425
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107117
|
-
const { RipGrepTool: RipGrepTool2 } = await import("./ripGrep-
|
|
107426
|
+
const { RipGrepTool: RipGrepTool2 } = await import("./ripGrep-IMMYV3HQ.js");
|
|
107118
107427
|
return new RipGrepTool2(this);
|
|
107119
107428
|
});
|
|
107120
107429
|
} else {
|
|
@@ -107127,34 +107436,34 @@ var Config = class {
|
|
|
107127
107436
|
)
|
|
107128
107437
|
);
|
|
107129
107438
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107130
|
-
const { GrepTool } = await import("./grep-
|
|
107439
|
+
const { GrepTool } = await import("./grep-RO7NJDWP.js");
|
|
107131
107440
|
return new GrepTool(this);
|
|
107132
107441
|
});
|
|
107133
107442
|
}
|
|
107134
107443
|
} else {
|
|
107135
107444
|
await registerLazy(ToolNames.GREP, async () => {
|
|
107136
|
-
const { GrepTool } = await import("./grep-
|
|
107445
|
+
const { GrepTool } = await import("./grep-RO7NJDWP.js");
|
|
107137
107446
|
return new GrepTool(this);
|
|
107138
107447
|
});
|
|
107139
107448
|
}
|
|
107140
107449
|
await registerLazy(ToolNames.GLOB, async () => {
|
|
107141
|
-
const { GlobTool } = await import("./glob-
|
|
107450
|
+
const { GlobTool } = await import("./glob-HZYBHMPB.js");
|
|
107142
107451
|
return new GlobTool(this);
|
|
107143
107452
|
});
|
|
107144
107453
|
await registerLazy(ToolNames.EDIT, async () => {
|
|
107145
|
-
const { EditTool } = await import("./edit-
|
|
107454
|
+
const { EditTool } = await import("./edit-JXJILHZW.js");
|
|
107146
107455
|
return new EditTool(this);
|
|
107147
107456
|
});
|
|
107148
107457
|
await registerLazy(ToolNames.NOTEBOOK_EDIT, async () => {
|
|
107149
|
-
const { NotebookEditTool } = await import("./notebook-edit-
|
|
107458
|
+
const { NotebookEditTool } = await import("./notebook-edit-7NXIUSLW.js");
|
|
107150
107459
|
return new NotebookEditTool(this);
|
|
107151
107460
|
});
|
|
107152
107461
|
await registerLazy(ToolNames.WRITE_FILE, async () => {
|
|
107153
|
-
const { WriteFileTool } = await import("./write-file-
|
|
107462
|
+
const { WriteFileTool } = await import("./write-file-BNUIFGU4.js");
|
|
107154
107463
|
return new WriteFileTool(this);
|
|
107155
107464
|
});
|
|
107156
107465
|
await registerLazy(ToolNames.SHELL, async () => {
|
|
107157
|
-
const { ShellTool: ShellTool2 } = await import("./shell-
|
|
107466
|
+
const { ShellTool: ShellTool2 } = await import("./shell-AXZQIDDU.js");
|
|
107158
107467
|
return new ShellTool2(this);
|
|
107159
107468
|
});
|
|
107160
107469
|
await registerLazy(ToolNames.TODO_WRITE, async () => {
|
|
@@ -107167,16 +107476,16 @@ var Config = class {
|
|
|
107167
107476
|
});
|
|
107168
107477
|
if (!this.sdkMode) {
|
|
107169
107478
|
await registerLazy(ToolNames.EXIT_PLAN_MODE, async () => {
|
|
107170
|
-
const { ExitPlanModeTool } = await import("./exitPlanMode-
|
|
107479
|
+
const { ExitPlanModeTool } = await import("./exitPlanMode-WEETA2GM.js");
|
|
107171
107480
|
return new ExitPlanModeTool(this);
|
|
107172
107481
|
});
|
|
107173
107482
|
}
|
|
107174
107483
|
await registerLazy(ToolNames.ENTER_WORKTREE, async () => {
|
|
107175
|
-
const { EnterWorktreeTool } = await import("./enter-worktree-
|
|
107484
|
+
const { EnterWorktreeTool } = await import("./enter-worktree-HQ3RBRIA.js");
|
|
107176
107485
|
return new EnterWorktreeTool(this);
|
|
107177
107486
|
});
|
|
107178
107487
|
await registerLazy(ToolNames.EXIT_WORKTREE, async () => {
|
|
107179
|
-
const { ExitWorktreeTool } = await import("./exit-worktree-
|
|
107488
|
+
const { ExitWorktreeTool } = await import("./exit-worktree-73TYZY5X.js");
|
|
107180
107489
|
return new ExitWorktreeTool(this);
|
|
107181
107490
|
});
|
|
107182
107491
|
await registerLazy(ToolNames.WEB_FETCH, async () => {
|
|
@@ -107205,7 +107514,7 @@ var Config = class {
|
|
|
107205
107514
|
});
|
|
107206
107515
|
}
|
|
107207
107516
|
await registerLazy(ToolNames.MONITOR, async () => {
|
|
107208
|
-
const { MonitorTool } = await import("./monitor-
|
|
107517
|
+
const { MonitorTool } = await import("./monitor-IOU376UD.js");
|
|
107209
107518
|
return new MonitorTool(this);
|
|
107210
107519
|
});
|
|
107211
107520
|
if (this.pendingMcpBudgetCallback) {
|
|
@@ -122690,6 +122999,7 @@ export {
|
|
|
122690
122999
|
StreamEventType,
|
|
122691
123000
|
isValidNonThoughtTextPart,
|
|
122692
123001
|
InvalidStreamError,
|
|
123002
|
+
repairOrphanedToolUseTurns,
|
|
122693
123003
|
GeminiChat,
|
|
122694
123004
|
isSchemaDepthError,
|
|
122695
123005
|
isInvalidArgumentError,
|