blun-king-cli 9.1.172 → 9.1.174
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.
|
@@ -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)
|