blun-king-cli 9.1.75 → 9.1.77
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/LIESMICH.txt +25 -1
- package/README.md +23 -0
- package/bin/micro-compaction-policy.cjs +75 -0
- package/bin/pending-media-policy.cjs +43 -0
- package/blun.mjs +105 -23
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -9,7 +9,7 @@ Installation
|
|
|
9
9
|
------------
|
|
10
10
|
Die geprüfte Version exakt global installieren:
|
|
11
11
|
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.77
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -43,6 +43,11 @@ einen aktiven Zug ab, ohne den geschriebenen Entwurf zu löschen. Das gilt auch
|
|
|
43
43
|
bei Autovervollständigung, Geistervorschlägen, Bash-Eingabe und einer offenen
|
|
44
44
|
Mehrzeileneingabe.
|
|
45
45
|
|
|
46
|
+
Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
|
|
47
|
+
konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
|
|
48
|
+
FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
|
|
49
|
+
`turn.agent_busy`-Fehler.
|
|
50
|
+
|
|
46
51
|
Zuverlässiger King-Start
|
|
47
52
|
-----------------------
|
|
48
53
|
Der Windows-Hilfsprozess für private Pfade übernimmt TEMP und TMP aus der
|
|
@@ -67,6 +72,20 @@ parallel geprüft und eingelesen. Die Skills werden weiterhin in der
|
|
|
67
72
|
ursprünglichen, festen Reihenfolge registriert; Priorität und Verhalten bei
|
|
68
73
|
doppelten Namen bleiben unverändert.
|
|
69
74
|
|
|
75
|
+
Kontextentlastung bei langen Sitzungen
|
|
76
|
+
--------------------------------------
|
|
77
|
+
King bewahrt den vollständigen Verlauf weiterhin im Sitzungs-Wire auf. Erreicht
|
|
78
|
+
der aktive Verlauf 75 Prozent des Modellfensters, ersetzt die Modellprojektion
|
|
79
|
+
ältere große Werkzeugergebnisse durch einen kurzen Platzhalter. Die letzten 20
|
|
80
|
+
Nachrichten bleiben unverändert. Solange der Prefix-Cache warm ist, wird der
|
|
81
|
+
Schnitt höchstens nach jeweils 20 weiteren Nachrichten verschoben. Nach einer
|
|
82
|
+
Stunde ohne Modellantwort darf er sofort nachziehen.
|
|
83
|
+
|
|
84
|
+
Dabei werden keine gespeicherten Nachrichten geändert oder gelöscht. Fortsetzen,
|
|
85
|
+
Exportieren und die sichtbare Historie behalten die ursprünglichen
|
|
86
|
+
Werkzeugergebnisse. Das Telemetrieereignis `micro_compaction_finished` nennt den
|
|
87
|
+
Auslöser, den Schnitt und die geschätzte Tokenzahl vor und nach der Entlastung.
|
|
88
|
+
|
|
70
89
|
Zug-Wächter
|
|
71
90
|
-----------
|
|
72
91
|
Läuft ein Zug 20 Minuten ohne neues Werkzeugergebnis, meldet die Konsole den
|
|
@@ -175,6 +194,11 @@ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Seku
|
|
|
175
194
|
genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
|
|
176
195
|
GetMedia abrufen.
|
|
177
196
|
|
|
197
|
+
Ein angenommener Medienauftrag bleibt über Verdichtungen und schnelle
|
|
198
|
+
Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
|
|
199
|
+
verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
|
|
200
|
+
die Medienerzeugung sei nicht verfügbar.
|
|
201
|
+
|
|
178
202
|
Angehängte Bilder werden weiterhin mit ReadMediaFile gelesen. Bild-, Video- und
|
|
179
203
|
Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
|
|
180
204
|
nutzbar bleibt. GenerateVideo kann außerdem einen abgeschlossenen Bildauftrag
|
package/README.md
CHANGED
|
@@ -62,6 +62,10 @@ blun tools enable context7
|
|
|
62
62
|
Telegram-Nachrichten werden in Eingangsreihenfolge verarbeitet. Trifft eine
|
|
63
63
|
Nachricht im Leerlauf ein, startet die Warteschlange selbstständig; während
|
|
64
64
|
eines Werkzeuglaufs wird sie am nächsten geschützten Übergabepunkt eingefügt.
|
|
65
|
+
Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
|
|
66
|
+
konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
|
|
67
|
+
FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
|
|
68
|
+
`turn.agent_busy`-Fehler.
|
|
65
69
|
`Strg+C` und `Esc` brechen einen aktiven Zug zuverlässig ab, ohne den bereits
|
|
66
70
|
geschriebenen Entwurf zu löschen. Das gilt auch bei Autovervollständigung,
|
|
67
71
|
Geistervorschlägen, Bash-Eingabe und einer noch offenen Mehrzeileneingabe.
|
|
@@ -90,6 +94,20 @@ parallel geprüft und eingelesen. Die Skills werden weiterhin in der
|
|
|
90
94
|
ursprünglichen, festen Reihenfolge registriert; Priorität und Verhalten bei
|
|
91
95
|
doppelten Namen bleiben unverändert.
|
|
92
96
|
|
|
97
|
+
## Kontextentlastung bei langen Sitzungen
|
|
98
|
+
|
|
99
|
+
King bewahrt den vollständigen Verlauf weiterhin im Sitzungs-Wire auf. Erreicht
|
|
100
|
+
der aktive Verlauf 75 Prozent des Modellfensters, ersetzt die Modellprojektion
|
|
101
|
+
ältere große Werkzeugergebnisse durch einen kurzen Platzhalter. Die letzten 20
|
|
102
|
+
Nachrichten bleiben unverändert. Solange der Prefix-Cache warm ist, wird der
|
|
103
|
+
Schnitt höchstens nach jeweils 20 weiteren Nachrichten verschoben. Nach einer
|
|
104
|
+
Stunde ohne Modellantwort darf er sofort nachziehen.
|
|
105
|
+
|
|
106
|
+
Dabei werden keine gespeicherten Nachrichten geändert oder gelöscht. Fortsetzen,
|
|
107
|
+
Exportieren und die sichtbare Historie behalten die ursprünglichen
|
|
108
|
+
Werkzeugergebnisse. Das Telemetrieereignis `micro_compaction_finished` nennt den
|
|
109
|
+
Auslöser, den Schnitt und die geschätzte Tokenzahl vor und nach der Entlastung.
|
|
110
|
+
|
|
93
111
|
## Zug-Wächter
|
|
94
112
|
|
|
95
113
|
Läuft ein Zug 20 Minuten ohne neues Werkzeugergebnis, meldet die Konsole den
|
|
@@ -203,6 +221,11 @@ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Seku
|
|
|
203
221
|
genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
|
|
204
222
|
`GetMedia` abrufen.
|
|
205
223
|
|
|
224
|
+
Ein angenommener Medienauftrag bleibt über Verdichtungen und schnelle
|
|
225
|
+
Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
|
|
226
|
+
verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
|
|
227
|
+
die Medienerzeugung sei nicht verfügbar.
|
|
228
|
+
|
|
206
229
|
Angehängte Bilder werden weiterhin mit `ReadMediaFile` gelesen. Bild-, Video-
|
|
207
230
|
und Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
|
|
208
231
|
nutzbar bleibt. `GenerateVideo` kann außerdem einen abgeschlossenen Bildauftrag
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MICRO_COMPACTION_PRESSURE_RATIO = 0.75;
|
|
4
|
+
const MICRO_COMPACTION_MIN_ADVANCE_MESSAGES = 20;
|
|
5
|
+
|
|
6
|
+
function selectMicroCompactionCutoff(options = {}) {
|
|
7
|
+
const historyLength = nonNegativeInteger(options.historyLength);
|
|
8
|
+
const currentCutoff = nonNegativeInteger(options.currentCutoff);
|
|
9
|
+
const keepRecentMessages = nonNegativeInteger(options.keepRecentMessages);
|
|
10
|
+
const nextCutoff = Math.max(0, historyLength - keepRecentMessages);
|
|
11
|
+
if (nextCutoff <= currentCutoff) return null;
|
|
12
|
+
|
|
13
|
+
const maxContextTokens = positiveFinite(options.maxContextTokens);
|
|
14
|
+
const contextTokens = nonNegativeFinite(options.contextTokens);
|
|
15
|
+
const contextUsageRatio = maxContextTokens === null
|
|
16
|
+
? 1
|
|
17
|
+
: contextTokens / maxContextTokens;
|
|
18
|
+
const minContextUsageRatio = nonNegativeFinite(options.minContextUsageRatio);
|
|
19
|
+
if (contextUsageRatio < minContextUsageRatio) return null;
|
|
20
|
+
|
|
21
|
+
const now = finiteOr(options.now, Date.now());
|
|
22
|
+
const lastAssistantAt = finiteOrNull(options.lastAssistantAt);
|
|
23
|
+
const cacheAgeMs = lastAssistantAt === null ? null : Math.max(0, now - lastAssistantAt);
|
|
24
|
+
const cacheMissedThresholdMs = nonNegativeFinite(options.cacheMissedThresholdMs);
|
|
25
|
+
const cacheMissed = cacheAgeMs !== null && cacheAgeMs >= cacheMissedThresholdMs;
|
|
26
|
+
const underContextPressure = maxContextTokens !== null
|
|
27
|
+
&& contextUsageRatio >= MICRO_COMPACTION_PRESSURE_RATIO;
|
|
28
|
+
if (!cacheMissed && !underContextPressure) return null;
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
!cacheMissed
|
|
32
|
+
&& currentCutoff > 0
|
|
33
|
+
&& nextCutoff - currentCutoff < MICRO_COMPACTION_MIN_ADVANCE_MESSAGES
|
|
34
|
+
) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
cacheAgeMs,
|
|
40
|
+
contextUsageRatio,
|
|
41
|
+
cutoff: nextCutoff,
|
|
42
|
+
trigger: cacheMissed ? 'cache_miss' : 'context_pressure',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function finiteOr(value, fallback) {
|
|
47
|
+
const number = Number(value);
|
|
48
|
+
return Number.isFinite(number) ? number : fallback;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function finiteOrNull(value) {
|
|
52
|
+
if (value === null || value === undefined) return null;
|
|
53
|
+
const number = Number(value);
|
|
54
|
+
return Number.isFinite(number) ? number : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nonNegativeFinite(value) {
|
|
58
|
+
const number = Number(value);
|
|
59
|
+
return Number.isFinite(number) && number >= 0 ? number : 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function nonNegativeInteger(value) {
|
|
63
|
+
return Math.floor(nonNegativeFinite(value));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function positiveFinite(value) {
|
|
67
|
+
const number = Number(value);
|
|
68
|
+
return Number.isFinite(number) && number > 0 ? number : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
MICRO_COMPACTION_MIN_ADVANCE_MESSAGES,
|
|
73
|
+
MICRO_COMPACTION_PRESSURE_RATIO,
|
|
74
|
+
selectMicroCompactionCutoff,
|
|
75
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TERMINAL_STATUSES = new Set([
|
|
4
|
+
'blocked',
|
|
5
|
+
'cancelled',
|
|
6
|
+
'canceled',
|
|
7
|
+
'complete',
|
|
8
|
+
'completed',
|
|
9
|
+
'expired',
|
|
10
|
+
'failed',
|
|
11
|
+
'succeeded',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
function validMediaId(value) {
|
|
15
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 200
|
|
16
|
+
&& /^[A-Za-z0-9_-]+$/.test(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function terminalStatus(value) {
|
|
20
|
+
return typeof value === 'string' && TERMINAL_STATUSES.has(value.trim().toLowerCase());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createPendingMediaTracker() {
|
|
24
|
+
const pending = new Set();
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
observeSubmission(job) {
|
|
28
|
+
if (!validMediaId(job?.id)) return;
|
|
29
|
+
if (terminalStatus(job?.status)) pending.delete(job.id);
|
|
30
|
+
else pending.add(job.id);
|
|
31
|
+
},
|
|
32
|
+
observeLookup(result) {
|
|
33
|
+
if (!validMediaId(result?.id)) return;
|
|
34
|
+
if (result.kind !== 'status' || terminalStatus(result.status)) pending.delete(result.id);
|
|
35
|
+
else pending.add(result.id);
|
|
36
|
+
},
|
|
37
|
+
ids() {
|
|
38
|
+
return [...pending];
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { createPendingMediaTracker };
|
package/blun.mjs
CHANGED
|
@@ -75889,8 +75889,9 @@ var init_full = __esmMin((() => {
|
|
|
75889
75889
|
}));
|
|
75890
75890
|
//#endregion
|
|
75891
75891
|
//#region ../../packages/agent-core/src/agent/compaction/micro.ts
|
|
75892
|
-
var DEFAULT_CONFIG, MicroCompaction;
|
|
75892
|
+
var selectMicroCompactionCutoff, DEFAULT_CONFIG, MicroCompaction;
|
|
75893
75893
|
var init_micro = __esmMin((() => {
|
|
75894
|
+
({ selectMicroCompactionCutoff } = createRequire(import.meta.url)("./bin/micro-compaction-policy.cjs"));
|
|
75894
75895
|
init_tokens();
|
|
75895
75896
|
DEFAULT_CONFIG = {
|
|
75896
75897
|
keepRecentMessages: 20,
|
|
@@ -75920,9 +75921,67 @@ var init_micro = __esmMin((() => {
|
|
|
75920
75921
|
});
|
|
75921
75922
|
this.cutoff = cutoff;
|
|
75922
75923
|
}
|
|
75923
|
-
detect() {
|
|
75924
|
+
detect() {
|
|
75925
|
+
const config = this.config;
|
|
75926
|
+
const { history, lastAssistantAt } = this.agent.context;
|
|
75927
|
+
const maxContextTokens = this.agent.config.modelCapabilities.max_context_tokens;
|
|
75928
|
+
const contextTokens = this.agent.context.tokenCountWithPending;
|
|
75929
|
+
const selection = selectMicroCompactionCutoff({
|
|
75930
|
+
historyLength: history.length,
|
|
75931
|
+
currentCutoff: this.cutoff,
|
|
75932
|
+
keepRecentMessages: config.keepRecentMessages,
|
|
75933
|
+
contextTokens,
|
|
75934
|
+
maxContextTokens,
|
|
75935
|
+
lastAssistantAt,
|
|
75936
|
+
now: Date.now(),
|
|
75937
|
+
cacheMissedThresholdMs: config.cacheMissedThresholdMs,
|
|
75938
|
+
minContextUsageRatio: config.minContextUsageRatio
|
|
75939
|
+
});
|
|
75940
|
+
if (selection === null) return;
|
|
75941
|
+
const previousCutoff = this.cutoff;
|
|
75942
|
+
const effect = this.measureEffect(history, selection.cutoff);
|
|
75943
|
+
const previousEffect = this.measureEffect(history, previousCutoff);
|
|
75944
|
+
if (effect.truncatedToolResultCount <= previousEffect.truncatedToolResultCount) return;
|
|
75945
|
+
this.apply(selection.cutoff);
|
|
75946
|
+
const rawContextTokens = estimateTokensForMessages(history);
|
|
75947
|
+
const tokensBefore = rawContextTokens - previousEffect.truncatedToolResultTokensBefore + previousEffect.truncatedToolResultTokensAfter;
|
|
75948
|
+
const tokensAfter = rawContextTokens - effect.truncatedToolResultTokensBefore + effect.truncatedToolResultTokensAfter;
|
|
75949
|
+
this.agent.telemetry.track("micro_compaction_finished", {
|
|
75950
|
+
trigger: selection.trigger,
|
|
75951
|
+
keep_recent_messages: config.keepRecentMessages,
|
|
75952
|
+
min_content_tokens: config.minContentTokens,
|
|
75953
|
+
cache_missed_threshold_ms: config.cacheMissedThresholdMs,
|
|
75954
|
+
truncated_marker: config.truncatedMarker,
|
|
75955
|
+
min_context_usage_ratio: config.minContextUsageRatio,
|
|
75956
|
+
context_usage_ratio: selection.contextUsageRatio,
|
|
75957
|
+
truncated_tool_result_count: effect.truncatedToolResultCount,
|
|
75958
|
+
truncated_tool_result_tokens_before: effect.truncatedToolResultTokensBefore,
|
|
75959
|
+
truncated_tool_result_tokens_after: effect.truncatedToolResultTokensAfter,
|
|
75960
|
+
tokens_before: tokensBefore,
|
|
75961
|
+
tokens_after: tokensAfter,
|
|
75962
|
+
previous_cutoff: previousCutoff,
|
|
75963
|
+
cutoff: selection.cutoff,
|
|
75964
|
+
message_count: history.length,
|
|
75965
|
+
cache_age_ms: selection.cacheAgeMs,
|
|
75966
|
+
thinking_effort: this.agent.config.thinkingEffort
|
|
75967
|
+
});
|
|
75968
|
+
}
|
|
75924
75969
|
compact(messages) {
|
|
75925
|
-
|
|
75970
|
+
const config = this.config;
|
|
75971
|
+
const result = [];
|
|
75972
|
+
let i = 0;
|
|
75973
|
+
for (const msg of messages) {
|
|
75974
|
+
if (i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && estimateTokensForContentParts(msg.content) >= config.minContentTokens) result.push({
|
|
75975
|
+
...msg,
|
|
75976
|
+
content: [{
|
|
75977
|
+
type: "text",
|
|
75978
|
+
text: config.truncatedMarker
|
|
75979
|
+
}]
|
|
75980
|
+
});
|
|
75981
|
+
else result.push(msg);
|
|
75982
|
+
i++;
|
|
75983
|
+
}
|
|
75984
|
+
return result;
|
|
75926
75985
|
}
|
|
75927
75986
|
measureEffect(messages, cutoff) {
|
|
75928
75987
|
let markerTokenCount;
|
|
@@ -259942,9 +260001,14 @@ function blunToolsForOrigin(tools, origin) {
|
|
|
259942
260001
|
if (origin.kind !== "system_trigger" && origin.kind !== "injection") return tools;
|
|
259943
260002
|
return tools.filter((tool) => !BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name));
|
|
259944
260003
|
}
|
|
259945
|
-
function
|
|
259946
|
-
if (
|
|
259947
|
-
return
|
|
260004
|
+
function blunPendingMediaSystemPrompt(systemPrompt, pendingMediaJobIds) {
|
|
260005
|
+
if (pendingMediaJobIds.length === 0) return systemPrompt;
|
|
260006
|
+
return `${systemPrompt}\n\n## Pending BLUN media\n\nThe following media jobs were already accepted and are still pending: ${pendingMediaJobIds.join(", ")}. Call GetMedia exactly once for each listed id before replying. If a job is still processing, report that status truthfully and wait for a later turn; do not claim that media tools are unavailable.`;
|
|
260007
|
+
}
|
|
260008
|
+
function blunFastConversationTools(tools, input, pendingMediaJobIds = []) {
|
|
260009
|
+
const telegramTurn = BLUN_TELEGRAM_CHANNEL_RE.test(blunExtractText(input));
|
|
260010
|
+
const mediaPending = pendingMediaJobIds.length > 0;
|
|
260011
|
+
return tools.filter((tool) => telegramTurn && BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name) || mediaPending && tool.name === "GetMedia");
|
|
259948
260012
|
}
|
|
259949
260013
|
/**
|
|
259950
260014
|
* Keep frequently used native tools resident and expose every other tool by
|
|
@@ -260281,11 +260345,15 @@ var init_turn = __esmMin((() => {
|
|
|
260281
260345
|
input,
|
|
260282
260346
|
origin
|
|
260283
260347
|
});
|
|
260284
|
-
|
|
260285
|
-
|
|
260348
|
+
if (this.activeTurn !== null) return {
|
|
260349
|
+
accepted: false,
|
|
260350
|
+
buffered: false,
|
|
260351
|
+
turnId: null
|
|
260352
|
+
};
|
|
260353
|
+
const buffered = this.agent.fullCompaction.isCompacting;
|
|
260286
260354
|
const turnId = this.launch(input, origin);
|
|
260287
260355
|
return {
|
|
260288
|
-
accepted:
|
|
260356
|
+
accepted: true,
|
|
260289
260357
|
buffered,
|
|
260290
260358
|
turnId
|
|
260291
260359
|
};
|
|
@@ -260773,7 +260841,9 @@ var init_turn = __esmMin((() => {
|
|
|
260773
260841
|
const turnHasAttachment = blunTurnHasAttachment(input);
|
|
260774
260842
|
const turnThinkingEffort = turnHasAttachment ? void 0 : selectThinkingEffortForTurn(blunExtractText(blunThinkingIntentInput(input)), origin.kind);
|
|
260775
260843
|
const fastConversation = turnThinkingEffort === "off" || turnThinkingEffort === "low";
|
|
260776
|
-
const
|
|
260844
|
+
const pendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
|
|
260845
|
+
const turnSystemPrompt = pendingMediaJobIds.length > 0 ? blunPendingMediaSystemPrompt(fastConversation ? this.agent.fastConversationSystemPrompt : this.agent.effectiveSystemPrompt, pendingMediaJobIds) : fastConversation ? this.agent.fastConversationSystemPrompt : void 0;
|
|
260846
|
+
const turnLLM = this.agent.llmForTurn(turnThinkingEffort, turnSystemPrompt);
|
|
260777
260847
|
let previousStepToolOutcome = "initial";
|
|
260778
260848
|
let currentStepHadTool = false;
|
|
260779
260849
|
let currentStepHadFailure = false;
|
|
@@ -260785,7 +260855,7 @@ var init_turn = __esmMin((() => {
|
|
|
260785
260855
|
try {
|
|
260786
260856
|
const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, this.agent.tools.loopTools), origin);
|
|
260787
260857
|
const toolSelection = fastConversation ? {
|
|
260788
|
-
tools: [...blunFastConversationTools(eligibleTools, input)],
|
|
260858
|
+
tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds)],
|
|
260789
260859
|
deferredToolCount: 0
|
|
260790
260860
|
} : turnNeedsTools ? blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames) : {
|
|
260791
260861
|
tools: [],
|
|
@@ -260849,8 +260919,9 @@ var init_turn = __esmMin((() => {
|
|
|
260849
260919
|
if (steerEfforts.some((effort) => effort === void 0)) return;
|
|
260850
260920
|
const steerInput = pendingSteers.flatMap((steer) => [...steer.input]);
|
|
260851
260921
|
const steerThinkingEffort = "low";
|
|
260852
|
-
const
|
|
260853
|
-
const
|
|
260922
|
+
const steerPendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
|
|
260923
|
+
const steerLLM = this.agent.llmForTurn(steerThinkingEffort, blunPendingMediaSystemPrompt(this.agent.fastConversationSystemPrompt, steerPendingMediaJobIds));
|
|
260924
|
+
const steerTools = [...blunFastConversationTools(eligibleTools, steerInput, steerPendingMediaJobIds)];
|
|
260854
260925
|
const steerHistory = () => blunFastConversationHistory(this.agent.context.history);
|
|
260855
260926
|
const buildSteerMessages = () => this.agent.context.project(steerHistory(), { dropOrphanResults: true });
|
|
260856
260927
|
const buildSteerMessagesStrict = () => this.agent.context.project(steerHistory(), {
|
|
@@ -311242,8 +311313,9 @@ async function assertSuccess(response, operation) {
|
|
|
311242
311313
|
} catch {}
|
|
311243
311314
|
throw new Error(`${operation} failed: HTTP ${String(response.status)}${detail ? `: ${detail}` : ""}`);
|
|
311244
311315
|
}
|
|
311245
|
-
var BlunMediaService;
|
|
311316
|
+
var createPendingMediaTracker, BlunMediaService;
|
|
311246
311317
|
var init_blun_media = __esmMin((() => {
|
|
311318
|
+
({ createPendingMediaTracker } = createRequire(import.meta.url)("./bin/pending-media-policy.cjs"));
|
|
311247
311319
|
BlunMediaService = class {
|
|
311248
311320
|
tokenProvider;
|
|
311249
311321
|
apiKey;
|
|
@@ -311251,6 +311323,7 @@ var init_blun_media = __esmMin((() => {
|
|
|
311251
311323
|
defaultHeaders;
|
|
311252
311324
|
customHeaders;
|
|
311253
311325
|
fetchImpl;
|
|
311326
|
+
pendingMedia = createPendingMediaTracker();
|
|
311254
311327
|
constructor(options) {
|
|
311255
311328
|
this.tokenProvider = options.tokenProvider;
|
|
311256
311329
|
this.apiKey = options.apiKey;
|
|
@@ -311303,28 +311376,28 @@ var init_blun_media = __esmMin((() => {
|
|
|
311303
311376
|
}
|
|
311304
311377
|
async getMedia(id, options) {
|
|
311305
311378
|
const response = await this.request(`/media/${encodeURIComponent(id)}`, { method: "GET" }, options);
|
|
311306
|
-
if (response.status === 409) return parseBlockedStatus(response, id);
|
|
311307
|
-
if (response.status === 410) return {
|
|
311379
|
+
if (response.status === 409) return this.trackMediaLookup(parseBlockedStatus(response, id));
|
|
311380
|
+
if (response.status === 410) return this.trackMediaLookup({
|
|
311308
311381
|
kind: "status",
|
|
311309
311382
|
id,
|
|
311310
311383
|
status: "expired"
|
|
311311
|
-
};
|
|
311384
|
+
});
|
|
311312
311385
|
await assertSuccess(response, "Media lookup");
|
|
311313
311386
|
const mimeType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
311314
|
-
if (mimeType === "application/json" || mimeType.endsWith("+json")) return parseStatus(await response.json(), id);
|
|
311315
|
-
if (mimeType === "text/plain") return {
|
|
311387
|
+
if (mimeType === "application/json" || mimeType.endsWith("+json")) return this.trackMediaLookup(parseStatus(await response.json(), id));
|
|
311388
|
+
if (mimeType === "text/plain") return this.trackMediaLookup({
|
|
311316
311389
|
kind: "text",
|
|
311317
311390
|
id,
|
|
311318
311391
|
mimeType,
|
|
311319
311392
|
text: await response.text()
|
|
311320
|
-
};
|
|
311393
|
+
});
|
|
311321
311394
|
if (!mimeType.startsWith("image/") && !mimeType.startsWith("audio/") && !mimeType.startsWith("video/")) throw new Error(`Media lookup returned unsupported content type ${mimeType || "(missing)"}`);
|
|
311322
|
-
return {
|
|
311395
|
+
return this.trackMediaLookup({
|
|
311323
311396
|
kind: "file",
|
|
311324
311397
|
id,
|
|
311325
311398
|
mimeType,
|
|
311326
311399
|
data: new Uint8Array(await response.arrayBuffer())
|
|
311327
|
-
};
|
|
311400
|
+
});
|
|
311328
311401
|
}
|
|
311329
311402
|
async submit(path, body, options) {
|
|
311330
311403
|
const response = await this.request(path, {
|
|
@@ -311333,7 +311406,16 @@ var init_blun_media = __esmMin((() => {
|
|
|
311333
311406
|
}, options);
|
|
311334
311407
|
if (response.status === 409) throw await blockedSubmissionError(response);
|
|
311335
311408
|
await assertSuccess(response, "Media request");
|
|
311336
|
-
|
|
311409
|
+
const job = parseJob(await response.json());
|
|
311410
|
+
this.pendingMedia.observeSubmission(job);
|
|
311411
|
+
return job;
|
|
311412
|
+
}
|
|
311413
|
+
trackMediaLookup(result) {
|
|
311414
|
+
this.pendingMedia.observeLookup(result);
|
|
311415
|
+
return result;
|
|
311416
|
+
}
|
|
311417
|
+
pendingMediaJobIds() {
|
|
311418
|
+
return this.pendingMedia.ids();
|
|
311337
311419
|
}
|
|
311338
311420
|
async request(path, init, options) {
|
|
311339
311421
|
const firstToken = await this.resolveToken(false);
|