comfyui-mcp 0.52.0 → 0.52.2
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/README.md +0 -7
- package/dist/boot.js +41 -0
- package/dist/boot.js.map +1 -1
- package/dist/comfyui/client.js +6 -27
- package/dist/comfyui/client.js.map +1 -1
- package/dist/comfyui/cloud-client.js +9 -1
- package/dist/comfyui/cloud-client.js.map +1 -1
- package/dist/comfyui/upload-target.js +38 -0
- package/dist/comfyui/upload-target.js.map +1 -0
- package/dist/orchestrator/agent-backend.js.map +1 -1
- package/dist/orchestrator/blind-native-gate.js +138 -0
- package/dist/orchestrator/blind-native-gate.js.map +1 -0
- package/dist/orchestrator/claude-backend.js +172 -50
- package/dist/orchestrator/claude-backend.js.map +1 -1
- package/dist/orchestrator/codex-backend.js +67 -11
- package/dist/orchestrator/codex-backend.js.map +1 -1
- package/dist/orchestrator/grok-backend.js +24 -14
- package/dist/orchestrator/grok-backend.js.map +1 -1
- package/dist/orchestrator/index.js +23 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/mcp-session-health.js +88 -7
- package/dist/orchestrator/mcp-session-health.js.map +1 -1
- package/dist/orchestrator/panel-agent.js +159 -6
- package/dist/orchestrator/panel-agent.js.map +1 -1
- package/dist/orchestrator/panel-tools.js +62 -14
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/orchestrator/preview-budget.js +41 -0
- package/dist/orchestrator/preview-budget.js.map +1 -0
- package/dist/orchestrator/session-store.js +43 -0
- package/dist/orchestrator/session-store.js.map +1 -1
- package/dist/orchestrator/turn-origins.js +68 -26
- package/dist/orchestrator/turn-origins.js.map +1 -1
- package/dist/services/download-cache.js +17 -4
- package/dist/services/download-cache.js.map +1 -1
- package/dist/services/download-jobs.js +136 -1
- package/dist/services/download-jobs.js.map +1 -1
- package/dist/services/extra-paths.js +57 -4
- package/dist/services/extra-paths.js.map +1 -1
- package/dist/services/image-management.js +9 -2
- package/dist/services/image-management.js.map +1 -1
- package/dist/services/listener-ownership.js +17 -5
- package/dist/services/listener-ownership.js.map +1 -1
- package/dist/services/model-resolver.js +106 -45
- package/dist/services/model-resolver.js.map +1 -1
- package/dist/services/node-management.js +85 -10
- package/dist/services/node-management.js.map +1 -1
- package/dist/services/process-control.js +110 -8
- package/dist/services/process-control.js.map +1 -1
- package/dist/services/queue-monitor.js +54 -7
- package/dist/services/queue-monitor.js.map +1 -1
- package/dist/services/queue-monitor.stall.test.js +86 -1
- package/dist/services/queue-monitor.stall.test.js.map +1 -1
- package/dist/services/registry-client.js +31 -18
- package/dist/services/registry-client.js.map +1 -1
- package/dist/services/ui-bridge.js +80 -0
- package/dist/services/ui-bridge.js.map +1 -1
- package/dist/services/workflow-settings-extractor.js +1 -1
- package/dist/services/workflow-settings-extractor.js.map +1 -1
- package/dist/tools/image-management.js +20 -6
- package/dist/tools/image-management.js.map +1 -1
- package/dist/tools/model-management.js +21 -2
- package/dist/tools/model-management.js.map +1 -1
- package/locales/ar/main.json +1 -0
- package/locales/en/main.json +1 -0
- package/locales/es/main.json +1 -0
- package/locales/fa/main.json +1 -0
- package/locales/fr/main.json +1 -0
- package/locales/ja/main.json +1 -0
- package/locales/ko/main.json +1 -0
- package/locales/pt-BR/main.json +1 -0
- package/locales/ru/main.json +1 -0
- package/locales/tr/main.json +1 -0
- package/locales/zh/main.json +1 -0
- package/locales/zh-TW/main.json +1 -0
- package/package.json +3 -5
- package/plugin/skills/model-registry/SKILL.md +1 -1
- package/scripts/check-docs-links.mjs +23 -0
- package/scripts/check-docs-mdx.mjs +71 -0
- package/scripts/smoke-install.mjs +10 -9
- package/dist/services/download-root-correspondence.js +0 -64
- package/dist/services/download-root-correspondence.js.map +0 -1
- package/model-settings.json +0 -509
- package/model-settings.user.jsonc.example +0 -97
- package/scripts/postinstall.mjs +0 -29
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
// panel server (Claude lane) and the stdio `comfyui` child. `route()` already reads
|
|
21
21
|
// that message for the session id and throws the rest away.
|
|
22
22
|
//
|
|
23
|
-
// This module is
|
|
24
|
-
// it does not diagnose WHY a server is missing
|
|
25
|
-
// two candidate causes need different fixes)
|
|
26
|
-
//
|
|
23
|
+
// This module is the comparison and the wording. The comparison says what was
|
|
24
|
+
// observed and nothing more: it does not diagnose WHY a server is missing
|
|
25
|
+
// (still open for #1524, and the two candidate causes need different fixes).
|
|
26
|
+
// The wording carries the recovery story the same way: a reconnect attempt is
|
|
27
|
+
// reported as attempted, its outcome as whatever the re-read status poll said —
|
|
28
|
+
// never as a promise that a restart or reconnect would help.
|
|
27
29
|
/**
|
|
28
30
|
* Compare the MCP servers we handed the session against the ones it reports.
|
|
29
31
|
*
|
|
@@ -84,6 +86,21 @@ export function inspectMcpServers(configured, reported) {
|
|
|
84
86
|
* sessions their tools are gone.
|
|
85
87
|
*/
|
|
86
88
|
const FAILED_STATUSES = new Set(["failed", "needs-auth", "disabled", "error"]);
|
|
89
|
+
/**
|
|
90
|
+
* Whether an automatic reconnect is worth attempting for a degraded status.
|
|
91
|
+
*
|
|
92
|
+
* `failed` / `error` / absent-from-the-report are DROP shapes — re-running the
|
|
93
|
+
* connection can bring the server back. `needs-auth` waits on the user
|
|
94
|
+
* completing an auth flow and `disabled` is a deliberate off switch: an
|
|
95
|
+
* automatic retry of the first claims effort that cannot land, and of the
|
|
96
|
+
* second overrides intent — so neither is retried, only reported.
|
|
97
|
+
*/
|
|
98
|
+
export function reconnectableMcpStatus(status) {
|
|
99
|
+
if (status === null)
|
|
100
|
+
return true;
|
|
101
|
+
const s = status.trim().toLowerCase();
|
|
102
|
+
return s === "failed" || s === "error";
|
|
103
|
+
}
|
|
87
104
|
/**
|
|
88
105
|
* The line the user sees when a session starts without a server it was given.
|
|
89
106
|
*
|
|
@@ -102,9 +119,73 @@ export function degradedMcpNotice(degraded) {
|
|
|
102
119
|
const plural = degraded.length > 1 ? "servers" : "server";
|
|
103
120
|
const theirTools = degraded.length > 1 ? "Their tools are" : "Its tools are";
|
|
104
121
|
return (`This session started without ${degraded.length} of its MCP ${plural}: ${named}. ` +
|
|
105
|
-
`${theirTools} not available to the agent
|
|
122
|
+
`${theirTools} not available to the agent until the server connects, ` +
|
|
106
123
|
`so anything that needs them will fail or be worked around silently. ` +
|
|
107
|
-
`
|
|
108
|
-
`
|
|
124
|
+
`A reconnect is attempted at the next turn boundary for any of them that can be retried, ` +
|
|
125
|
+
`and the outcome is reported; starting a new session (Disconnect → Connect) ` +
|
|
126
|
+
`re-runs the connection from scratch. ` +
|
|
127
|
+
`Whether either succeeds depends on why this one did not, which this message does not know.`);
|
|
128
|
+
}
|
|
129
|
+
/** Format a degraded-server list the way every notice in this module does. */
|
|
130
|
+
function namedDegraded(degraded) {
|
|
131
|
+
return degraded
|
|
132
|
+
.map((d) => (d.status === null ? `\`${d.name}\` (not reported)` : `\`${d.name}\` (${d.status})`))
|
|
133
|
+
.join(", ");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The line the user sees when a server is down and STAYS down past the
|
|
137
|
+
* recovery this session can offer (#1524). `why` names exactly which situation
|
|
138
|
+
* the session is in — the one thing the reader must not have to guess:
|
|
139
|
+
*
|
|
140
|
+
* - `reconnect-failed`: an automatic reconnect was tried and the re-read still
|
|
141
|
+
* reports the server down;
|
|
142
|
+
* - `unverified`: an automatic reconnect was tried but the status re-read that
|
|
143
|
+
* would judge it could not be read — so the outcome is not claimed either
|
|
144
|
+
* way ("did not come back" would be a claim nobody checked);
|
|
145
|
+
* - `not-retriable`: the status (`needs-auth`, `disabled`) is not one an
|
|
146
|
+
* automatic reconnect can clear — nothing was attempted;
|
|
147
|
+
* - `unsupported`: the harness exposes no reconnect to ask for — nothing
|
|
148
|
+
* could be attempted.
|
|
149
|
+
*
|
|
150
|
+
* Like `degradedMcpNotice`, it stops at the observation: it does not diagnose
|
|
151
|
+
* why the server is down and does not promise a new session fixes it.
|
|
152
|
+
*/
|
|
153
|
+
export function unrecoveredMcpNotice(degraded, why) {
|
|
154
|
+
if (degraded.length === 0)
|
|
155
|
+
return "";
|
|
156
|
+
const plural = degraded.length > 1;
|
|
157
|
+
const reason = why === "reconnect-failed"
|
|
158
|
+
? `the automatic reconnect did not bring ${plural ? "them" : "it"} back`
|
|
159
|
+
: why === "unverified"
|
|
160
|
+
? "an automatic reconnect was attempted, but its outcome could not be read back"
|
|
161
|
+
: why === "not-retriable"
|
|
162
|
+
? "an automatic reconnect cannot clear this status, so none was attempted"
|
|
163
|
+
: "this harness offers no reconnect to ask for";
|
|
164
|
+
return (`MCP ${plural ? "servers" : "server"} ${namedDegraded(degraded)} ${plural ? "are" : "is"} ` +
|
|
165
|
+
`unavailable to the agent — ${reason}. ` +
|
|
166
|
+
`Anything that needs ${plural ? "their" : "its"} tools will fail or be worked around ` +
|
|
167
|
+
`silently for the rest of this session. ` +
|
|
168
|
+
`Starting a new session (Disconnect → Connect) re-runs the connection; ` +
|
|
169
|
+
`whether it succeeds depends on why the server is down, which this message does not know.`);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The line the user sees when a down server reads CONNECTED again (#1524).
|
|
173
|
+
* `byReconnect` distinguishes the two honest shapes: the session's own
|
|
174
|
+
* reconnect attempt landed, or the harness's supervision brought the server
|
|
175
|
+
* back on its own (the reporter watched `comfyui` do exactly this while
|
|
176
|
+
* `panel` stayed down). The claim is only ever what a status poll reported —
|
|
177
|
+
* never that the tools were exercised.
|
|
178
|
+
*/
|
|
179
|
+
export function recoveredMcpNotice(names, byReconnect) {
|
|
180
|
+
if (names.length === 0)
|
|
181
|
+
return "";
|
|
182
|
+
const named = names.map((n) => `\`${n}\``).join(", ");
|
|
183
|
+
const plural = names.length > 1;
|
|
184
|
+
const how = byReconnect
|
|
185
|
+
? `the automatic reconnect brought ${plural ? "them" : "it"} back`
|
|
186
|
+
: `${plural ? "they are" : "it is"} connected again`;
|
|
187
|
+
return (`MCP ${plural ? "servers" : "server"} ${named}: ${how} — ` +
|
|
188
|
+
`${plural ? "their" : "its"} tools are available to the agent. ` +
|
|
189
|
+
`Anything the agent reported as unreachable while the server was down may be worth another try.`);
|
|
109
190
|
}
|
|
110
191
|
//# sourceMappingURL=mcp-session-health.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-session-health.js","sourceRoot":"","sources":["../../src/orchestrator/mcp-session-health.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kCAAkC;AAClC,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,8EAA8E;AAC9E,iFAAiF;AACjF,6EAA6E;AAC7E,6BAA6B;AAC7B,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,cAAc;AACd,EAAE;AACF,mFAAmF;AACnF,EAAE;AACF,qFAAqF;AACrF,oFAAoF;AACpF,4DAA4D;AAC5D,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"mcp-session-health.js","sourceRoot":"","sources":["../../src/orchestrator/mcp-session-health.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@ import { COMPLETION_DISAGREEMENT_NOTE } from "./download-done-guard.js";
|
|
|
20
20
|
import { downloadsAtRiskOfRespawn } from "../services/download-jobs.js";
|
|
21
21
|
import { orphanedByDeferredRespawnNote } from "../services/panel-secrets.js";
|
|
22
22
|
import { errorText, promptText } from "./error-text.js";
|
|
23
|
+
import { MAX_SESSION_PREVIEW_ATTACHMENTS, MAX_SESSION_PREVIEW_BYTES, previewByteBudgetLabel, } from "./preview-budget.js";
|
|
23
24
|
import { dedupeAudioRefs, noAudioPartText } from "./audio-attachment.js";
|
|
24
25
|
import { runErrorNotice } from "./cli-remedy.js";
|
|
25
26
|
import { runCompletionDirective } from "./todo-state.js";
|
|
@@ -305,6 +306,23 @@ export class PanelAgent {
|
|
|
305
306
|
pendingRewind = null;
|
|
306
307
|
/** Captured from the session's init message; enables resume across restarts. */
|
|
307
308
|
sessionId = null;
|
|
309
|
+
/** #1516 — cumulative AUTOMATIC previews committed to turns of the CURRENT
|
|
310
|
+
* provider conversation (charged at the drain, the point of fact). Reset
|
|
311
|
+
* whenever the session id changes: a fresh conversation starts at zero. */
|
|
312
|
+
previewImagesCharged = 0;
|
|
313
|
+
/** #1516 — the byte half of the ledger ADOPTED from the durable store (see
|
|
314
|
+
* initialPreviewLedger). Live bytes come from the backend; the total is
|
|
315
|
+
* previewBytesBase + backend.automaticPreviewBytes(). */
|
|
316
|
+
previewBytesBase = 0;
|
|
317
|
+
/** #1516 — automatic previews composed but NOT delivered, accounted where the
|
|
318
|
+
* withholding was decided (session budget at inject, either budget at the
|
|
319
|
+
* drain trim). Diagnostics; each was named in an agent-visible note. */
|
|
320
|
+
previewWithheldSession = 0;
|
|
321
|
+
/** #1516 — the session id the charged ledger belongs to; reconciled lazily. */
|
|
322
|
+
previewLedgerSid = null;
|
|
323
|
+
/** #1516 — durable ledger awaiting adoption once the live session id proves
|
|
324
|
+
* to be the conversation it was recorded against (never applied blindly). */
|
|
325
|
+
pendingPreviewLedger = null;
|
|
308
326
|
/** Id of the assistant message currently streaming (from message_start), so
|
|
309
327
|
* stream deltas and the final committed `say` share one bubble id. */
|
|
310
328
|
streamMsgId = null;
|
|
@@ -334,6 +352,7 @@ export class PanelAgent {
|
|
|
334
352
|
this.deps = deps;
|
|
335
353
|
this.model = deps.model;
|
|
336
354
|
this.effort = deps.effort;
|
|
355
|
+
this.pendingPreviewLedger = deps.initialPreviewLedger ?? null;
|
|
337
356
|
// Default to the Claude adapter; injectable so a future toggle can swap it.
|
|
338
357
|
this.backend =
|
|
339
358
|
backend ??
|
|
@@ -343,6 +362,7 @@ export class PanelAgent {
|
|
|
343
362
|
systemAppend: deps.systemAppend,
|
|
344
363
|
panelServer: deps.panelServer,
|
|
345
364
|
pluginPath: deps.pluginPath,
|
|
365
|
+
isBlind: deps.isBlind,
|
|
346
366
|
});
|
|
347
367
|
}
|
|
348
368
|
short() {
|
|
@@ -478,6 +498,78 @@ export class PanelAgent {
|
|
|
478
498
|
queuedPreviewImageCount() {
|
|
479
499
|
return this.queue.reduce((n, it) => n + (it.completionOnly ? (it.images?.length ?? 0) : 0), 0);
|
|
480
500
|
}
|
|
501
|
+
/** #1516 — keep the cumulative preview ledger attached to the conversation it
|
|
502
|
+
* measures. The session id only becomes known at the init event and can
|
|
503
|
+
* change under us (fresh fork, resume-miss restart, provider switch), so
|
|
504
|
+
* this runs lazily at every budget decision: a new id resets the ledger to
|
|
505
|
+
* zero, and the durable pending ledger is adopted ONLY when the live id is
|
|
506
|
+
* exactly the one it was recorded against — a ledger applied to the wrong
|
|
507
|
+
* conversation would either starve a fresh one or un-bound an old one. */
|
|
508
|
+
reconcilePreviewLedger() {
|
|
509
|
+
if (this.sessionId === this.previewLedgerSid)
|
|
510
|
+
return;
|
|
511
|
+
this.previewLedgerSid = this.sessionId;
|
|
512
|
+
this.previewImagesCharged = 0;
|
|
513
|
+
this.previewBytesBase = 0;
|
|
514
|
+
this.previewWithheldSession = 0;
|
|
515
|
+
if (this.sessionId && this.pendingPreviewLedger) {
|
|
516
|
+
if (this.pendingPreviewLedger.sid === this.sessionId) {
|
|
517
|
+
this.previewImagesCharged = this.pendingPreviewLedger.images;
|
|
518
|
+
this.previewBytesBase = this.pendingPreviewLedger.bytes;
|
|
519
|
+
}
|
|
520
|
+
this.pendingPreviewLedger = null;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/** #1516 — cumulative delivered automatic-preview BYTES for the current
|
|
524
|
+
* conversation: the durable base (pre-restart) plus what this process's
|
|
525
|
+
* backend actually fetched. Backends that do not report bytes contribute
|
|
526
|
+
* zero and are bounded by the image count alone. */
|
|
527
|
+
sessionPreviewBytes() {
|
|
528
|
+
return this.previewBytesBase + (this.backend.automaticPreviewBytes?.() ?? 0);
|
|
529
|
+
}
|
|
530
|
+
/** #1516 — how many automatic previews this conversation may still receive:
|
|
531
|
+
* the tighter of the remaining COUNT and the remaining BYTES. Bytes
|
|
532
|
+
* delivered so far is what is knowable here — the in-flight turn's fetches
|
|
533
|
+
* have not happened yet — so the byte gate can overshoot by at most one
|
|
534
|
+
* turn's worth; the backend enforces the same budget at fetch time, which
|
|
535
|
+
* is the bound of record (see codex-backend's image loop). */
|
|
536
|
+
sessionPreviewBudgetLeft() {
|
|
537
|
+
this.reconcilePreviewLedger();
|
|
538
|
+
if (this.sessionPreviewBytes() >= MAX_SESSION_PREVIEW_BYTES)
|
|
539
|
+
return 0;
|
|
540
|
+
return Math.max(0, MAX_SESSION_PREVIEW_ATTACHMENTS - this.previewImagesCharged);
|
|
541
|
+
}
|
|
542
|
+
/** #1516 — the cumulative automatic-preview ledger, for diagnostics: what was
|
|
543
|
+
* delivered to the current provider conversation, what was withheld at the
|
|
544
|
+
* session budget, and the budget itself. */
|
|
545
|
+
previewBudgetStatus() {
|
|
546
|
+
this.reconcilePreviewLedger();
|
|
547
|
+
return {
|
|
548
|
+
images: this.previewImagesCharged,
|
|
549
|
+
bytes: this.sessionPreviewBytes(),
|
|
550
|
+
withheld: this.previewWithheldSession,
|
|
551
|
+
budgetImages: MAX_SESSION_PREVIEW_ATTACHMENTS,
|
|
552
|
+
budgetBytes: MAX_SESSION_PREVIEW_BYTES,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
/** #1516 — persist the ledger through the manager so an orchestrator restart
|
|
556
|
+
* does not reset a budget the still-living provider conversation keeps
|
|
557
|
+
* accumulating against. Bytes are read NOW (the in-flight turn's fetches
|
|
558
|
+
* lag), so the stored value is a lower bound — disclosed, and refreshed on
|
|
559
|
+
* every later change. */
|
|
560
|
+
reportPreviewLedger() {
|
|
561
|
+
if (!this.deps.onPreviewLedger)
|
|
562
|
+
return;
|
|
563
|
+
try {
|
|
564
|
+
this.deps.onPreviewLedger(this.tabId, {
|
|
565
|
+
images: this.previewImagesCharged,
|
|
566
|
+
bytes: this.sessionPreviewBytes(),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
catch (err) {
|
|
570
|
+
logger.debug(`[panel-agent ${this.short()}] preview-ledger report failed: ${msgOf(err)}`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
481
573
|
/** Drop a still-queued message (the user cancelled/edited it before the agent
|
|
482
574
|
* got to it). Returns true if it was found and removed; false if it was
|
|
483
575
|
* already dequeued (the turn started — too late to cancel). */
|
|
@@ -536,10 +628,29 @@ export class PanelAgent {
|
|
|
536
628
|
// this one, which is why the drain corrects the count rather than trusting
|
|
537
629
|
// it (see MAX_RUN_COMPLETION_IMAGE_ATTACHMENTS).
|
|
538
630
|
const previewBudgetLeft = Math.max(0, MAX_RUN_COMPLETION_IMAGE_ATTACHMENTS - this.queuedPreviewImageCount());
|
|
631
|
+
// #1516 — ...AND what is left of the CONVERSATION's cumulative budget.
|
|
632
|
+
// The per-turn bound above stops one turn from going unbounded; this one
|
|
633
|
+
// stops N turns from accumulating 8N inline images into a Codex rollout
|
|
634
|
+
// that compaction then recopies without a media-aware bound. Queued
|
|
635
|
+
// previews are charged against the session budget HERE too (they drain
|
|
636
|
+
// into this conversation), so a burst queueing behind a busy turn cannot
|
|
637
|
+
// over-commit it. `sessionBound` remembers WHICH budget spoke, so the
|
|
638
|
+
// notice below names the bound that actually fired.
|
|
639
|
+
const sessionBudgetLeft = Math.max(0, this.sessionPreviewBudgetLeft() - this.queuedPreviewImageCount());
|
|
640
|
+
const sessionBound = sessionBudgetLeft < previewBudgetLeft;
|
|
641
|
+
const effectiveBudget = sessionBound ? sessionBudgetLeft : previewBudgetLeft;
|
|
539
642
|
const attachedImgs = correlationIsUntrusted
|
|
540
643
|
? []
|
|
541
|
-
: attachableImgs.slice(0,
|
|
644
|
+
: attachableImgs.slice(0, effectiveBudget);
|
|
542
645
|
const omittedImgs = attachableImgs.length - attachedImgs.length;
|
|
646
|
+
// #1516 — account for what the SESSION budget withheld (diagnostics +
|
|
647
|
+
// durable ledger). Only the session-bound case counts here: a per-turn
|
|
648
|
+
// trim is the pre-existing turn budget doing its job, and an untrusted
|
|
649
|
+
// correlation never had pixels to withhold.
|
|
650
|
+
if (sessionBound && !correlationIsUntrusted && omittedImgs > 0) {
|
|
651
|
+
this.previewWithheldSession += omittedImgs;
|
|
652
|
+
this.reportPreviewLedger();
|
|
653
|
+
}
|
|
543
654
|
// The unnamed remainder. Every sentence below counts outputs with
|
|
544
655
|
// `imgs.length` but attaches and names from `attachableImgs`, so on a MIXED
|
|
545
656
|
// event — one output with a filename, one without — the two disagree and
|
|
@@ -614,9 +725,13 @@ export class PanelAgent {
|
|
|
614
725
|
attachableImgs.length === 0
|
|
615
726
|
? `The panel reported no usable filename for ${imgs.length === 1 ? "it" : "them"}, so ${imgs.length === 1 ? "it is" : "they are"} NOT attached to this agent turn and cannot be fetched by name — check get_history (action:"list") if you need to see what this run produced. `
|
|
616
727
|
: attachedImgs.length === 0
|
|
617
|
-
?
|
|
728
|
+
? sessionBound
|
|
729
|
+
? `NONE of these outputs are attached to this agent turn: this conversation's cumulative automatic-preview budget (${MAX_SESSION_PREVIEW_ATTACHMENTS} images / ~${previewByteBudgetLabel()}) is spent — the bound that keeps a long session from accumulating unbounded inline media the provider retains and recopies at compaction. All ${imgs.length} are shown to the user in the panel — fetch one with get_image action:"get" if you need to look: ${withheldRefs}. `
|
|
730
|
+
: `NONE of these outputs are attached to this agent turn: earlier completion(s) in this same turn already spent its ${MAX_RUN_COMPLETION_IMAGE_ATTACHMENTS}-image preview budget. All ${imgs.length} are shown to the user in the panel — fetch one with get_image action:"get" if you need to look: ${withheldRefs}. `
|
|
618
731
|
: omittedImgs > 0
|
|
619
|
-
?
|
|
732
|
+
? sessionBound
|
|
733
|
+
? `The first ${attachedImgs.length} image(s) are attached below; all ${imgs.length} outputs are already shown to the user in the panel. ${omittedImgs} further preview(s) were omitted because this conversation's cumulative automatic-preview budget (${MAX_SESSION_PREVIEW_ATTACHMENTS} images / ~${previewByteBudgetLabel()}) is nearly spent — fetch one with get_image action:"get" if you need it: ${withheldRefs}. `
|
|
734
|
+
: `The first ${attachedImgs.length} image(s) are attached below; all ${imgs.length} outputs are already shown to the user in the panel. ${omittedImgs} further preview(s) were omitted to keep this TURN's image context bounded — fetch one with get_image action:"get" if you need it: ${withheldRefs}. `
|
|
620
735
|
: `The image(s) are attached below and already shown to the user in the panel. `
|
|
621
736
|
: `You cannot view images on this provider, but they are already shown to the user in the panel. `
|
|
622
737
|
: ``) +
|
|
@@ -647,7 +762,10 @@ export class PanelAgent {
|
|
|
647
762
|
// up to the bound above. Past it (or for an UNDETERMINED origin) the text
|
|
648
763
|
// says so and points at get_image, so a fetch is needed but never a guess.
|
|
649
764
|
if (this.backend.capabilities.vision) {
|
|
650
|
-
|
|
765
|
+
// `automatic` is what the backend's cumulative byte ledger keys on —
|
|
766
|
+
// a user's explicit attachment never carries it and never spends the
|
|
767
|
+
// session preview budget (#1516).
|
|
768
|
+
images = attachedImgs.map((i) => ({ ...i, type: i.type ?? "output", automatic: true }));
|
|
651
769
|
}
|
|
652
770
|
}
|
|
653
771
|
else if (ev.kind === "ask_answer") {
|
|
@@ -1155,17 +1273,34 @@ export class PanelAgent {
|
|
|
1155
1273
|
// corrects the notices instead of assuming they agree with the outcome.
|
|
1156
1274
|
// A user's own attachment is never touched — only `completionOnly` items,
|
|
1157
1275
|
// which are the injected panel events.
|
|
1158
|
-
|
|
1276
|
+
// The drain is also where the CUMULATIVE session budget (#1516) meets the
|
|
1277
|
+
// fact of the turn: the ceiling is the tighter of the per-turn 8 and what
|
|
1278
|
+
// the conversation has left. injectEvent already charged queued previews
|
|
1279
|
+
// against its estimate, so an in-flight batch (invisible there) is the
|
|
1280
|
+
// divergence this line absorbs — same estimate/fact split as the per-turn
|
|
1281
|
+
// budget above.
|
|
1282
|
+
let previewBudget = Math.min(MAX_RUN_COMPLETION_IMAGE_ATTACHMENTS, this.sessionPreviewBudgetLeft());
|
|
1159
1283
|
const trimmedPreviews = [];
|
|
1284
|
+
let deliveredPreviews = 0;
|
|
1160
1285
|
let images = batch.flatMap((it) => {
|
|
1161
1286
|
const refs = it.images ?? [];
|
|
1162
1287
|
if (!it.completionOnly)
|
|
1163
1288
|
return refs;
|
|
1164
1289
|
const take = refs.slice(0, Math.max(0, previewBudget));
|
|
1165
1290
|
previewBudget -= take.length;
|
|
1291
|
+
deliveredPreviews += take.length;
|
|
1166
1292
|
trimmedPreviews.push(...refs.slice(take.length));
|
|
1167
1293
|
return take;
|
|
1168
1294
|
});
|
|
1295
|
+
// Charge the conversation for what THIS turn actually carries, and persist
|
|
1296
|
+
// the ledger so a restart does not reset the budget of a conversation that
|
|
1297
|
+
// lives on. A replayed (interrupted-then-requeued) batch charges AGAIN —
|
|
1298
|
+
// deliberately: the replay really does write those pixels into the
|
|
1299
|
+
// provider thread a second time, which is the accumulation being bounded.
|
|
1300
|
+
if (deliveredPreviews > 0) {
|
|
1301
|
+
this.previewImagesCharged += deliveredPreviews;
|
|
1302
|
+
this.reportPreviewLedger();
|
|
1303
|
+
}
|
|
1169
1304
|
// A trim here means a notice ABOVE is now wrong: it was composed when its
|
|
1170
1305
|
// images were going to ride this turn, and they are not. Measured sequence
|
|
1171
1306
|
// — 8 previews in flight, a second completion queued behind them (its own
|
|
@@ -1182,9 +1317,13 @@ export class PanelAgent {
|
|
|
1182
1317
|
if (trimmedPreviews.length && this.backend.capabilities.vision) {
|
|
1183
1318
|
text +=
|
|
1184
1319
|
`\n\n[panel note: ${trimmedPreviews.length} automatic preview image(s) were NOT attached to this turn. ` +
|
|
1185
|
-
`Several run completions were merged into one turn and they SHARE
|
|
1320
|
+
`Several run completions were merged into one turn and they SHARE one bounded automatic-preview budget (at most ${MAX_RUN_COMPLETION_IMAGE_ATTACHMENTS} per turn, and a cumulative per-conversation ceiling of ${MAX_SESSION_PREVIEW_ATTACHMENTS} images / ~${previewByteBudgetLabel()}), ` +
|
|
1186
1321
|
`so a notice above may say its images are "attached below" when they are not — where they disagree, THIS note is the accurate one. ` +
|
|
1187
1322
|
`They are outputs on the ComfyUI side that this turn does not carry — fetch any of them with get_image action:"get" — ${describeImageRefs(trimmedPreviews)}.]`;
|
|
1323
|
+
// These previews are withheld from the conversation for good (the note
|
|
1324
|
+
// redirects to get_image), so the diagnostics counter accounts them.
|
|
1325
|
+
this.previewWithheldSession += trimmedPreviews.length;
|
|
1326
|
+
this.reportPreviewLedger();
|
|
1188
1327
|
logger.info(`[panel-agent ${this.short()}] trimmed ${trimmedPreviews.length} automatic preview image(s) at the drain ` +
|
|
1189
1328
|
`(completions merged into one turn); the turn says so and names them (#1516)`);
|
|
1190
1329
|
}
|
|
@@ -1985,6 +2124,19 @@ export class PanelAgentManager {
|
|
|
1985
2124
|
this.opts.sessionStore?.set(id, sid, this.opts.identityForKey?.(id));
|
|
1986
2125
|
this.opts.onSession?.(id, sid, model);
|
|
1987
2126
|
},
|
|
2127
|
+
// #1516 — persist the cumulative preview ledger beside the session id it
|
|
2128
|
+
// belongs to. setPreviewLedger REFUSES a session mismatch, so a ledger is
|
|
2129
|
+
// only ever stored against the exact conversation it measures — a stale
|
|
2130
|
+
// report for a since-replaced session is dropped, not misfiled.
|
|
2131
|
+
onPreviewLedger: (id, ledger) => {
|
|
2132
|
+
const sid = this.agents.get(id)?.sessionId;
|
|
2133
|
+
if (sid)
|
|
2134
|
+
this.opts.sessionStore?.setPreviewLedger(id, sid, ledger);
|
|
2135
|
+
},
|
|
2136
|
+
// #1516 — hand the freshly-spawned agent the durable ledger of the
|
|
2137
|
+
// conversation it is expected to resume; the agent adopts it only when
|
|
2138
|
+
// the live session id proves to be that exact conversation.
|
|
2139
|
+
initialPreviewLedger: this.opts.sessionStore?.previewLedger(tabId),
|
|
1988
2140
|
onTurnAnchor: this.opts.onTurnAnchor,
|
|
1989
2141
|
// Wrap onTurn so the manager learns when a turn ends — the safe point to
|
|
1990
2142
|
// apply a deferred, session-restarting effort change.
|
|
@@ -2004,6 +2156,7 @@ export class PanelAgentManager {
|
|
|
2004
2156
|
onEventUndelivered: this.opts.onEventUndelivered,
|
|
2005
2157
|
panelServer: this.opts.makePanelServer?.(tabId),
|
|
2006
2158
|
pluginPath: this.opts.pluginPath,
|
|
2159
|
+
isBlind: this.opts.isBlind,
|
|
2007
2160
|
}, backend);
|
|
2008
2161
|
}
|
|
2009
2162
|
/** Update the system-prompt append used for NEWLY-spawned agents. Lets the
|