newmark-agent 0.5.3 → 0.5.7
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/dist/conversation-utility-host.bundle.cjs +147 -29
- package/dist/core/agent.d.ts +7 -0
- package/dist/core/agent.js +111 -26
- package/dist/core/agentKernelRunner.js +30 -2
- package/dist/core/autoRouter.d.ts +1 -0
- package/dist/core/autoRouter.js +9 -0
- package/dist/core/conversationKernel.d.ts +12 -0
- package/dist/core/conversationKernel.js +32 -1
- package/dist/core/electronUtilityRuntimePool.js +7 -2
- package/dist/core/types.d.ts +21 -0
- package/dist/core/wslAgentRuntimePool.js +6 -2
- package/dist/main.js +34 -0
- package/dist/preload.js +1 -0
- package/dist/providers/provider-events.d.ts +6 -0
- package/dist/providers/provider-events.js +15 -4
- package/dist/server.js +21 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +4 -0
- package/dist/ui/index.html +2345 -197
- package/dist/ui/lucide-sprite.svg +4 -0
- package/dist/wsl-agent-host.bundle.cjs +147 -29
- package/package.json +8 -3
|
@@ -328670,7 +328670,7 @@ function providerStreamTimeoutError(timeoutMs) {
|
|
|
328670
328670
|
error.message = `Stream read timeout after ${timeoutMs}ms`;
|
|
328671
328671
|
return error;
|
|
328672
328672
|
}
|
|
328673
|
-
async function readProviderStreamChunk(reader, signal, timeoutMs =
|
|
328673
|
+
async function readProviderStreamChunk(reader, signal, timeoutMs = 0) {
|
|
328674
328674
|
if (signal.aborted) throw providerAbortError(signal);
|
|
328675
328675
|
let timer;
|
|
328676
328676
|
let onAbort;
|
|
@@ -328678,9 +328678,9 @@ async function readProviderStreamChunk(reader, signal, timeoutMs = 3e4) {
|
|
|
328678
328678
|
onAbort = () => reject(providerAbortError(signal));
|
|
328679
328679
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
328680
328680
|
});
|
|
328681
|
-
const timeoutPromise = new Promise((_3, reject) => {
|
|
328681
|
+
const timeoutPromise = timeoutMs > 0 ? new Promise((_3, reject) => {
|
|
328682
328682
|
timer = setTimeout(() => reject(providerStreamTimeoutError(timeoutMs)), timeoutMs);
|
|
328683
|
-
});
|
|
328683
|
+
}) : new Promise(() => void 0);
|
|
328684
328684
|
try {
|
|
328685
328685
|
return await Promise.race([reader.read(), abortPromise, timeoutPromise]);
|
|
328686
328686
|
} catch (error) {
|
|
@@ -336933,6 +336933,10 @@ var ToolExecutor = class {
|
|
|
336933
336933
|
async webSearch(query) {
|
|
336934
336934
|
return this.wsearch(query);
|
|
336935
336935
|
}
|
|
336936
|
+
/** OCR entry point for the runtime's final visual fallback. */
|
|
336937
|
+
async finalVisualFallbackOcr(dataUrl, signal) {
|
|
336938
|
+
return await this.localOcr.recognizeDataUrl(dataUrl, signal, "sparse-ui");
|
|
336939
|
+
}
|
|
336936
336940
|
setHostProfile(profile) {
|
|
336937
336941
|
this.hostProfile = { ...profile };
|
|
336938
336942
|
}
|
|
@@ -340846,9 +340850,20 @@ async function runAgentKernel(agent) {
|
|
|
340846
340850
|
try {
|
|
340847
340851
|
const linkedPlanRevisionBeforeRun = agent.getLinkedPlan().revision;
|
|
340848
340852
|
const modelBeforeKernelRun = agent.model;
|
|
340849
|
-
|
|
340853
|
+
const preflightVisualFallback = !agent.activeModelConfig()?.vision ? await agent.finalVisualFallback("vision input not supported by the selected model", processSignal) : null;
|
|
340854
|
+
let lastTurn = preflightVisualFallback ? { text: preflightVisualFallback, stopReason: "stop", errorMessage: "" } : await runWithCompressionResume([], false);
|
|
340855
|
+
if (preflightVisualFallback) {
|
|
340856
|
+
tokens.push({ type: "text", text: preflightVisualFallback });
|
|
340857
|
+
agent.recordWorkStatus("Final visual fallback used: local mini OCR plus conservative text correction.");
|
|
340858
|
+
}
|
|
340850
340859
|
if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some((t3) => t3.text?.includes("[Model fallback]"))) {
|
|
340851
|
-
|
|
340860
|
+
const notice = `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.`;
|
|
340861
|
+
tokens.unshift({ type: "text", text: notice });
|
|
340862
|
+
agent.emitWorkEvent({
|
|
340863
|
+
type: "status",
|
|
340864
|
+
content: notice,
|
|
340865
|
+
fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId }
|
|
340866
|
+
});
|
|
340852
340867
|
}
|
|
340853
340868
|
let emptyResponseRetries = 0;
|
|
340854
340869
|
while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
|
|
@@ -340869,6 +340884,11 @@ async function runAgentKernel(agent) {
|
|
|
340869
340884
|
const notice = routeTransitionNotice(agent, previous);
|
|
340870
340885
|
tokens.push({ type: "text", text: notice });
|
|
340871
340886
|
agent.recordWorkStatus(notice);
|
|
340887
|
+
agent.emitWorkEvent({
|
|
340888
|
+
type: "status",
|
|
340889
|
+
content: notice,
|
|
340890
|
+
fallback: { from: previous, to: agent.model, providerId: agent.activeDeployment()?.providerId }
|
|
340891
|
+
});
|
|
340872
340892
|
kernel2.state.model = toKernelModel(agent);
|
|
340873
340893
|
const fallbackToolSurface = refreshToolSurface(true);
|
|
340874
340894
|
kernel2.state.systemPrompt = [agent.buildSystemPrompt(), fallbackToolSurface.systemPromptNotice].filter(Boolean).join("\n\n");
|
|
@@ -340876,6 +340896,14 @@ async function runAgentKernel(agent) {
|
|
|
340876
340896
|
await agent.waitForPlannedRouteRetry();
|
|
340877
340897
|
lastTurn = await runWithCompressionResume([], false);
|
|
340878
340898
|
}
|
|
340899
|
+
if (kernelTurnFailed(agent, lastTurn)) {
|
|
340900
|
+
const visualFallback = await agent.finalVisualFallback(lastTurn.errorMessage || lastTurn.text, processSignal);
|
|
340901
|
+
if (visualFallback) {
|
|
340902
|
+
tokens.push({ type: "text", text: visualFallback });
|
|
340903
|
+
agent.recordWorkStatus("Final visual fallback used: local mini OCR plus conservative text correction.");
|
|
340904
|
+
lastTurn = { ...lastTurn, text: visualFallback, errorMessage: "", stopReason: "stop" };
|
|
340905
|
+
}
|
|
340906
|
+
}
|
|
340879
340907
|
if (kernelTurnFailed(agent, lastTurn)) {
|
|
340880
340908
|
throw new ProviderRunError(normalizePublicProviderError(lastTurn.errorMessage || lastTurn.text, [agent.activeModelConfig()?.api_key]));
|
|
340881
340909
|
}
|
|
@@ -343285,14 +343313,16 @@ var AutoRouter = class {
|
|
|
343285
343313
|
retryDelayMs
|
|
343286
343314
|
});
|
|
343287
343315
|
}
|
|
343316
|
+
if (failure.allowModelFallback === false) return attempts.slice(0, remainingAttempts);
|
|
343288
343317
|
const selection = decision.requestedSelection;
|
|
343289
343318
|
if (selection.kind === "fixed") return attempts.slice(0, remainingAttempts);
|
|
343290
343319
|
const scope = selection.kind === "auto" ? selection.scope : { kind: "provider", providerId: current.providerId };
|
|
343291
343320
|
const subset = selection.kind === "auto" ? selection.subset : void 0;
|
|
343292
343321
|
const currentGroup = current.logicalModelGroupId;
|
|
343322
|
+
const currentProviderId = current.providerId;
|
|
343293
343323
|
const now2 = this.now();
|
|
343294
343324
|
const attemptedDeployments = decision.attempts.map((attempt) => attempt.deployment);
|
|
343295
|
-
const eligible = candidates.filter((candidate) => candidate.enabled && inScope(candidate.deployment, scope) && inSubset(candidate.deployment, subset) && !sameDeployment(candidate.deployment, current) && !attemptedDeployments.some((attempted) => sameDeployment(candidate.deployment, attempted)) && validationEligible(candidate, now2).length === 0 && this.passedInitialHardFilters(decision, candidate) && this.circuitState(candidate.deployment, now2, false) !== "open");
|
|
343325
|
+
const eligible = candidates.filter((candidate) => candidate.enabled && candidate.deployment.providerId === currentProviderId && inScope(candidate.deployment, scope) && inSubset(candidate.deployment, subset) && !sameDeployment(candidate.deployment, current) && !attemptedDeployments.some((attempted) => sameDeployment(candidate.deployment, attempted)) && validationEligible(candidate, now2).length === 0 && this.passedInitialHardFilters(decision, candidate) && this.circuitState(candidate.deployment, now2, false) !== "open");
|
|
343296
343326
|
const equivalent = currentGroup ? eligible.find((candidate) => candidate.deployment.logicalModelGroupId === currentGroup && !candidate.fallbackOnly) : void 0;
|
|
343297
343327
|
const fallback = eligible.find((candidate) => candidate.fallbackOnly);
|
|
343298
343328
|
const rankedAlternates = decision.rankedCandidates.map((ranked) => eligible.find((candidate) => sameDeployment(candidate.deployment, ranked.deployment))).filter((candidate) => !!candidate && candidate !== equivalent && candidate !== fallback && !candidate.fallbackOnly);
|
|
@@ -346730,7 +346760,8 @@ ${String(event.toolArgs || "")}`;
|
|
|
346730
346760
|
sequence,
|
|
346731
346761
|
status: input.status,
|
|
346732
346762
|
guide: !isToolEvent && input.guide ? this.normalizeGuideReceipt(input.guide) : void 0,
|
|
346733
|
-
displayImage: isToolEvent ? this.hydrateDisplayImage(input.displayImage) : void 0
|
|
346763
|
+
displayImage: isToolEvent ? this.hydrateDisplayImage(input.displayImage) : void 0,
|
|
346764
|
+
fallback: input.fallback
|
|
346734
346765
|
};
|
|
346735
346766
|
if (activeRun && this.isPersistablePublicWorkEvent(event)) {
|
|
346736
346767
|
activeRun.sequence = Number(sequence || activeRun.sequence + 1);
|
|
@@ -349649,9 +349680,15 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
349649
349680
|
}
|
|
349650
349681
|
compressionBuildBlockStart(messages) {
|
|
349651
349682
|
const activeRunId = this.currentWorkRunId();
|
|
349652
|
-
if (
|
|
349653
|
-
|
|
349654
|
-
|
|
349683
|
+
if (activeRunId) {
|
|
349684
|
+
const index = messages.findIndex((message) => String(message.run_id || message.runId || "") === activeRunId);
|
|
349685
|
+
if (index >= 0) return index;
|
|
349686
|
+
}
|
|
349687
|
+
let lastRunBoundary = -1;
|
|
349688
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
349689
|
+
if (String(messages[index]?.run_id || messages[index]?.runId || "")) lastRunBoundary = index + 1;
|
|
349690
|
+
}
|
|
349691
|
+
return lastRunBoundary >= 0 ? lastRunBoundary : messages.length;
|
|
349655
349692
|
}
|
|
349656
349693
|
recentContextSuffix(messages, maxMessages, tokenBudget) {
|
|
349657
349694
|
if (!messages.length) return [];
|
|
@@ -350580,17 +350617,12 @@ ${msg.content}
|
|
|
350580
350617
|
currentAttempt.sideEffectBoundary = this.routeSideEffectCommitted;
|
|
350581
350618
|
if (this.routeAttemptStartedAt > 0) currentAttempt.durationMs = Math.max(0, Date.now() - this.routeAttemptStartedAt);
|
|
350582
350619
|
}
|
|
350583
|
-
if (!fallbackEnabled) {
|
|
350584
|
-
this.lastRouteDecision.finalStatus = "failed";
|
|
350585
|
-
this.routeAttemptStartedAt = 0;
|
|
350586
|
-
this.persistRouteDecision(this.lastRouteDecision);
|
|
350587
|
-
return null;
|
|
350588
|
-
}
|
|
350589
350620
|
if (!this.pendingAutoAttempts.length) {
|
|
350590
350621
|
this.pendingAutoAttempts = this.autoRouter.planAttempts(this.lastRouteDecision, this.autoRouteCandidates(), {
|
|
350591
350622
|
error: failure,
|
|
350592
350623
|
streamCommitted: this.routeStreamCommitted,
|
|
350593
|
-
sideEffectCommitted: this.routeSideEffectCommitted
|
|
350624
|
+
sideEffectCommitted: this.routeSideEffectCommitted,
|
|
350625
|
+
allowModelFallback: fallbackEnabled
|
|
350594
350626
|
});
|
|
350595
350627
|
}
|
|
350596
350628
|
const next2 = this.pendingAutoAttempts.shift();
|
|
@@ -350625,7 +350657,6 @@ ${msg.content}
|
|
|
350625
350657
|
if (!next?.name) return null;
|
|
350626
350658
|
this.model = next.name;
|
|
350627
350659
|
this.fixedDeployment = this.deploymentRef(next);
|
|
350628
|
-
if (next.provider) this.config.set("models", "auto_switch_anchor_provider", next.provider);
|
|
350629
350660
|
return current;
|
|
350630
350661
|
}
|
|
350631
350662
|
isLlmErrorText(text) {
|
|
@@ -350633,12 +350664,10 @@ ${msg.content}
|
|
|
350633
350664
|
}
|
|
350634
350665
|
scopedSwitchModels(currentModelName) {
|
|
350635
350666
|
const all = this.config.allModels();
|
|
350636
|
-
|
|
350637
|
-
const
|
|
350638
|
-
|
|
350639
|
-
|
|
350640
|
-
const provider = this.config.findProvider(providerId);
|
|
350641
|
-
return all.filter((m2) => m2.provider_id === (provider?.id || providerId));
|
|
350667
|
+
const current = currentModelName === "auto" ? this.activeModelConfig() : this.config.findModel(currentModelName);
|
|
350668
|
+
const providerId = current?.provider_id || this.fixedDeployment?.providerId || (currentModelName !== "auto" ? this.activeDeployment()?.providerId : void 0);
|
|
350669
|
+
if (!providerId) return [];
|
|
350670
|
+
return all.filter((m2) => m2.provider_id === providerId);
|
|
350642
350671
|
}
|
|
350643
350672
|
async validateModels(selectedNames, options = {}) {
|
|
350644
350673
|
if (this.modelValidationPromise) return this.modelValidationPromise;
|
|
@@ -350820,6 +350849,60 @@ ${msg.content}
|
|
|
350820
350849
|
};
|
|
350821
350850
|
return results;
|
|
350822
350851
|
}
|
|
350852
|
+
/**
|
|
350853
|
+
* Final visual safety net: OCR each submitted image and ask a text-only
|
|
350854
|
+
* request to conservatively repair the OCR. This is intentionally callable
|
|
350855
|
+
* only after a visual-input refusal and after same-provider vision routing
|
|
350856
|
+
* has been exhausted; the original image is never sent again.
|
|
350857
|
+
*/
|
|
350858
|
+
async finalVisualFallback(errorText, signal) {
|
|
350859
|
+
if (!/(?:vision|image|multimodal|image_url|input_image).*(?:not supported|unsupported|拒绝|不支持|failed|failure|invalid)|(?:not supported|unsupported|拒绝|不支持).*(?:vision|image|multimodal|image_url|input_image)/i.test(String(errorText || ""))) return null;
|
|
350860
|
+
const current = this.activeModelConfig();
|
|
350861
|
+
if (!current) return null;
|
|
350862
|
+
const alternateVision = this.config.allModels().some(
|
|
350863
|
+
(model) => model.enabled !== false && model.provider_id === current.provider_id && model.name !== current.name && !!model.vision && !!model.api_key && !!model.provider_url && !["unavailable", "auth_error", "invalid_config"].includes(String(model.evaluation?.status || model.validation?.status || "").toLowerCase())
|
|
350864
|
+
);
|
|
350865
|
+
if (alternateVision) return null;
|
|
350866
|
+
const latest = [...this.history].reverse().find((item) => item?.role === "user");
|
|
350867
|
+
const parts = latest?.content && Array.isArray(latest.content) ? latest.content : [];
|
|
350868
|
+
const images = parts.map((part) => {
|
|
350869
|
+
const image = part.image_url;
|
|
350870
|
+
return image && typeof image === "object" ? String(image.url || "") : "";
|
|
350871
|
+
}).filter((value) => /^data:image\/(?:png|jpeg);base64,/i.test(value)).slice(0, 4);
|
|
350872
|
+
if (!images.length) return null;
|
|
350873
|
+
const ocr = [];
|
|
350874
|
+
for (const [index, image] of images.entries()) {
|
|
350875
|
+
try {
|
|
350876
|
+
const result = await this.tools.finalVisualFallbackOcr(image, signal);
|
|
350877
|
+
if (result.ok && result.text.trim()) ocr.push({ index: index + 1, text: result.text.slice(0, 5e4), confidence: result.confidence });
|
|
350878
|
+
} catch {
|
|
350879
|
+
}
|
|
350880
|
+
}
|
|
350881
|
+
if (!ocr.length) return JSON.stringify({ ok: false, fallback: "mini_ocr_llm", error: "Local OCR returned no readable text; no visual content was fabricated." });
|
|
350882
|
+
const task = typeof latest?.content === "string" ? latest.content : "";
|
|
350883
|
+
const evidence = ocr.map((item) => `Image ${item.index} (OCR confidence ${item.confidence.toFixed(1)}):
|
|
350884
|
+
${item.text}`).join("\n\n");
|
|
350885
|
+
const prompt = `The provider rejected image input. Answer the user's task using only this approximate OCR evidence. Correct obvious character, spacing, and line-break errors only when supported by context. Preserve [uncertain] markers for ambiguity and never invent missing visual content.
|
|
350886
|
+
User task:
|
|
350887
|
+
${task.slice(0, 12e3)}
|
|
350888
|
+
OCR evidence:
|
|
350889
|
+
${evidence}`;
|
|
350890
|
+
let corrected = "";
|
|
350891
|
+
try {
|
|
350892
|
+
const provider = this.engineModel();
|
|
350893
|
+
if (provider) corrected = String(await provider.chat(this.activeModelName(), [{ role: "user", content: prompt }], "You are a text-only OCR correction assistant. Be conservative and explicit about uncertainty.", 0.05, 3e3, signal) || "").trim();
|
|
350894
|
+
} catch {
|
|
350895
|
+
}
|
|
350896
|
+
return JSON.stringify({
|
|
350897
|
+
ok: !!(corrected || ocr.length),
|
|
350898
|
+
fallback: "mini_ocr_llm",
|
|
350899
|
+
approximate: true,
|
|
350900
|
+
warning: "\u89C6\u89C9\u8F93\u5165\u88AB\u62D2\u7EDD\uFF1B\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u672C\u5730 OCR\uFF0C\u5E76\u7ECF\u6587\u672C\u6A21\u578B\u4FDD\u5B88\u6821\u6B63\uFF0C\u53EF\u80FD\u4E0D\u5B8C\u6574\u3002",
|
|
350901
|
+
raw_ocr: ocr,
|
|
350902
|
+
corrected: corrected || ocr.map((item) => item.text).join("\n\n"),
|
|
350903
|
+
uncertainty: corrected ? "preserved" : "raw_ocr_only"
|
|
350904
|
+
}, null, 2);
|
|
350905
|
+
}
|
|
350823
350906
|
engineModel() {
|
|
350824
350907
|
if (this.forcedProvider) {
|
|
350825
350908
|
const active = this.activeDeployment();
|
|
@@ -351202,8 +351285,10 @@ ${persisted.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n"
|
|
|
351202
351285
|
}
|
|
351203
351286
|
const selectedModel = this.activeModelConfig();
|
|
351204
351287
|
if (images.length && !selectedModel?.vision) {
|
|
351205
|
-
|
|
351206
|
-
|
|
351288
|
+
const hasSameProviderVision = selectedModel && this.config.allModels().some(
|
|
351289
|
+
(model) => model.enabled !== false && model.provider_id === selectedModel.provider_id && model.name !== selectedModel.name && !!model.vision
|
|
351290
|
+
);
|
|
351291
|
+
if (hasSameProviderVision) this.switchToFallbackModel("vision input not supported by the selected model");
|
|
351207
351292
|
}
|
|
351208
351293
|
const now2 = this.nowLabel();
|
|
351209
351294
|
const visibleUserInput = inputEnvelope?.visibleUserInput === void 0 ? text : String(inputEnvelope.visibleUserInput || "");
|
|
@@ -351964,7 +352049,10 @@ ${items.map((item) => this.formatAutomation(item)).join("\n")}`;
|
|
|
351964
352049
|
if (!sa) return "[Subagent] Not found.";
|
|
351965
352050
|
const requestedModel = this.normalizeSubagentModelSelection(sa.model && sa.model !== "default" ? sa.model : this.modelSelectionValue() || this.model);
|
|
351966
352051
|
const requestedDeployment = parseDeploymentSelectionValue2(requestedModel);
|
|
351967
|
-
|
|
352052
|
+
let assignedModel = requestedModel === "auto" ? this.activeModelConfig() : requestedDeployment ? this.config.findDeployment(requestedDeployment) : this.config.findModel(requestedModel);
|
|
352053
|
+
if (!assignedModel && !requestedDeployment && requestedModel !== "auto") {
|
|
352054
|
+
assignedModel = this.activeModelConfig();
|
|
352055
|
+
}
|
|
351968
352056
|
const model = assignedModel?.name || (requestedModel === "auto" ? this.activeModelName() : requestedModel);
|
|
351969
352057
|
const activeModel = this.activeModelConfig();
|
|
351970
352058
|
const activeProvider = this.engineModel();
|
|
@@ -353393,6 +353481,32 @@ var ConversationKernel = class {
|
|
|
353393
353481
|
followUp: queued?.followUp.slice() || []
|
|
353394
353482
|
};
|
|
353395
353483
|
}
|
|
353484
|
+
/**
|
|
353485
|
+
* dev-0.5.6: 排队 followUp 消息 drain 时转成普通用户消息载荷。
|
|
353486
|
+
*
|
|
353487
|
+
* 入队时(enqueueSameSession)为去重/编辑保留了 clientMessageId,并把
|
|
353488
|
+
* runId 固定为入队时运行的 runId;若原样传给 process,写入 chatMessages
|
|
353489
|
+
* 的用户消息会携带旧 runId + clientMessageId,渲染端(PC renderTranscript /
|
|
353490
|
+
* 移动端 projectRemoteConversationItems)会把「clientMessageId + runId 命中
|
|
353491
|
+
* workRuns」的消息误判为 guide 消息而跳过气泡,导致排队自动发送的消息不
|
|
353492
|
+
* 显示在用户输入时间线。清除这两个字段后 process 走普通用户消息分支
|
|
353493
|
+
* (chatMessages.push,mode 取 visibleMode,runId 取当前 run)。
|
|
353494
|
+
*/
|
|
353495
|
+
drainQueuedFollowUpMessage(message) {
|
|
353496
|
+
if (typeof message === "string") return message;
|
|
353497
|
+
const text = String(message.text || "");
|
|
353498
|
+
const images = message.images;
|
|
353499
|
+
const attachments = message.attachments;
|
|
353500
|
+
const visible = message.visibleUserInput;
|
|
353501
|
+
const visibleMode = message.visibleMode;
|
|
353502
|
+
return {
|
|
353503
|
+
text,
|
|
353504
|
+
...images?.length ? { images } : {},
|
|
353505
|
+
...attachments?.length ? { attachments } : {},
|
|
353506
|
+
...visible ? { visibleUserInput: visible } : {},
|
|
353507
|
+
...visibleMode ? { visibleMode } : {}
|
|
353508
|
+
};
|
|
353509
|
+
}
|
|
353396
353510
|
queueItems(target) {
|
|
353397
353511
|
const runtime = this.findRuntime(target);
|
|
353398
353512
|
if (!runtime) return [];
|
|
@@ -354134,7 +354248,7 @@ ${batchText}`,
|
|
|
354134
354248
|
};
|
|
354135
354249
|
lastTokens = await this.runSingle(runtime, batchMessage, "steer");
|
|
354136
354250
|
} else {
|
|
354137
|
-
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
354251
|
+
lastTokens = await this.runSingle(runtime, this.drainQueuedFollowUpMessage(next.message), next.queueMode);
|
|
354138
354252
|
}
|
|
354139
354253
|
}
|
|
354140
354254
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -354593,7 +354707,11 @@ ${text}`;
|
|
|
354593
354707
|
type: "queue_update",
|
|
354594
354708
|
content: "Conversation queue updated.",
|
|
354595
354709
|
conversationId: runtime.id,
|
|
354596
|
-
queue: this.queued(runtime.target)
|
|
354710
|
+
queue: this.queued(runtime.target),
|
|
354711
|
+
// Structured rows with stable kernel ids so every consumer (PC UI and
|
|
354712
|
+
// the paired mobile client) can render/edit/delete the same items.
|
|
354713
|
+
queueItems: this.queueItems(runtime.target),
|
|
354714
|
+
queuePaused: runtime.queuePaused === true
|
|
354597
354715
|
});
|
|
354598
354716
|
}
|
|
354599
354717
|
clearQueued(runtime) {
|
package/dist/core/agent.d.ts
CHANGED
|
@@ -990,6 +990,13 @@ export declare class Agent {
|
|
|
990
990
|
isModelValidationRunning(): boolean;
|
|
991
991
|
modelValidationStatus(): ModelValidationProgress;
|
|
992
992
|
private runModelValidation;
|
|
993
|
+
/**
|
|
994
|
+
* Final visual safety net: OCR each submitted image and ask a text-only
|
|
995
|
+
* request to conservatively repair the OCR. This is intentionally callable
|
|
996
|
+
* only after a visual-input refusal and after same-provider vision routing
|
|
997
|
+
* has been exhausted; the original image is never sent again.
|
|
998
|
+
*/
|
|
999
|
+
finalVisualFallback(errorText: string, signal?: AbortSignal): Promise<string | null>;
|
|
993
1000
|
engineModel(): LLMProvider | null;
|
|
994
1001
|
/**
|
|
995
1002
|
* dev-0.4.3 模型原生思考强度档位映射表(模型名 → thinking_tier_map)。
|
package/dist/core/agent.js
CHANGED
|
@@ -2298,6 +2298,7 @@ class Agent {
|
|
|
2298
2298
|
status: input.status,
|
|
2299
2299
|
guide: !isToolEvent && input.guide ? this.normalizeGuideReceipt(input.guide) : undefined,
|
|
2300
2300
|
displayImage: isToolEvent ? this.hydrateDisplayImage(input.displayImage) : undefined,
|
|
2301
|
+
fallback: input.fallback,
|
|
2301
2302
|
};
|
|
2302
2303
|
if (activeRun && this.isPersistablePublicWorkEvent(event)) {
|
|
2303
2304
|
activeRun.sequence = Number(sequence || activeRun.sequence + 1);
|
|
@@ -5587,10 +5588,22 @@ class Agent {
|
|
|
5587
5588
|
}
|
|
5588
5589
|
compressionBuildBlockStart(messages) {
|
|
5589
5590
|
const activeRunId = this.currentWorkRunId();
|
|
5590
|
-
if (
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5591
|
+
if (activeRunId) {
|
|
5592
|
+
const index = messages.findIndex(message => String(message.run_id || message.runId || '') === activeRunId);
|
|
5593
|
+
if (index >= 0)
|
|
5594
|
+
return index;
|
|
5595
|
+
}
|
|
5596
|
+
// dev-0.5.6: 空闲对话(无活动 run)时不得把全部历史算进当前 Build Block,
|
|
5597
|
+
// 否则上下文显示窗口的长期历史恒为 0。回退语义:
|
|
5598
|
+
// - 存在带 run_id 的消息:boundary 取最后一个 run 的起点之后(该 run 及其
|
|
5599
|
+
// 之前的历史属于长期历史,其后无归属的消息属于当前未命名区块);
|
|
5600
|
+
// - 完全没有 run_id:全部历史都是长期历史(不存在当前 Build Block)。
|
|
5601
|
+
let lastRunBoundary = -1;
|
|
5602
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
5603
|
+
if (String(messages[index]?.run_id || messages[index]?.runId || ''))
|
|
5604
|
+
lastRunBoundary = index + 1;
|
|
5605
|
+
}
|
|
5606
|
+
return lastRunBoundary >= 0 ? lastRunBoundary : messages.length;
|
|
5594
5607
|
}
|
|
5595
5608
|
recentContextSuffix(messages, maxMessages, tokenBudget) {
|
|
5596
5609
|
if (!messages.length)
|
|
@@ -6651,17 +6664,12 @@ class Agent {
|
|
|
6651
6664
|
if (this.routeAttemptStartedAt > 0)
|
|
6652
6665
|
currentAttempt.durationMs = Math.max(0, Date.now() - this.routeAttemptStartedAt);
|
|
6653
6666
|
}
|
|
6654
|
-
if (!fallbackEnabled) {
|
|
6655
|
-
this.lastRouteDecision.finalStatus = 'failed';
|
|
6656
|
-
this.routeAttemptStartedAt = 0;
|
|
6657
|
-
this.persistRouteDecision(this.lastRouteDecision);
|
|
6658
|
-
return null;
|
|
6659
|
-
}
|
|
6660
6667
|
if (!this.pendingAutoAttempts.length) {
|
|
6661
6668
|
this.pendingAutoAttempts = this.autoRouter.planAttempts(this.lastRouteDecision, this.autoRouteCandidates(), {
|
|
6662
6669
|
error: failure,
|
|
6663
6670
|
streamCommitted: this.routeStreamCommitted,
|
|
6664
6671
|
sideEffectCommitted: this.routeSideEffectCommitted,
|
|
6672
|
+
allowModelFallback: fallbackEnabled,
|
|
6665
6673
|
});
|
|
6666
6674
|
}
|
|
6667
6675
|
const next = this.pendingAutoAttempts.shift();
|
|
@@ -6702,8 +6710,11 @@ class Agent {
|
|
|
6702
6710
|
return null;
|
|
6703
6711
|
this.model = next.name;
|
|
6704
6712
|
this.fixedDeployment = this.deploymentRef(next);
|
|
6705
|
-
|
|
6706
|
-
|
|
6713
|
+
// Do NOT rewrite auto_switch_anchor_provider here. The anchor describes
|
|
6714
|
+
// the user's Auto-routing scope; a fixed-model recovery must never
|
|
6715
|
+
// repoint it (a single transient failure would otherwise silently move
|
|
6716
|
+
// every future Auto route — and every future fallback pool — to the
|
|
6717
|
+
// recovery provider, which is the cross-provider leak users observed).
|
|
6707
6718
|
return current;
|
|
6708
6719
|
}
|
|
6709
6720
|
isLlmErrorText(text) {
|
|
@@ -6711,18 +6722,23 @@ class Agent {
|
|
|
6711
6722
|
}
|
|
6712
6723
|
scopedSwitchModels(currentModelName) {
|
|
6713
6724
|
const all = this.config.allModels();
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6725
|
+
// Fixed-model recovery is always provider-local. A same-named model on a
|
|
6726
|
+
// different provider is a different deployment and must never receive the
|
|
6727
|
+
// failed provider's credentials or become an implicit fallback.
|
|
6728
|
+
// Resolution order is strictly deployment-first: the pinned deployment,
|
|
6729
|
+
// then the exact active deployment. The global auto anchor is deliberately
|
|
6730
|
+
// NOT consulted here — it belongs to Auto routing only and can point at a
|
|
6731
|
+
// different provider (for example after a previous cross-provider switch),
|
|
6732
|
+
// which is exactly the same-name cross-provider leak this guard exists to
|
|
6733
|
+
// prevent. When no provider can be established unambiguously, return an
|
|
6734
|
+
// empty pool: failing the switch is safe, crossing providers is not.
|
|
6735
|
+
const current = currentModelName === 'auto' ? this.activeModelConfig() : this.config.findModel(currentModelName);
|
|
6736
|
+
const providerId = current?.provider_id
|
|
6737
|
+
|| this.fixedDeployment?.providerId
|
|
6738
|
+
|| (currentModelName !== 'auto' ? this.activeDeployment()?.providerId : undefined);
|
|
6722
6739
|
if (!providerId)
|
|
6723
|
-
return
|
|
6724
|
-
|
|
6725
|
-
return all.filter(m => m.provider_id === (provider?.id || providerId));
|
|
6740
|
+
return [];
|
|
6741
|
+
return all.filter(m => m.provider_id === providerId);
|
|
6726
6742
|
}
|
|
6727
6743
|
async validateModels(selectedNames, options = {}) {
|
|
6728
6744
|
if (this.modelValidationPromise)
|
|
@@ -6913,6 +6929,62 @@ class Agent {
|
|
|
6913
6929
|
};
|
|
6914
6930
|
return results;
|
|
6915
6931
|
}
|
|
6932
|
+
/**
|
|
6933
|
+
* Final visual safety net: OCR each submitted image and ask a text-only
|
|
6934
|
+
* request to conservatively repair the OCR. This is intentionally callable
|
|
6935
|
+
* only after a visual-input refusal and after same-provider vision routing
|
|
6936
|
+
* has been exhausted; the original image is never sent again.
|
|
6937
|
+
*/
|
|
6938
|
+
async finalVisualFallback(errorText, signal) {
|
|
6939
|
+
if (!/(?:vision|image|multimodal|image_url|input_image).*(?:not supported|unsupported|拒绝|不支持|failed|failure|invalid)|(?:not supported|unsupported|拒绝|不支持).*(?:vision|image|multimodal|image_url|input_image)/i.test(String(errorText || '')))
|
|
6940
|
+
return null;
|
|
6941
|
+
const current = this.activeModelConfig();
|
|
6942
|
+
if (!current)
|
|
6943
|
+
return null;
|
|
6944
|
+
const alternateVision = this.config.allModels().some(model => model.enabled !== false && model.provider_id === current.provider_id &&
|
|
6945
|
+
model.name !== current.name && !!model.vision && !!model.api_key && !!model.provider_url &&
|
|
6946
|
+
!['unavailable', 'auth_error', 'invalid_config'].includes(String(model.evaluation?.status || model.validation?.status || '').toLowerCase()));
|
|
6947
|
+
if (alternateVision)
|
|
6948
|
+
return null;
|
|
6949
|
+
const latest = [...this.history].reverse().find(item => item?.role === 'user');
|
|
6950
|
+
const parts = latest?.content && Array.isArray(latest.content) ? latest.content : [];
|
|
6951
|
+
const images = parts.map(part => {
|
|
6952
|
+
const image = part.image_url;
|
|
6953
|
+
return image && typeof image === 'object' ? String(image.url || '') : '';
|
|
6954
|
+
}).filter(value => /^data:image\/(?:png|jpeg);base64,/i.test(value)).slice(0, 4);
|
|
6955
|
+
if (!images.length)
|
|
6956
|
+
return null;
|
|
6957
|
+
const ocr = [];
|
|
6958
|
+
for (const [index, image] of images.entries()) {
|
|
6959
|
+
try {
|
|
6960
|
+
const result = await this.tools.finalVisualFallbackOcr(image, signal);
|
|
6961
|
+
if (result.ok && result.text.trim())
|
|
6962
|
+
ocr.push({ index: index + 1, text: result.text.slice(0, 50_000), confidence: result.confidence });
|
|
6963
|
+
}
|
|
6964
|
+
catch { }
|
|
6965
|
+
}
|
|
6966
|
+
if (!ocr.length)
|
|
6967
|
+
return JSON.stringify({ ok: false, fallback: 'mini_ocr_llm', error: 'Local OCR returned no readable text; no visual content was fabricated.' });
|
|
6968
|
+
const task = typeof latest?.content === 'string' ? latest.content : '';
|
|
6969
|
+
const evidence = ocr.map(item => `Image ${item.index} (OCR confidence ${item.confidence.toFixed(1)}):\n${item.text}`).join('\n\n');
|
|
6970
|
+
const prompt = `The provider rejected image input. Answer the user's task using only this approximate OCR evidence. Correct obvious character, spacing, and line-break errors only when supported by context. Preserve [uncertain] markers for ambiguity and never invent missing visual content.\nUser task:\n${task.slice(0, 12_000)}\nOCR evidence:\n${evidence}`;
|
|
6971
|
+
let corrected = '';
|
|
6972
|
+
try {
|
|
6973
|
+
const provider = this.engineModel();
|
|
6974
|
+
if (provider)
|
|
6975
|
+
corrected = String(await provider.chat(this.activeModelName(), [{ role: 'user', content: prompt }], 'You are a text-only OCR correction assistant. Be conservative and explicit about uncertainty.', 0.05, 3000, signal) || '').trim();
|
|
6976
|
+
}
|
|
6977
|
+
catch { }
|
|
6978
|
+
return JSON.stringify({
|
|
6979
|
+
ok: !!(corrected || ocr.length),
|
|
6980
|
+
fallback: 'mini_ocr_llm',
|
|
6981
|
+
approximate: true,
|
|
6982
|
+
warning: '视觉输入被拒绝;以下内容来自本地 OCR,并经文本模型保守校正,可能不完整。',
|
|
6983
|
+
raw_ocr: ocr,
|
|
6984
|
+
corrected: corrected || ocr.map(item => item.text).join('\n\n'),
|
|
6985
|
+
uncertainty: corrected ? 'preserved' : 'raw_ocr_only',
|
|
6986
|
+
}, null, 2);
|
|
6987
|
+
}
|
|
6916
6988
|
engineModel() {
|
|
6917
6989
|
if (this.forcedProvider) {
|
|
6918
6990
|
const active = this.activeDeployment();
|
|
@@ -7353,8 +7425,13 @@ class Agent {
|
|
|
7353
7425
|
}
|
|
7354
7426
|
const selectedModel = this.activeModelConfig();
|
|
7355
7427
|
if (images.length && !selectedModel?.vision) {
|
|
7356
|
-
|
|
7357
|
-
|
|
7428
|
+
// Give the normal route planner first chance to select another
|
|
7429
|
+
// same-provider vision deployment. If none is available, the kernel
|
|
7430
|
+
// preflight invokes the final mini-OCR + text-only correction path.
|
|
7431
|
+
const hasSameProviderVision = selectedModel && this.config.allModels().some(model => model.enabled !== false && model.provider_id === selectedModel.provider_id &&
|
|
7432
|
+
model.name !== selectedModel.name && !!model.vision);
|
|
7433
|
+
if (hasSameProviderVision)
|
|
7434
|
+
this.switchToFallbackModel('vision input not supported by the selected model');
|
|
7358
7435
|
}
|
|
7359
7436
|
const now = this.nowLabel();
|
|
7360
7437
|
const visibleUserInput = inputEnvelope?.visibleUserInput === undefined
|
|
@@ -8163,9 +8240,17 @@ class Agent {
|
|
|
8163
8240
|
return '[Subagent] Not found.';
|
|
8164
8241
|
const requestedModel = this.normalizeSubagentModelSelection(sa.model && sa.model !== 'default' ? sa.model : this.modelSelectionValue() || this.model);
|
|
8165
8242
|
const requestedDeployment = parseDeploymentSelectionValue(requestedModel);
|
|
8166
|
-
|
|
8243
|
+
let assignedModel = requestedModel === 'auto'
|
|
8167
8244
|
? this.activeModelConfig()
|
|
8168
8245
|
: (requestedDeployment ? this.config.findDeployment(requestedDeployment) : this.config.findModel(requestedModel));
|
|
8246
|
+
// Imported presets (and stale records) may reference a bare model name
|
|
8247
|
+
// that no longer exists in the configured catalog. Resolving by name must
|
|
8248
|
+
// never guess a provider; instead the peer inherits the parent deployment
|
|
8249
|
+
// so the job can still run. Qualified deployment values keep failing
|
|
8250
|
+
// closed — they are exact references, not guesses.
|
|
8251
|
+
if (!assignedModel && !requestedDeployment && requestedModel !== 'auto') {
|
|
8252
|
+
assignedModel = this.activeModelConfig();
|
|
8253
|
+
}
|
|
8169
8254
|
const model = assignedModel?.name || (requestedModel === 'auto' ? this.activeModelName() : requestedModel);
|
|
8170
8255
|
const activeModel = this.activeModelConfig();
|
|
8171
8256
|
const activeProvider = this.engineModel();
|
|
@@ -429,9 +429,24 @@ async function runAgentKernel(agent) {
|
|
|
429
429
|
try {
|
|
430
430
|
const linkedPlanRevisionBeforeRun = agent.getLinkedPlan().revision;
|
|
431
431
|
const modelBeforeKernelRun = agent.model;
|
|
432
|
-
|
|
432
|
+
const preflightVisualFallback = !agent.activeModelConfig()?.vision
|
|
433
|
+
? await agent.finalVisualFallback('vision input not supported by the selected model', processSignal)
|
|
434
|
+
: null;
|
|
435
|
+
let lastTurn = preflightVisualFallback
|
|
436
|
+
? { text: preflightVisualFallback, stopReason: 'stop', errorMessage: '' }
|
|
437
|
+
: await runWithCompressionResume([], false);
|
|
438
|
+
if (preflightVisualFallback) {
|
|
439
|
+
tokens.push({ type: 'text', text: preflightVisualFallback });
|
|
440
|
+
agent.recordWorkStatus('Final visual fallback used: local mini OCR plus conservative text correction.');
|
|
441
|
+
}
|
|
433
442
|
if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some(t => t.text?.includes('[Model fallback]'))) {
|
|
434
|
-
|
|
443
|
+
const notice = `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.`;
|
|
444
|
+
tokens.unshift({ type: 'text', text: notice });
|
|
445
|
+
agent.emitWorkEvent({
|
|
446
|
+
type: 'status',
|
|
447
|
+
content: notice,
|
|
448
|
+
fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId },
|
|
449
|
+
});
|
|
435
450
|
}
|
|
436
451
|
let emptyResponseRetries = 0;
|
|
437
452
|
while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
|
|
@@ -453,6 +468,11 @@ async function runAgentKernel(agent) {
|
|
|
453
468
|
const notice = routeTransitionNotice(agent, previous);
|
|
454
469
|
tokens.push({ type: 'text', text: notice });
|
|
455
470
|
agent.recordWorkStatus(notice);
|
|
471
|
+
agent.emitWorkEvent({
|
|
472
|
+
type: 'status',
|
|
473
|
+
content: notice,
|
|
474
|
+
fallback: { from: previous, to: agent.model, providerId: agent.activeDeployment()?.providerId },
|
|
475
|
+
});
|
|
456
476
|
kernel.state.model = toKernelModel(agent);
|
|
457
477
|
const fallbackToolSurface = refreshToolSurface(true);
|
|
458
478
|
kernel.state.systemPrompt = [agent.buildSystemPrompt(), fallbackToolSurface.systemPromptNotice].filter(Boolean).join('\n\n');
|
|
@@ -460,6 +480,14 @@ async function runAgentKernel(agent) {
|
|
|
460
480
|
await agent.waitForPlannedRouteRetry();
|
|
461
481
|
lastTurn = await runWithCompressionResume([], false);
|
|
462
482
|
}
|
|
483
|
+
if (kernelTurnFailed(agent, lastTurn)) {
|
|
484
|
+
const visualFallback = await agent.finalVisualFallback(lastTurn.errorMessage || lastTurn.text, processSignal);
|
|
485
|
+
if (visualFallback) {
|
|
486
|
+
tokens.push({ type: 'text', text: visualFallback });
|
|
487
|
+
agent.recordWorkStatus('Final visual fallback used: local mini OCR plus conservative text correction.');
|
|
488
|
+
lastTurn = { ...lastTurn, text: visualFallback, errorMessage: '', stopReason: 'stop' };
|
|
489
|
+
}
|
|
490
|
+
}
|
|
463
491
|
if (kernelTurnFailed(agent, lastTurn)) {
|
|
464
492
|
throw new ProviderRunError(normalizePublicProviderError(lastTurn.errorMessage || lastTurn.text, [agent.activeModelConfig()?.api_key]));
|
|
465
493
|
}
|
|
@@ -153,6 +153,7 @@ export declare class AutoRouter {
|
|
|
153
153
|
error: RouteFailure;
|
|
154
154
|
streamCommitted: boolean;
|
|
155
155
|
sideEffectCommitted: boolean;
|
|
156
|
+
allowModelFallback?: boolean;
|
|
156
157
|
}): PlannedRouteAttempt[];
|
|
157
158
|
recordEndpointFailure(deployment: DeploymentRef, failure: RouteFailureType | RouteFailure): void;
|
|
158
159
|
recordEndpointSuccess(deployment: DeploymentRef, latencyMs?: number, throughput?: number): void;
|
package/dist/core/autoRouter.js
CHANGED
|
@@ -290,15 +290,24 @@ class AutoRouter {
|
|
|
290
290
|
retryDelayMs,
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
|
+
if (failure.allowModelFallback === false)
|
|
294
|
+
return attempts.slice(0, remainingAttempts);
|
|
293
295
|
const selection = decision.requestedSelection;
|
|
294
296
|
if (selection.kind === 'fixed')
|
|
295
297
|
return attempts.slice(0, remainingAttempts);
|
|
296
298
|
const scope = selection.kind === 'auto' ? selection.scope : { kind: 'provider', providerId: current.providerId };
|
|
297
299
|
const subset = selection.kind === 'auto' ? selection.subset : undefined;
|
|
298
300
|
const currentGroup = current.logicalModelGroupId;
|
|
301
|
+
const currentProviderId = current.providerId;
|
|
299
302
|
const now = this.now();
|
|
300
303
|
const attemptedDeployments = decision.attempts.map(attempt => attempt.deployment);
|
|
304
|
+
// Fallback is a recovery operation, not a new global routing decision.
|
|
305
|
+
// It must never cross the provider boundary, even when the original Auto
|
|
306
|
+
// selection has global scope. This prevents equal model ids from silently
|
|
307
|
+
// switching credentials/endpoints (for example provider A/model X to
|
|
308
|
+
// provider B/model X).
|
|
301
309
|
const eligible = candidates.filter(candidate => candidate.enabled
|
|
310
|
+
&& candidate.deployment.providerId === currentProviderId
|
|
302
311
|
&& inScope(candidate.deployment, scope)
|
|
303
312
|
&& inSubset(candidate.deployment, subset)
|
|
304
313
|
&& !sameDeployment(candidate.deployment, current)
|
|
@@ -194,6 +194,18 @@ export declare class ConversationKernel {
|
|
|
194
194
|
steering: string[];
|
|
195
195
|
followUp: string[];
|
|
196
196
|
};
|
|
197
|
+
/**
|
|
198
|
+
* dev-0.5.6: 排队 followUp 消息 drain 时转成普通用户消息载荷。
|
|
199
|
+
*
|
|
200
|
+
* 入队时(enqueueSameSession)为去重/编辑保留了 clientMessageId,并把
|
|
201
|
+
* runId 固定为入队时运行的 runId;若原样传给 process,写入 chatMessages
|
|
202
|
+
* 的用户消息会携带旧 runId + clientMessageId,渲染端(PC renderTranscript /
|
|
203
|
+
* 移动端 projectRemoteConversationItems)会把「clientMessageId + runId 命中
|
|
204
|
+
* workRuns」的消息误判为 guide 消息而跳过气泡,导致排队自动发送的消息不
|
|
205
|
+
* 显示在用户输入时间线。清除这两个字段后 process 走普通用户消息分支
|
|
206
|
+
* (chatMessages.push,mode 取 visibleMode,runId 取当前 run)。
|
|
207
|
+
*/
|
|
208
|
+
private drainQueuedFollowUpMessage;
|
|
197
209
|
queueItems(target: ConversationTargetInput): ConversationQueueItemSnapshot[];
|
|
198
210
|
enqueueNext(target: ConversationTargetInput, input: {
|
|
199
211
|
id: string;
|