blun-king-cli 9.1.171 → 9.1.173
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/bin/launcher-runtime.js +45 -1
- package/bin/repeated-assistant-response-policy.cjs +109 -0
- package/blun.mjs +43 -0
- package/package.json +1 -1
package/bin/launcher-runtime.js
CHANGED
|
@@ -58,6 +58,25 @@ const PROFILE = parseProfileLaunchArgs(RAW_ARGS, launcherModeFromArgv(process.ar
|
|
|
58
58
|
const ARGS = PROFILE.args;
|
|
59
59
|
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
60
60
|
const RUNTIME_READY_TIMEOUT_MS = 60_000;
|
|
61
|
+
const RUNNING_UPDATE_RECHECK_MS = 60_000;
|
|
62
|
+
const RUNNING_UPDATE_RECHECK_JITTER_MS = 30_000;
|
|
63
|
+
|
|
64
|
+
function runningUpdateRecheckDelay(randomValue = Math.random()) {
|
|
65
|
+
const normalized = Number.isFinite(randomValue)
|
|
66
|
+
? Math.min(Math.max(randomValue, 0), 0.999999999)
|
|
67
|
+
: 0;
|
|
68
|
+
return RUNNING_UPDATE_RECHECK_MS
|
|
69
|
+
+ Math.floor(normalized * RUNNING_UPDATE_RECHECK_JITTER_MS);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function scheduleRunningUpdateRecheck(callback, options = {}) {
|
|
73
|
+
const timer = (options.setTimeoutImpl || setTimeout)(
|
|
74
|
+
callback,
|
|
75
|
+
options.delayMs ?? runningUpdateRecheckDelay((options.randomImpl || Math.random)()),
|
|
76
|
+
);
|
|
77
|
+
timer.unref?.();
|
|
78
|
+
return timer;
|
|
79
|
+
}
|
|
61
80
|
|
|
62
81
|
function normalizeWindowsPath(value) {
|
|
63
82
|
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
@@ -232,9 +251,23 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
232
251
|
let handoffMode;
|
|
233
252
|
let updateStarted = false;
|
|
234
253
|
let runtimeReady = false;
|
|
254
|
+
let runningUpdatePollTimer;
|
|
235
255
|
let runningUpdateMode = readRunningUpdateMode(env.BLUN_HOME);
|
|
236
256
|
const automaticMode = () => runningUpdateMode === RUNNING_UPDATE_MODES.RESUME
|
|
237
257
|
|| runningUpdateMode === RUNNING_UPDATE_MODES.NEW;
|
|
258
|
+
const clearRunningUpdatePoll = () => {
|
|
259
|
+
if (runningUpdatePollTimer === undefined) return;
|
|
260
|
+
(options.clearTimeoutImpl || clearTimeout)(runningUpdatePollTimer);
|
|
261
|
+
runningUpdatePollTimer = undefined;
|
|
262
|
+
};
|
|
263
|
+
const scheduleNextPreparation = (child) => {
|
|
264
|
+
clearRunningUpdatePoll();
|
|
265
|
+
if (preparedTarget !== undefined || updateStarted || !automaticMode()) return;
|
|
266
|
+
runningUpdatePollTimer = (options.scheduleRunningUpdateRecheck || scheduleRunningUpdateRecheck)(() => {
|
|
267
|
+
runningUpdatePollTimer = undefined;
|
|
268
|
+
startPreparation(child);
|
|
269
|
+
});
|
|
270
|
+
};
|
|
238
271
|
const announcePrepared = (child) => {
|
|
239
272
|
if (preparedTarget === undefined || !automaticMode() || child.connected !== true) return;
|
|
240
273
|
child.send({
|
|
@@ -249,6 +282,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
249
282
|
const sharedHome = env.BLUN_SHARED_HOME;
|
|
250
283
|
if (typeof sharedHome !== 'string' || sharedHome.length === 0) {
|
|
251
284
|
updateStarted = false;
|
|
285
|
+
scheduleNextPreparation(child);
|
|
252
286
|
return;
|
|
253
287
|
}
|
|
254
288
|
Promise.resolve((options.prepareRunningUpdate || prepareRunningUpdate)({
|
|
@@ -258,16 +292,19 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
258
292
|
})).then((target) => {
|
|
259
293
|
if (target === null) {
|
|
260
294
|
updateStarted = false;
|
|
295
|
+
scheduleNextPreparation(child);
|
|
261
296
|
return;
|
|
262
297
|
}
|
|
263
298
|
preparedTarget = target;
|
|
299
|
+
clearRunningUpdatePoll();
|
|
264
300
|
announcePrepared(child);
|
|
265
301
|
}).catch((error) => {
|
|
266
302
|
updateStarted = false;
|
|
267
303
|
options.onRunningUpdateError?.(error);
|
|
304
|
+
scheduleNextPreparation(child);
|
|
268
305
|
});
|
|
269
306
|
};
|
|
270
|
-
const core = spawnProtectedCore(args, env, cwd, {
|
|
307
|
+
const core = (options.spawnProtectedCore || spawnProtectedCore)(args, env, cwd, {
|
|
271
308
|
packageRoot,
|
|
272
309
|
onMessage(message, child) {
|
|
273
310
|
if (message?.type === RUNTIME_READY_MESSAGE) {
|
|
@@ -278,6 +315,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
278
315
|
runningUpdateMode = normalizeRunningUpdateMode(message.mode);
|
|
279
316
|
handoffSessionId = undefined;
|
|
280
317
|
handoffMode = undefined;
|
|
318
|
+
clearRunningUpdatePoll();
|
|
281
319
|
if (automaticMode()) {
|
|
282
320
|
if (preparedTarget !== undefined) announcePrepared(child);
|
|
283
321
|
else if (runtimeReady) {
|
|
@@ -299,6 +337,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
299
337
|
const loaded = await core.loaded;
|
|
300
338
|
await releaseNotice();
|
|
301
339
|
const result = await core.completed;
|
|
340
|
+
clearRunningUpdatePoll();
|
|
302
341
|
if (result.error) throw result.error;
|
|
303
342
|
if (result.code === RUNNING_UPDATE_HANDOFF_EXIT_CODE
|
|
304
343
|
&& preparedTarget !== undefined
|
|
@@ -656,9 +695,14 @@ async function runLauncher(options = {}) {
|
|
|
656
695
|
}
|
|
657
696
|
|
|
658
697
|
module.exports = {
|
|
698
|
+
RUNNING_UPDATE_RECHECK_JITTER_MS,
|
|
699
|
+
RUNNING_UPDATE_RECHECK_MS,
|
|
659
700
|
createManagedNodeEnvironment,
|
|
660
701
|
resolveLauncherPrivatePaths,
|
|
702
|
+
runningUpdateRecheckDelay,
|
|
661
703
|
runLauncher,
|
|
704
|
+
scheduleRunningUpdateRecheck,
|
|
662
705
|
shouldDetachProtectedCore,
|
|
663
706
|
spawnManagedLauncher,
|
|
707
|
+
superviseProtectedCore,
|
|
664
708
|
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MIN_RESPONSE_CHARS = 160;
|
|
4
|
+
const MAX_RECENT_ASSISTANT_RESPONSES = 8;
|
|
5
|
+
const REQUIRED_MATCHING_RESPONSES = 3;
|
|
6
|
+
const RESPONSE_SIMILARITY_THRESHOLD = 0.78;
|
|
7
|
+
|
|
8
|
+
function isUserPrompt(message) {
|
|
9
|
+
if (message?.role !== 'user') return false;
|
|
10
|
+
const kind = message.origin?.kind;
|
|
11
|
+
return kind !== 'injection' && kind !== 'compaction_summary' && kind !== 'system_trigger';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function assistantResponseText(message) {
|
|
15
|
+
if (
|
|
16
|
+
message?.role !== 'assistant'
|
|
17
|
+
|| (message.toolCalls?.length ?? 0) > 0
|
|
18
|
+
|| message.origin?.kind === 'injection'
|
|
19
|
+
|| !Array.isArray(message.content)
|
|
20
|
+
) return '';
|
|
21
|
+
|
|
22
|
+
const text = message.content
|
|
23
|
+
.filter((part) => part?.type === 'text' && typeof part.text === 'string')
|
|
24
|
+
.map((part) => part.text)
|
|
25
|
+
.join('\n')
|
|
26
|
+
.trim();
|
|
27
|
+
return text.length >= MIN_RESPONSE_CHARS ? text : '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizedResponseTokens(text) {
|
|
31
|
+
return String(text)
|
|
32
|
+
.normalize('NFKC')
|
|
33
|
+
.toLocaleLowerCase('en-US')
|
|
34
|
+
.match(/[\p{L}\p{N}_]+/gu)
|
|
35
|
+
?.map((token) => /\p{N}/u.test(token) ? '#' : token) ?? [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function tokenBigrams(tokens) {
|
|
39
|
+
const result = new Set();
|
|
40
|
+
for (let index = 0; index + 1 < tokens.length; index += 1) {
|
|
41
|
+
result.add(`${tokens[index]}\u0000${tokens[index + 1]}`);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function responseSimilarity(left, right) {
|
|
47
|
+
const leftBigrams = tokenBigrams(normalizedResponseTokens(left));
|
|
48
|
+
const rightBigrams = tokenBigrams(normalizedResponseTokens(right));
|
|
49
|
+
if (leftBigrams.size < 8 || rightBigrams.size < 8) return 0;
|
|
50
|
+
|
|
51
|
+
let intersection = 0;
|
|
52
|
+
for (const bigram of leftBigrams) {
|
|
53
|
+
if (rightBigrams.has(bigram)) intersection += 1;
|
|
54
|
+
}
|
|
55
|
+
return (2 * intersection) / (leftBigrams.size + rightBigrams.size);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hasUserPromptBetween(history, leftIndex, rightIndex) {
|
|
59
|
+
for (let index = leftIndex + 1; index < rightIndex; index += 1) {
|
|
60
|
+
if (isUserPrompt(history[index])) return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function detectRepeatedAssistantResponse(history) {
|
|
66
|
+
if (!Array.isArray(history) || history.length === 0) return null;
|
|
67
|
+
|
|
68
|
+
let latestUserIndex = -1;
|
|
69
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
70
|
+
if (isUserPrompt(history[index])) {
|
|
71
|
+
latestUserIndex = index;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (latestUserIndex < 0) return null;
|
|
76
|
+
|
|
77
|
+
const responses = [];
|
|
78
|
+
for (let index = latestUserIndex - 1; index >= 0 && responses.length < MAX_RECENT_ASSISTANT_RESPONSES; index -= 1) {
|
|
79
|
+
const text = assistantResponseText(history[index]);
|
|
80
|
+
if (text) responses.push({ index, text });
|
|
81
|
+
}
|
|
82
|
+
if (responses.length < REQUIRED_MATCHING_RESPONSES) return null;
|
|
83
|
+
|
|
84
|
+
const latest = responses[0];
|
|
85
|
+
const matches = [latest];
|
|
86
|
+
for (const candidate of responses.slice(1)) {
|
|
87
|
+
if (!hasUserPromptBetween(history, candidate.index, latest.index)) continue;
|
|
88
|
+
const similarity = responseSimilarity(latest.text, candidate.text);
|
|
89
|
+
if (similarity >= RESPONSE_SIMILARITY_THRESHOLD) {
|
|
90
|
+
matches.push({ ...candidate, similarity });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (matches.length < REQUIRED_MATCHING_RESPONSES) return null;
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
count: matches.length,
|
|
97
|
+
minimumSimilarity: Math.min(...matches.slice(1).map((match) => match.similarity)),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
MAX_RECENT_ASSISTANT_RESPONSES,
|
|
103
|
+
MIN_RESPONSE_CHARS,
|
|
104
|
+
REQUIRED_MATCHING_RESPONSES,
|
|
105
|
+
RESPONSE_SIMILARITY_THRESHOLD,
|
|
106
|
+
assistantResponseText,
|
|
107
|
+
detectRepeatedAssistantResponse,
|
|
108
|
+
responseSimilarity,
|
|
109
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -232089,6 +232089,47 @@ var init_tool_awareness = __esmMin((() => {
|
|
|
232089
232089
|
};
|
|
232090
232090
|
}));
|
|
232091
232091
|
//#endregion
|
|
232092
|
+
//#region ../../packages/agent-core/src/agent/injection/repeated-assistant-response.ts
|
|
232093
|
+
function isRepeatedAssistantResponseReminder(message) {
|
|
232094
|
+
return message.origin?.kind === "injection" && message.origin.variant === "repeated_assistant_response";
|
|
232095
|
+
}
|
|
232096
|
+
function buildRepeatedAssistantResponseReminder() {
|
|
232097
|
+
return [
|
|
232098
|
+
"<response-loop-check>",
|
|
232099
|
+
"Your latest text-only answer substantially repeats at least two earlier answers despite newer user messages between them.",
|
|
232100
|
+
"Do not repeat the earlier question, list, or waiting state again.",
|
|
232101
|
+
"Re-read the latest non-injection user message and every newer correction, then take a different concrete action that advances that request.",
|
|
232102
|
+
"If action is genuinely blocked, report the exact current blocker and its evidence once instead of returning to the old answer.",
|
|
232103
|
+
"</response-loop-check>"
|
|
232104
|
+
].join("\n");
|
|
232105
|
+
}
|
|
232106
|
+
var RepeatedAssistantResponseInjector, detectRepeatedAssistantResponse;
|
|
232107
|
+
var init_repeated_assistant_response = __esmMin((() => {
|
|
232108
|
+
init_injector();
|
|
232109
|
+
({ detectRepeatedAssistantResponse } = createRequire(import.meta.url)("./bin/repeated-assistant-response-policy.cjs"));
|
|
232110
|
+
RepeatedAssistantResponseInjector = class extends DynamicInjector {
|
|
232111
|
+
injectionVariant = "repeated_assistant_response";
|
|
232112
|
+
async inject() {
|
|
232113
|
+
const detection = detectRepeatedAssistantResponse(this.agent.context.history);
|
|
232114
|
+
const existing = this.agent.context.history.filter(isRepeatedAssistantResponseReminder);
|
|
232115
|
+
if (detection === null) {
|
|
232116
|
+
if (existing.length > 0) this.agent.context.removeSystemRemindersMatching(isRepeatedAssistantResponseReminder);
|
|
232117
|
+
return;
|
|
232118
|
+
}
|
|
232119
|
+
const injection = buildRepeatedAssistantResponseReminder();
|
|
232120
|
+
const expected = `<system-reminder>\n${injection}\n</system-reminder>`;
|
|
232121
|
+
if (existing.length === 1 && reminderText(existing[0]) === expected) return;
|
|
232122
|
+
this.agent.context.removeSystemRemindersMatching(isRepeatedAssistantResponseReminder);
|
|
232123
|
+
this.injectedAt = this.agent.context.history.length;
|
|
232124
|
+
this.lastInjection = injection;
|
|
232125
|
+
this.agent.context.appendSystemReminder(injection, {
|
|
232126
|
+
kind: "injection",
|
|
232127
|
+
variant: this.injectionVariant
|
|
232128
|
+
});
|
|
232129
|
+
}
|
|
232130
|
+
};
|
|
232131
|
+
}));
|
|
232132
|
+
//#endregion
|
|
232092
232133
|
//#region ../../packages/agent-core/src/agent/injection/mission-contract-evidence.js
|
|
232093
232134
|
function createEvidenceApi(dependencies) {
|
|
232094
232135
|
"use strict";
|
|
@@ -232887,6 +232928,7 @@ var init_manager$2 = __esmMin((() => {
|
|
|
232887
232928
|
init_personal_memory_recall();
|
|
232888
232929
|
init_plugin_session_start();
|
|
232889
232930
|
init_plan_mode();
|
|
232931
|
+
init_repeated_assistant_response();
|
|
232890
232932
|
init_tool_awareness();
|
|
232891
232933
|
ACTIVE_BACKGROUND_TASK_GUIDANCE = "The conversation was compacted, so the earlier messages that started these background tasks are gone — but the tasks are still running from before. Do not start duplicates. Use TaskOutput to fetch a task’s result, TaskList to list them, TaskUpdate to send additional instructions to a running agent task, and TaskStop to cancel one.";
|
|
232892
232934
|
InjectionManager = class {
|
|
@@ -232902,6 +232944,7 @@ var init_manager$2 = __esmMin((() => {
|
|
|
232902
232944
|
new ErrorMemoryInjector(agent),
|
|
232903
232945
|
new ToolAwarenessInjector(agent),
|
|
232904
232946
|
new MistakeMdInjector(agent),
|
|
232947
|
+
new RepeatedAssistantResponseInjector(agent),
|
|
232905
232948
|
new ActionStyleInjector(agent),
|
|
232906
232949
|
new PlanModeInjector(agent),
|
|
232907
232950
|
new PermissionModeInjector(agent)
|