blun-king-cli 9.1.569 → 9.1.571
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/CHANGELOG.md +55 -0
- package/bin/core-bootstrap.js +2 -1
- package/bin/telegram-session-queue-runtime.mjs +306 -0
- package/blun.mjs +558 -245
- package/package.json +3 -2
- package/standard-skills/research-evidence/scripts/evidence-collection.cjs +1 -1
- package/telegram-plugin/dist/bridge.mjs +11 -36
- package/telegram-plugin/dist/noise.mjs +42 -29
package/blun.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// BLUN_BUILD_INPUT_SHA256:058cccc03f986fac95f89d198bb56bfa928b0c5117340651158d2f731509129c
|
|
3
|
+
import { SessionQueueDelivery as BlunSessionQueueDelivery, captureSessionQueueBoundary } from "./bin/telegram-session-queue-runtime.mjs";
|
|
3
4
|
import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
4
5
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
5
6
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
@@ -21351,7 +21352,15 @@ var init_types$14 = __esmMin((() => {}));
|
|
|
21351
21352
|
//#region ../../packages/agent-core/src/flags/registry.ts
|
|
21352
21353
|
var FLAG_DEFINITIONS;
|
|
21353
21354
|
var init_registry$1 = __esmMin((() => {
|
|
21354
|
-
FLAG_DEFINITIONS = [
|
|
21355
|
+
FLAG_DEFINITIONS = [
|
|
21356
|
+
{
|
|
21357
|
+
id: 'agent_spine_timeline',
|
|
21358
|
+
title: 'AgentSpine session transport',
|
|
21359
|
+
description: 'Pass the current native record source to the enabled AgentSpine plugin.',
|
|
21360
|
+
env: 'BLUN_EXPERIMENTAL_AGENT_SPINE_TIMELINE',
|
|
21361
|
+
default: false,
|
|
21362
|
+
surface: 'core',
|
|
21363
|
+
},{
|
|
21355
21364
|
id: "vision_reader",
|
|
21356
21365
|
title: "Vision reader",
|
|
21357
21366
|
description: "Enrich user images with observations from a configured internal service.",
|
|
@@ -231318,10 +231327,9 @@ async function runHook(command, input, options) {
|
|
|
231318
231327
|
cwd: options.cwd,
|
|
231319
231328
|
stdio: "pipe",
|
|
231320
231329
|
detached: process.platform !== "win32",
|
|
231321
|
-
env: options.env
|
|
231322
|
-
|
|
231323
|
-
|
|
231324
|
-
} : void 0
|
|
231330
|
+
env: options.env || options.runtimeEnv
|
|
231331
|
+
? overlayRuntimeEnv({ ...process.env, ...options.env }, options.runtimeEnv)
|
|
231332
|
+
: undefined
|
|
231325
231333
|
});
|
|
231326
231334
|
} catch (error) {
|
|
231327
231335
|
return allowResult({ stderr: errorMessage$11(error) });
|
|
@@ -231659,28 +231667,44 @@ async prepareToolUse(args = {}) {
|
|
|
231659
231667
|
await Promise.allSettled([...this.pendingTriggers]);
|
|
231660
231668
|
}
|
|
231661
231669
|
async triggerInner(event, args) {
|
|
231662
|
-
|
|
231663
|
-
|
|
231664
|
-
|
|
231665
|
-
|
|
231666
|
-
|
|
231667
|
-
|
|
231668
|
-
|
|
231669
|
-
|
|
231670
|
-
|
|
231671
|
-
|
|
231672
|
-
|
|
231673
|
-
|
|
231674
|
-
|
|
231675
|
-
|
|
231676
|
-
|
|
231677
|
-
|
|
231678
|
-
|
|
231679
|
-
|
|
231680
|
-
|
|
231681
|
-
|
|
231682
|
-
|
|
231683
|
-
|
|
231670
|
+
const matcherValue = matcherValueText(args.matcherValue);
|
|
231671
|
+
const inputData = toHookInputData({
|
|
231672
|
+
hookEventName: event,
|
|
231673
|
+
sessionId: this.options.sessionId ?? '',
|
|
231674
|
+
cwd: this.options.cwd ?? '',
|
|
231675
|
+
...args.inputData,
|
|
231676
|
+
});
|
|
231677
|
+
const matched = this.matchingHooks(event, matcherValue);
|
|
231678
|
+
if (matched.length === 0)
|
|
231679
|
+
return [];
|
|
231680
|
+
if (event === 'UserPromptSubmit'
|
|
231681
|
+
&& !Object.hasOwn(inputData, 'event_id') && !Object.hasOwn(inputData, 'hook_event_id')) {
|
|
231682
|
+
// One logical delivery, shared by every hook; explicit IDs preserve redelivery identity.
|
|
231683
|
+
inputData['event_id'] = releasePromptUUID();
|
|
231684
|
+
}
|
|
231685
|
+
this.emitTriggered(event, matcherValue, matched.length);
|
|
231686
|
+
const startedAt = Date.now();
|
|
231687
|
+
const results = await Promise.all(matched.map(async (hook) => {
|
|
231688
|
+
let runtimeEnv;
|
|
231689
|
+
try {
|
|
231690
|
+
runtimeEnv = this.options.runtimeEnv?.(hook);
|
|
231691
|
+
}
|
|
231692
|
+
catch {
|
|
231693
|
+
// A resolver error may contain private values. Other hooks still run.
|
|
231694
|
+
return { action: 'allow', stderr: 'Hook runtime environment is unavailable.' };
|
|
231695
|
+
}
|
|
231696
|
+
return runHook(hook.command, inputData, {
|
|
231697
|
+
timeout: hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS,
|
|
231698
|
+
cwd: hook.cwd ?? (this.options.cwd === '' ? undefined : this.options.cwd),
|
|
231699
|
+
env: hook.env,
|
|
231700
|
+
runtimeEnv,
|
|
231701
|
+
signal: args.signal,
|
|
231702
|
+
});
|
|
231703
|
+
}));
|
|
231704
|
+
const { action, reason } = aggregateResults(event, results);
|
|
231705
|
+
this.emitResolved(event, matcherValue, action, reason, Date.now() - startedAt);
|
|
231706
|
+
return results;
|
|
231707
|
+
}
|
|
231684
231708
|
matchingHooks(event, matcherValue) {
|
|
231685
231709
|
const seen = /* @__PURE__ */ new Set();
|
|
231686
231710
|
const matched = [];
|
|
@@ -236096,6 +236120,13 @@ var init_records = __esmMin((() => {
|
|
|
236096
236120
|
persistence;
|
|
236097
236121
|
_restoring = null;
|
|
236098
236122
|
metadataInitialized = false;
|
|
236123
|
+
persistedProtocolVersion;
|
|
236124
|
+
get fileSource() {
|
|
236125
|
+
if (!(this.persistence instanceof FileSystemAgentRecordPersistence)
|
|
236126
|
+
|| this.persistedProtocolVersion === undefined)
|
|
236127
|
+
return undefined;
|
|
236128
|
+
return { path: this.persistence.filePath, protocolVersion: this.persistedProtocolVersion };
|
|
236129
|
+
}
|
|
236099
236130
|
resumeCheckpointRestored = false;
|
|
236100
236131
|
messageTimes = new WeakMap();
|
|
236101
236132
|
constructor(agent, persistence) {
|
|
@@ -236119,8 +236150,15 @@ var init_records = __esmMin((() => {
|
|
|
236119
236150
|
created_at: Date.now()
|
|
236120
236151
|
});
|
|
236121
236152
|
this.metadataInitialized = true;
|
|
236122
|
-
|
|
236123
|
-
|
|
236153
|
+
|
|
236154
|
+
this.persistedProtocolVersion = "1.4";
|
|
236155
|
+
}
|
|
236156
|
+
if (stamped.type === 'metadata') {
|
|
236157
|
+
if (!this.metadataInitialized) {
|
|
236158
|
+
this.persistedProtocolVersion = stamped.protocol_version;
|
|
236159
|
+
}
|
|
236160
|
+
this.metadataInitialized = true;
|
|
236161
|
+
}
|
|
236124
236162
|
this.persistence?.append(stamped);
|
|
236125
236163
|
}
|
|
236126
236164
|
restore(record) {
|
|
@@ -236134,7 +236172,8 @@ var init_records = __esmMin((() => {
|
|
|
236134
236172
|
}
|
|
236135
236173
|
}
|
|
236136
236174
|
async replay(options = {}) {
|
|
236137
|
-
|
|
236175
|
+
this.persistedProtocolVersion = undefined;
|
|
236176
|
+
if (!this.persistence) throw new Error("No persistence provided for AgentRecords");
|
|
236138
236177
|
this.resumeCheckpointRestored = false;
|
|
236139
236178
|
const rewriteMigratedRecords = options.rewriteMigratedRecords ?? true;
|
|
236140
236179
|
if (rewriteMigratedRecords && await this.tryReplayResumeCheckpoint()) return {};
|
|
@@ -236152,6 +236191,7 @@ var init_records = __esmMin((() => {
|
|
|
236152
236191
|
hasMetadata = true;
|
|
236153
236192
|
this.metadataInitialized = true;
|
|
236154
236193
|
const readVersion = record.protocol_version;
|
|
236194
|
+
this.persistedProtocolVersion = readVersion;
|
|
236155
236195
|
if (isNewerWireVersion(readVersion)) {
|
|
236156
236196
|
warning = `Session wire protocol version ${readVersion} is newer than the current version 1.4. Records will be replayed without migration.`;
|
|
236157
236197
|
shouldRewrite = false;
|
|
@@ -236174,6 +236214,7 @@ var init_records = __esmMin((() => {
|
|
|
236174
236214
|
if (completed && shouldRewrite && replayedRecords !== void 0) {
|
|
236175
236215
|
this.persistence.rewrite(replayedRecords);
|
|
236176
236216
|
await this.persistence.flush();
|
|
236217
|
+
this.persistedProtocolVersion = "1.4";
|
|
236177
236218
|
}
|
|
236178
236219
|
if (completed && this.persistence instanceof FileSystemAgentRecordPersistence) this.persistence.persistedRecordCount = replayedRecordCount;
|
|
236179
236220
|
if (completed && this.agent.blobStore !== void 0) for (const msg of this.agent.context.history) await this.agent.blobStore.rehydrateParts(msg.content);
|
|
@@ -236184,8 +236225,17 @@ var init_records = __esmMin((() => {
|
|
|
236184
236225
|
if (this.agent.replayBuilder.options.range !== void 0) return false;
|
|
236185
236226
|
const checkpoint = await loadResumeCheckpoint({ wirePath: this.persistence.filePath });
|
|
236186
236227
|
if (!checkpoint.ok) return false;
|
|
236228
|
+
let sourceProtocol;
|
|
236229
|
+
for await (const metadata of this.persistence.read()) {
|
|
236230
|
+
if (metadata.type !== 'metadata' || metadata.protocol_version !== "1.4") return false;
|
|
236231
|
+
sourceProtocol = metadata.protocol_version;
|
|
236232
|
+
break;
|
|
236233
|
+
}
|
|
236234
|
+
if (sourceProtocol === undefined) return false;
|
|
236235
|
+
|
|
236187
236236
|
if (!restoreAgentResumeSnapshot(this.agent, checkpoint.snapshot)) return false;
|
|
236188
236237
|
this.metadataInitialized = true;
|
|
236238
|
+
this.persistedProtocolVersion = sourceProtocol;
|
|
236189
236239
|
let tailRecordCount = 0;
|
|
236190
236240
|
for await (const record of this.persistence.readFrom(checkpoint.offset, checkpoint.recordCount)) {
|
|
236191
236241
|
this.restore(record);
|
|
@@ -265678,34 +265728,52 @@ var init_web_search = __esmMin((() => {
|
|
|
265678
265728
|
execute: (ctx) => this.execution(args, ctx)
|
|
265679
265729
|
};
|
|
265680
265730
|
}
|
|
265681
|
-
async execution(args, { toolCallId }) {
|
|
265682
|
-
|
|
265683
|
-
|
|
265684
|
-
|
|
265685
|
-
|
|
265686
|
-
|
|
265687
|
-
|
|
265688
|
-
|
|
265689
|
-
|
|
265690
|
-
|
|
265691
|
-
|
|
265692
|
-
|
|
265693
|
-
|
|
265694
|
-
|
|
265695
|
-
|
|
265696
|
-
|
|
265697
|
-
|
|
265698
|
-
|
|
265699
|
-
|
|
265700
|
-
|
|
265701
|
-
|
|
265702
|
-
|
|
265703
|
-
|
|
265704
|
-
|
|
265705
|
-
|
|
265706
|
-
|
|
265707
|
-
|
|
265708
|
-
|
|
265731
|
+
async execution(args, { toolCallId, }) {
|
|
265732
|
+
try {
|
|
265733
|
+
let failedSources = 0;
|
|
265734
|
+
const opts = { toolCallId, onNotice: notice => {
|
|
265735
|
+
if (notice.code === 'partial-sources' && Number.isInteger(notice.failedSources)
|
|
265736
|
+
&& notice.failedSources > 0 && notice.failedSources <= 128) {
|
|
265737
|
+
failedSources = Math.max(failedSources, notice.failedSources);
|
|
265738
|
+
}
|
|
265739
|
+
} };
|
|
265740
|
+
const results = await this.provider.search(args.query, opts);
|
|
265741
|
+
const builder = new ToolResultBuilder({ maxLineLength: null });
|
|
265742
|
+
if (failedSources > 0) {
|
|
265743
|
+
if (results.length === 0)
|
|
265744
|
+
throw new Error('Search sources failed without usable results.');
|
|
265745
|
+
builder.write(`Some search sources failed (${failedSources}). Results below are partial, not a complete search.\n\n`);
|
|
265746
|
+
}
|
|
265747
|
+
if (results.length === 0) {
|
|
265748
|
+
builder.write('No search results found.');
|
|
265749
|
+
return builder.ok();
|
|
265750
|
+
}
|
|
265751
|
+
let first = true;
|
|
265752
|
+
for (const result of results) {
|
|
265753
|
+
if (!first)
|
|
265754
|
+
builder.write('---\n\n');
|
|
265755
|
+
first = false;
|
|
265756
|
+
builder.write(`Title: ${result.title}\n`);
|
|
265757
|
+
if (result.siteName)
|
|
265758
|
+
builder.write(`Site: ${result.siteName}\n`);
|
|
265759
|
+
if (result.date)
|
|
265760
|
+
builder.write(`Date: ${result.date}\n`);
|
|
265761
|
+
builder.write(`URL: ${result.url}\n`);
|
|
265762
|
+
builder.write(`Snippet: ${result.snippet}\n\n`);
|
|
265763
|
+
}
|
|
265764
|
+
// Keep the citation reminder next to the data (not just in the static tool
|
|
265765
|
+
// description), so it is present on every search. Cite the page actually
|
|
265766
|
+
// relied on — after a FetchURL follow-up, that is the fetched page.
|
|
265767
|
+
builder.write('When you rely on a result in your answer, cite it inline as a markdown link, e.g. [title](url).');
|
|
265768
|
+
return builder.ok();
|
|
265769
|
+
}
|
|
265770
|
+
catch (error) {
|
|
265771
|
+
return {
|
|
265772
|
+
isError: true,
|
|
265773
|
+
output: classifySearchError(error),
|
|
265774
|
+
};
|
|
265775
|
+
}
|
|
265776
|
+
}
|
|
265709
265777
|
};
|
|
265710
265778
|
}));
|
|
265711
265779
|
//#endregion
|
|
@@ -298151,13 +298219,11 @@ function resolveStdioCwd(configCwd, defaultCwd) {
|
|
|
298151
298219
|
if (defaultCwd !== void 0 && !isAbsolute$2(configCwd)) return resolve$2(defaultCwd, configCwd);
|
|
298152
298220
|
return configCwd;
|
|
298153
298221
|
}
|
|
298154
|
-
function mergeStdioEnv(configEnv, parentEnv = process.env) {
|
|
298155
|
-
|
|
298156
|
-
|
|
298157
|
-
|
|
298158
|
-
|
|
298159
|
-
reconcileChildNoProxy(merged, configEnv);
|
|
298160
|
-
return merged;
|
|
298222
|
+
function mergeStdioEnv(configEnv, parentEnv = process.env, runtimeEnv) {
|
|
298223
|
+
const merged = overlayRuntimeEnv({ ...parentEnv, ...configEnv }, runtimeEnv);
|
|
298224
|
+
Object.assign(merged, proxyEnvForChild(merged));
|
|
298225
|
+
reconcileChildNoProxy(merged, overlayRuntimeEnv(configEnv ?? {}, runtimeEnv));
|
|
298226
|
+
return merged;
|
|
298161
298227
|
}
|
|
298162
298228
|
var STDERR_BUFFER_CAPACITY, StdioMcpClient, BoundedTail;
|
|
298163
298229
|
var init_client_stdio = __esmMin((() => {
|
|
@@ -298187,7 +298253,7 @@ var init_client_stdio = __esmMin((() => {
|
|
|
298187
298253
|
this.transport = new StdioClientTransport({
|
|
298188
298254
|
command: config.command,
|
|
298189
298255
|
args: config.args,
|
|
298190
|
-
env: mergeStdioEnv(config.env, config.inheritEnv === false ? {} : process.env),
|
|
298256
|
+
env: mergeStdioEnv(config.env, config.inheritEnv === false ? {} : process.env, options.runtimeEnv),
|
|
298191
298257
|
cwd: resolveStdioCwd(config.cwd, options.defaultCwd),
|
|
298192
298258
|
stderr: "pipe"
|
|
298193
298259
|
});
|
|
@@ -298689,22 +298755,32 @@ var init_connection_manager = __esmMin((() => {
|
|
|
298689
298755
|
return entry.attemptId;
|
|
298690
298756
|
}
|
|
298691
298757
|
createClient(config, name) {
|
|
298692
|
-
|
|
298693
|
-
|
|
298694
|
-
|
|
298695
|
-
|
|
298696
|
-
|
|
298697
|
-
|
|
298698
|
-
|
|
298699
|
-
|
|
298700
|
-
|
|
298701
|
-
|
|
298702
|
-
|
|
298703
|
-
|
|
298704
|
-
|
|
298705
|
-
|
|
298706
|
-
|
|
298707
|
-
|
|
298758
|
+
const toolCallTimeoutMs = config.toolTimeoutMs;
|
|
298759
|
+
if (config.transport === 'stdio') {
|
|
298760
|
+
let runtimeEnv;
|
|
298761
|
+
try {
|
|
298762
|
+
runtimeEnv = this.options.stdioRuntimeEnv?.(name, config);
|
|
298763
|
+
}
|
|
298764
|
+
catch {
|
|
298765
|
+
throw new Error('MCP runtime environment is unavailable.');
|
|
298766
|
+
}
|
|
298767
|
+
return new StdioMcpClient(config, {
|
|
298768
|
+
toolCallTimeoutMs, defaultCwd: this.options.stdioCwd, runtimeEnv,
|
|
298769
|
+
});
|
|
298770
|
+
}
|
|
298771
|
+
if (config.transport === 'sse') {
|
|
298772
|
+
return new SseMcpClient(config, {
|
|
298773
|
+
toolCallTimeoutMs,
|
|
298774
|
+
envLookup: this.options.envLookup,
|
|
298775
|
+
oauthProvider: this.resolveOAuthProvider(config, name),
|
|
298776
|
+
});
|
|
298777
|
+
}
|
|
298778
|
+
return new HttpMcpClient(config, {
|
|
298779
|
+
toolCallTimeoutMs,
|
|
298780
|
+
envLookup: this.options.envLookup,
|
|
298781
|
+
oauthProvider: this.resolveOAuthProvider(config, name),
|
|
298782
|
+
});
|
|
298783
|
+
}
|
|
298708
298784
|
resolveOAuthProvider(config, name) {
|
|
298709
298785
|
const oauthService = this.oauthService;
|
|
298710
298786
|
if (oauthService === void 0) return void 0;
|
|
@@ -299417,6 +299493,43 @@ var init_lsp = __esmMin((() => {
|
|
|
299417
299493
|
init_manager$1();
|
|
299418
299494
|
}));
|
|
299419
299495
|
//#endregion
|
|
299496
|
+
|
|
299497
|
+
import { randomBytes as releaseRecordRandomBytes } from "node:crypto";
|
|
299498
|
+
const EMPTY_RUNTIME = Object.freeze({
|
|
299499
|
+
AGENTSPINE_KING_TIMELINE_SOURCE: '',
|
|
299500
|
+
AGENTSPINE_KING_WIRE_PROTOCOL_VERSION: '',
|
|
299501
|
+
AGENTSPINE_TIMELINE_TRANSPORT_SESSION_ID: '',
|
|
299502
|
+
AGENTSPINE_TIMELINE_SESSION_CAPABILITY: '',
|
|
299503
|
+
});
|
|
299504
|
+
class AgentSpineSessionRuntime {
|
|
299505
|
+
#env = EMPTY_RUNTIME;
|
|
299506
|
+
#hooks;
|
|
299507
|
+
#servers;
|
|
299508
|
+
constructor(recipients) {
|
|
299509
|
+
this.#hooks = new Set(recipients?.hooks);
|
|
299510
|
+
this.#servers = new Map(Object.entries(recipients?.mcpServers ?? {}));
|
|
299511
|
+
}
|
|
299512
|
+
get hasRecipients() {
|
|
299513
|
+
return this.#hooks.size > 0 || this.#servers.size > 0;
|
|
299514
|
+
}
|
|
299515
|
+
bind(sessionId, source) {
|
|
299516
|
+
this.#env = EMPTY_RUNTIME;
|
|
299517
|
+
if (!sessionId || !/^[A-Za-z0-9][A-Za-z0-9:_.@/-]{0,127}$/.test(sessionId) || !source)
|
|
299518
|
+
return;
|
|
299519
|
+
this.#env = Object.freeze({
|
|
299520
|
+
AGENTSPINE_KING_TIMELINE_SOURCE: source.path,
|
|
299521
|
+
AGENTSPINE_KING_WIRE_PROTOCOL_VERSION: source.protocolVersion,
|
|
299522
|
+
AGENTSPINE_TIMELINE_TRANSPORT_SESSION_ID: sessionId,
|
|
299523
|
+
AGENTSPINE_TIMELINE_SESSION_CAPABILITY: `astc_${releaseRecordRandomBytes(32).toString('base64url')}`,
|
|
299524
|
+
});
|
|
299525
|
+
}
|
|
299526
|
+
forHook(hook) {
|
|
299527
|
+
return this.#hooks.has(hook) ? this.#env : EMPTY_RUNTIME;
|
|
299528
|
+
}
|
|
299529
|
+
forMcp(name, config) {
|
|
299530
|
+
return config.transport === 'stdio' && this.#servers.get(name) === config ? this.#env : EMPTY_RUNTIME;
|
|
299531
|
+
}
|
|
299532
|
+
}
|
|
299420
299533
|
//#region ../../packages/agent-core/src/session/index.ts
|
|
299421
299534
|
async function waitForSettlementOrTimeout(promise, timeoutMs) {
|
|
299422
299535
|
let timeout;
|
|
@@ -299497,6 +299610,16 @@ var init_session$1 = __esmMin((() => {
|
|
|
299497
299610
|
writeMetadataPromise = Promise.resolve();
|
|
299498
299611
|
agentsMdWarning;
|
|
299499
299612
|
accountMemoryContext = "";
|
|
299613
|
+
agentSpineRuntime;
|
|
299614
|
+
mcpLoadStarted = false;
|
|
299615
|
+
startMcpServers() {
|
|
299616
|
+
if (this.mcpLoadStarted)
|
|
299617
|
+
return;
|
|
299618
|
+
this.mcpLoadStarted = true;
|
|
299619
|
+
void this.loadMcpServers().catch((error) => {
|
|
299620
|
+
this.emitInitialMcpLoadError(error);
|
|
299621
|
+
});
|
|
299622
|
+
}
|
|
299500
299623
|
constructor(options) {
|
|
299501
299624
|
this.options = options;
|
|
299502
299625
|
this.logHandle = options.id === void 0 ? void 0 : getRootLogger().attachSession({
|
|
@@ -299506,6 +299629,8 @@ var init_session$1 = __esmMin((() => {
|
|
|
299506
299629
|
this.log = this.logHandle?.logger ?? (options.id === void 0 ? log : log.createChild({ sessionId: options.id }));
|
|
299507
299630
|
this.rpc = options.rpc;
|
|
299508
299631
|
this.experimentalFlags = options.experimentalFlags ?? new FlagResolver();
|
|
299632
|
+
this.agentSpineRuntime = this.experimentalFlags.enabled('agent_spine_timeline')
|
|
299633
|
+
? new AgentSpineSessionRuntime(options.agentSpineRuntime) : undefined;
|
|
299509
299634
|
this.visionReader = createManagedMediaVisionReader(options.toolServices?.media) ?? (this.experimentalFlags.enabled("vision_reader") ? createVisionReader(options.config?.services?.visionReader) : void 0);
|
|
299510
299635
|
this.hookEngine = new HookEngine(options.hooks, {
|
|
299511
299636
|
cwd: options.kaos.getcwd(),
|
|
@@ -299534,8 +299659,13 @@ var init_session$1 = __esmMin((() => {
|
|
|
299534
299659
|
code: "HOST_HOOK_WARNING",
|
|
299535
299660
|
message
|
|
299536
299661
|
});
|
|
299537
|
-
}
|
|
299538
|
-
|
|
299662
|
+
},
|
|
299663
|
+
runtimeEnv: (hook) => {
|
|
299664
|
+
const hostEnv = options.hookRuntimeEnv?.(hook);
|
|
299665
|
+
const timelineEnv = this.agentSpineRuntime?.forHook(hook);
|
|
299666
|
+
return timelineEnv === undefined ? hostEnv : { ...hostEnv, ...timelineEnv };
|
|
299667
|
+
}
|
|
299668
|
+
});
|
|
299539
299669
|
this.telemetry = options.telemetry ?? noopTelemetryClient;
|
|
299540
299670
|
this.toolKaos = options.kaos;
|
|
299541
299671
|
this.persistenceKaos = options.persistenceKaos ?? options.kaos;
|
|
@@ -299546,8 +299676,13 @@ var init_session$1 = __esmMin((() => {
|
|
|
299546
299676
|
this.mcp = new McpConnectionManager({
|
|
299547
299677
|
oauthService: new McpOAuthService({ blunHomeDir: resolveSharedBlunHome(options.blunHomeDir) }),
|
|
299548
299678
|
log: this.log,
|
|
299549
|
-
stdioCwd: options.kaos.getcwd()
|
|
299550
|
-
|
|
299679
|
+
stdioCwd: options.kaos.getcwd(),
|
|
299680
|
+
stdioRuntimeEnv: (name, config) => {
|
|
299681
|
+
const hostEnv = options.mcpRuntimeEnv?.(name, config);
|
|
299682
|
+
const timelineEnv = this.agentSpineRuntime?.forMcp(name, config);
|
|
299683
|
+
return timelineEnv === undefined ? hostEnv : { ...hostEnv, ...timelineEnv };
|
|
299684
|
+
}
|
|
299685
|
+
});
|
|
299551
299686
|
this.lsp = options.lspServers === void 0 || Object.keys(options.lspServers).length === 0 ? void 0 : new LspManager({
|
|
299552
299687
|
cwd: options.kaos.getcwd(),
|
|
299553
299688
|
servers: options.lspServers
|
|
@@ -299560,9 +299695,7 @@ var init_session$1 = __esmMin((() => {
|
|
|
299560
299695
|
}).then(() => {
|
|
299561
299696
|
this.refreshAgentBuiltinTools();
|
|
299562
299697
|
});
|
|
299563
|
-
this.
|
|
299564
|
-
this.emitInitialMcpLoadError(error);
|
|
299565
|
-
});
|
|
299698
|
+
if (!this.agentSpineRuntime?.hasRecipients) this.startMcpServers();
|
|
299566
299699
|
}
|
|
299567
299700
|
setToolKaos(kaos) {
|
|
299568
299701
|
this.toolKaos = kaos;
|
|
@@ -300208,7 +300341,18 @@ var init_session$1 = __esmMin((() => {
|
|
|
300208
300341
|
});
|
|
300209
300342
|
}
|
|
300210
300343
|
async triggerSessionStart(source) {
|
|
300211
|
-
|
|
300344
|
+
if (this.agentSpineRuntime?.hasRecipients && !this.mcpLoadStarted) {
|
|
300345
|
+
try {
|
|
300346
|
+
const main = this.getReadyAgent('main');
|
|
300347
|
+
await main?.records.flush();
|
|
300348
|
+
this.agentSpineRuntime.bind(this.options.id, main?.records.fileSource);
|
|
300349
|
+
}
|
|
300350
|
+
catch {
|
|
300351
|
+
this.log.warn('Native session record source is unavailable.');
|
|
300352
|
+
}
|
|
300353
|
+
this.startMcpServers();
|
|
300354
|
+
}
|
|
300355
|
+
const results = await this.hookEngine.trigger("SessionStart", {
|
|
300212
300356
|
matcherValue: source,
|
|
300213
300357
|
inputData: { source }
|
|
300214
300358
|
});
|
|
@@ -315095,43 +315239,80 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315095
315239
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
315096
315240
|
}
|
|
315097
315241
|
async search(query, options) {
|
|
315098
|
-
|
|
315099
|
-
|
|
315100
|
-
|
|
315101
|
-
|
|
315102
|
-
|
|
315103
|
-
|
|
315104
|
-
|
|
315105
|
-
|
|
315106
|
-
|
|
315107
|
-
|
|
315108
|
-
|
|
315109
|
-
|
|
315110
|
-
|
|
315111
|
-
|
|
315112
|
-
|
|
315113
|
-
|
|
315114
|
-
|
|
315115
|
-
|
|
315116
|
-
|
|
315117
|
-
|
|
315118
|
-
|
|
315119
|
-
|
|
315120
|
-
|
|
315121
|
-
|
|
315122
|
-
|
|
315123
|
-
|
|
315124
|
-
|
|
315125
|
-
|
|
315126
|
-
|
|
315127
|
-
|
|
315128
|
-
|
|
315129
|
-
|
|
315130
|
-
|
|
315131
|
-
|
|
315132
|
-
|
|
315133
|
-
|
|
315134
|
-
|
|
315242
|
+
const body = { text_query: query };
|
|
315243
|
+
const bodyJson = JSON.stringify(body);
|
|
315244
|
+
const toolCallId = options?.toolCallId;
|
|
315245
|
+
const supportsNotices = typeof options?.onNotice === 'function';
|
|
315246
|
+
const response = await this.post(bodyJson, toolCallId, supportsNotices);
|
|
315247
|
+
if (response.status === 401) {
|
|
315248
|
+
const detail = await safeReadText(response);
|
|
315249
|
+
throw new Error(`Blun search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
|
|
315250
|
+
}
|
|
315251
|
+
if (response.status !== 200) {
|
|
315252
|
+
const detail = await safeReadText(response);
|
|
315253
|
+
throw new Error(`Blun search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
315254
|
+
}
|
|
315255
|
+
const json = (await response.json());
|
|
315256
|
+
const raw = Array.isArray(json.search_results) ? json.search_results : [];
|
|
315257
|
+
const status = json.search_status;
|
|
315258
|
+
if (status !== undefined) {
|
|
315259
|
+
if (!status || typeof status !== 'object' || !Array.isArray(json.search_results)
|
|
315260
|
+
|| !Number.isInteger(status.failed_sources) || typeof status.failed_sources !== 'number'
|
|
315261
|
+
|| status.failed_sources < 0 || status.failed_sources > 128
|
|
315262
|
+
|| (status.state !== 'complete' && status.state !== 'partial')
|
|
315263
|
+
|| (status.state === 'complete' && status.failed_sources !== 0)
|
|
315264
|
+
|| (status.state === 'partial' && (status.failed_sources === 0 || raw.length === 0))) {
|
|
315265
|
+
throw new Error('Invalid search status response.');
|
|
315266
|
+
}
|
|
315267
|
+
if (!raw.every(row => {
|
|
315268
|
+
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315269
|
+
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315270
|
+
return false;
|
|
315271
|
+
try {
|
|
315272
|
+
const url = new URL(row.url);
|
|
315273
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315274
|
+
}
|
|
315275
|
+
catch {
|
|
315276
|
+
return false;
|
|
315277
|
+
}
|
|
315278
|
+
}))
|
|
315279
|
+
throw new Error('Invalid search results response.');
|
|
315280
|
+
if (status.state === 'partial') {
|
|
315281
|
+
if (!supportsNotices)
|
|
315282
|
+
throw new Error('Partial search results require a notice consumer.');
|
|
315283
|
+
options?.onNotice?.({ code: 'partial-sources', failedSources: status.failed_sources });
|
|
315284
|
+
}
|
|
315285
|
+
}
|
|
315286
|
+
return raw.map((r) => {
|
|
315287
|
+
const out = {
|
|
315288
|
+
title: r.title ?? '',
|
|
315289
|
+
url: r.url ?? '',
|
|
315290
|
+
snippet: r.snippet ?? '',
|
|
315291
|
+
};
|
|
315292
|
+
if (typeof r.date === 'string' && r.date.length > 0)
|
|
315293
|
+
out.date = r.date;
|
|
315294
|
+
if (typeof r.site_name === 'string' && r.site_name.length > 0)
|
|
315295
|
+
out.siteName = r.site_name;
|
|
315296
|
+
return out;
|
|
315297
|
+
});
|
|
315298
|
+
}
|
|
315299
|
+
async post(bodyJson, toolCallId, supportsNotices) {
|
|
315300
|
+
const accessToken = await this.resolveApiKey();
|
|
315301
|
+
return this.fetchImpl(this.baseUrl, {
|
|
315302
|
+
method: 'POST',
|
|
315303
|
+
headers: {
|
|
315304
|
+
...this.defaultHeaders,
|
|
315305
|
+
Authorization: `Bearer ${accessToken}`,
|
|
315306
|
+
'Content-Type': 'application/json',
|
|
315307
|
+
...(toolCallId !== undefined && toolCallId.length > 0
|
|
315308
|
+
? { 'X-Msh-Tool-Call-Id': toolCallId }
|
|
315309
|
+
: {}),
|
|
315310
|
+
...this.customHeaders,
|
|
315311
|
+
...(supportsNotices ? { 'X-BLUN-Search-Contract': 'partial-v1' } : {}),
|
|
315312
|
+
},
|
|
315313
|
+
body: bodyJson,
|
|
315314
|
+
});
|
|
315315
|
+
}
|
|
315135
315316
|
async resolveApiKey() {
|
|
315136
315317
|
if (this.tokenProvider !== void 0) try {
|
|
315137
315318
|
return await this.tokenProvider.getAccessToken();
|
|
@@ -318903,6 +319084,7 @@ var init_core_impl = __esmMin((() => {
|
|
|
318903
319084
|
const pluginSessionStarts = this.plugins.enabledSessionStarts();
|
|
318904
319085
|
const pluginCommands = await this.plugins.enabledCommands();
|
|
318905
319086
|
const mcpConfig = this.mergePluginMcpConfig(withCallerMcp);
|
|
319087
|
+
const pluginHooks = this.plugins.enabledHooks();
|
|
318906
319088
|
const runtime = await this.resolveRuntime(config);
|
|
318907
319089
|
const session = new Session$1({
|
|
318908
319090
|
kaos: parentKaos.withCwd(workDir),
|
|
@@ -318915,7 +319097,8 @@ var init_core_impl = __esmMin((() => {
|
|
|
318915
319097
|
rpc: proxyWithExtraPayload(await this.sdk, { sessionId: summary.id }),
|
|
318916
319098
|
providerManager: this.resolveProviderManager(summary.id),
|
|
318917
319099
|
background: config.background,
|
|
318918
|
-
hooks: [...config.hooks ?? [], ...
|
|
319100
|
+
hooks: [...(config.hooks ?? []), ...pluginHooks],
|
|
319101
|
+
agentSpineRuntime: this.agentSpineRuntimeRecipients(pluginHooks, mcpConfig),
|
|
318919
319102
|
permissionRules: config.permission?.rules,
|
|
318920
319103
|
skills: this.resolveSessionSkillConfig(config),
|
|
318921
319104
|
mcpConfig,
|
|
@@ -318998,6 +319181,7 @@ var init_core_impl = __esmMin((() => {
|
|
|
318998
319181
|
const pluginSessionStarts = this.plugins.enabledSessionStarts();
|
|
318999
319182
|
const pluginCommands = await this.plugins.enabledCommands();
|
|
319000
319183
|
const mcpConfig = this.mergePluginMcpConfig(withCallerMcp);
|
|
319184
|
+
const pluginHooks = this.plugins.enabledHooks();
|
|
319001
319185
|
const runtime = await this.resolveRuntime(config);
|
|
319002
319186
|
const parentKaos = parentKaosForRead;
|
|
319003
319187
|
const persistenceKaos = overrides.persistenceKaos ?? parentKaos;
|
|
@@ -319012,7 +319196,8 @@ var init_core_impl = __esmMin((() => {
|
|
|
319012
319196
|
rpc: proxyWithExtraPayload(await this.sdk, { sessionId: summary.id }),
|
|
319013
319197
|
providerManager: this.resolveProviderManager(summary.id),
|
|
319014
319198
|
background: config.background,
|
|
319015
|
-
hooks: [...config.hooks ?? [], ...
|
|
319199
|
+
hooks: [...(config.hooks ?? []), ...pluginHooks],
|
|
319200
|
+
agentSpineRuntime: this.agentSpineRuntimeRecipients(pluginHooks, mcpConfig),
|
|
319016
319201
|
permissionRules: config.permission?.rules,
|
|
319017
319202
|
skills: this.resolveSessionSkillConfig(config),
|
|
319018
319203
|
mcpConfig,
|
|
@@ -319468,6 +319653,21 @@ var init_core_impl = __esmMin((() => {
|
|
|
319468
319653
|
}
|
|
319469
319654
|
};
|
|
319470
319655
|
}
|
|
319656
|
+
agentSpineRuntimeRecipients(pluginHooks, mcpConfig) {
|
|
319657
|
+
if (!this.experimentalFlags.enabled('agent_spine_timeline'))
|
|
319658
|
+
return undefined;
|
|
319659
|
+
const plugin = this.plugins.get('agent-spine');
|
|
319660
|
+
if (plugin?.state !== 'ok' || !plugin.enabled)
|
|
319661
|
+
return undefined;
|
|
319662
|
+
// These objects came from PluginManager, not caller hooks or session metadata.
|
|
319663
|
+
const hooks = pluginHooks.filter(hook => hook.env?.['BLUN_PLUGIN_ROOT'] === plugin.root);
|
|
319664
|
+
const mcpServers = Object.fromEntries(Object.entries(mcpConfig?.servers ?? {}).filter(([name, config]) => {
|
|
319665
|
+
const origin = mcpConfig?.origins?.[name];
|
|
319666
|
+
return origin?.source === 'plugin' && origin.pluginId === plugin.id && origin.pluginEnabled
|
|
319667
|
+
&& config.transport === 'stdio' && config.enabled !== false;
|
|
319668
|
+
}));
|
|
319669
|
+
return hooks.length > 0 || Object.keys(mcpServers).length > 0 ? { hooks, mcpServers } : undefined;
|
|
319670
|
+
}
|
|
319471
319671
|
async resolveCurrentMcpConfig(session) {
|
|
319472
319672
|
const snapshot = session.mcpConfigSnapshot();
|
|
319473
319673
|
const callerServers = Object.fromEntries(Object.entries(snapshot?.servers ?? {}).filter(([name]) => snapshot?.origins?.[name]?.source === "session"));
|
|
@@ -422051,6 +422251,21 @@ var TelegramChannelController = class {
|
|
|
422051
422251
|
constructor(host, dir = telegramStateDir(), options = {}) {
|
|
422052
422252
|
this.host = host;
|
|
422053
422253
|
this.dir = dir;
|
|
422254
|
+
this.sessionScope = options.sessionScope;
|
|
422255
|
+
this.sessionDelivery = new BlunSessionQueueDelivery(dir, {
|
|
422256
|
+
ownsChannel: () => !this.stopped && this.ownsChannel(),
|
|
422257
|
+
currentSessionId: () => this.sessionScope?.()?.sessionId,
|
|
422258
|
+
legacyCheckpoint: () => this.checkpointOffset,
|
|
422259
|
+
inject: (envelope, acknowledge) => {
|
|
422260
|
+
const trace = telegramDeliveryIdentity(envelope.meta);
|
|
422261
|
+
telegramDeliveryLifecycle.record({ stage: "tui_injected", ...trace, route: "tui" });
|
|
422262
|
+
this.host.inject(envelope, () => {
|
|
422263
|
+
if (this.stopped || !this.ownsChannel()) return;
|
|
422264
|
+
acknowledge();
|
|
422265
|
+
telegramDeliveryLifecycle.record({ stage: "acknowledged", ...trace, route: "tui" });
|
|
422266
|
+
});
|
|
422267
|
+
}
|
|
422268
|
+
});
|
|
422054
422269
|
this.spawnBridge = options.spawnBridge ?? spawn;
|
|
422055
422270
|
this.bridgeStopTimeoutMs = options.bridgeStopTimeoutMs ?? BRIDGE_STOP_TIMEOUT_MS;
|
|
422056
422271
|
this.bridgeForceStopTimeoutMs = options.bridgeForceStopTimeoutMs ?? BRIDGE_FORCE_STOP_TIMEOUT_MS;
|
|
@@ -422305,6 +422520,7 @@ var TelegramChannelController = class {
|
|
|
422305
422520
|
this.loseOwnership();
|
|
422306
422521
|
return;
|
|
422307
422522
|
}
|
|
422523
|
+
if (this.sessionScope !== undefined) { this.pollSessionQueue(this.sessionScope()); return; }
|
|
422308
422524
|
let stats;
|
|
422309
422525
|
try {
|
|
422310
422526
|
stats = statSync(this.queueFile);
|
|
@@ -422486,6 +422702,16 @@ var TelegramChannelController = class {
|
|
|
422486
422702
|
if (!servers.some((server) => (server.name === "telegram" || server.name.endsWith(":telegram")) && server.status !== "disabled" && server.status !== "failed")) this.host.warn(uiText("telegramChannel.replyToolsMissing", { command: "/plugins install <repo>\\plugins\\telegram" }));
|
|
422487
422703
|
} catch {}
|
|
422488
422704
|
}
|
|
422705
|
+
|
|
422706
|
+
pollSessionQueue(scope, deliver = true) {
|
|
422707
|
+
try { this.sessionDelivery.poll(scope, deliver); this.sessionQueueError = undefined; }
|
|
422708
|
+
catch (error) { const message = String(error); if (message !== this.sessionQueueError) {
|
|
422709
|
+
this.sessionQueueError = message;
|
|
422710
|
+
this.host.warn(uiText("telegramChannel.leaseWriteFailed", { error: message }));
|
|
422711
|
+
} }
|
|
422712
|
+
}
|
|
422713
|
+
|
|
422714
|
+
bindSessionQueue(scope) { if (this.sessionScope !== undefined) this.pollSessionQueue(scope, false); }
|
|
422489
422715
|
};
|
|
422490
422716
|
//#endregion
|
|
422491
422717
|
//#region src/tui/commands/config-general.copy.ts
|
|
@@ -518947,28 +519173,46 @@ async function saveAutoRetrievedMedia(result) {
|
|
|
518947
519173
|
function isGroupChat(chatId) {
|
|
518948
519174
|
return chatId.startsWith("-");
|
|
518949
519175
|
}
|
|
518950
|
-
|
|
518951
|
-
|
|
519176
|
+
function normalizeGroupNoise(text) {
|
|
519177
|
+
return text
|
|
519178
|
+
.toLowerCase()
|
|
519179
|
+
.replace(/\u00e4/g, 'a')
|
|
519180
|
+
.replace(/\u00f6/g, 'o')
|
|
519181
|
+
.replace(/\u00fc/g, 'u')
|
|
519182
|
+
.replace(/\u00df/g, 'ss')
|
|
519183
|
+
.replace(/[\u2010-\u2015]/g, ' ')
|
|
519184
|
+
.replace(/[^\p{L}\p{N}\s]/gu, ' ')
|
|
519185
|
+
.replace(/\s+/g, ' ')
|
|
519186
|
+
.trim();
|
|
519187
|
+
}
|
|
519188
|
+
const GROUP_REPLY_EMPTY_CLAUSES = [
|
|
519189
|
+
/^(verstanden|kapiert|ok|okay|alles klar|geht klar|angekommen|seh sie|ich seh sie|gemerkt|passt|erledigt notiert|notiert|understood|got it|noted|acknowledged|roger|received)$/,
|
|
519190
|
+
/^(bin da|bin hier|hier bin ich|ich bin da|was brauchst du|was gibt es|was liegt an|wie kann ich helfen|bereit|ich warte|warte auf|melde mich|freut mich|here|standing by|ready|awaiting|what do you need)$/,
|
|
519191
|
+
/^(keine (gruppen ?)?antwort|gar keine antwort|keine reaktion|kein wort|no ?action|no (response|reply|answer|comment))( (notig|erforderlich|needed|required))?$/,
|
|
519192
|
+
/^(ich )?(halte mich raus|(bin|bleibe) (jetzt )?still|schweige|habe nichts zu melden)$/,
|
|
519193
|
+
/^(stille|still|silence|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht meine lane)$/,
|
|
519194
|
+
/^(i (am |will be )?)?(staying (silent|out|quiet)|keeping (quiet|out)|holding back)$/,
|
|
519195
|
+
/^(i have )?nothing (to (add|do|report)|for me)$/,
|
|
519196
|
+
/^no need to (respond|reply)$/,
|
|
519197
|
+
/^(nicht (direkt )?an mich( gerichtet)?|not (for me|my lane|addressed to me|directed at me|to me))$/,
|
|
519198
|
+
/^(die(se)? nachricht ist )?(nicht (direkt )?an mich|an die gruppe)( gerichtet)?$/,
|
|
519199
|
+
/^(this|the) message is (addressed to the group|not (addressed )?to me)$/,
|
|
519200
|
+
/^(es gibt )?kein(en|e)? (auftrag( (an|fur|fuer) mich)?|frage( an mich)?|arbeit( verlangt)?)$/,
|
|
519201
|
+
/^(there is )?no (task|question|work)( for me)?$/,
|
|
519202
|
+
/^(ich beende den )?turn ohne (ausgabe|antwort)( beendet)?$/,
|
|
519203
|
+
/^i (will )?end this turn without (a response|output)$/,
|
|
519204
|
+
/^(nach|gemaess) der leerlauf regel( \d+)? gibt es nichts zu melden$/,
|
|
519205
|
+
];
|
|
519206
|
+
const GROUP_REPLY_NOTIFICATION_CONTEXT = /^die(se)? nachricht ist eine (konfigurations )?information( [\p{L}\p{N} _-]{1,64} wird ab jetzt nur noch bei erwahnung benachrichtigt)?( an die gruppe gerichtet)?$/u;
|
|
519207
|
+
|
|
518952
519208
|
function isGroupNoiseReply(text) {
|
|
518953
|
-
|
|
518954
|
-
|
|
518955
|
-
|
|
518956
|
-
return STRONG_META.test(n) || SHORT_CONFIRM.test(n);
|
|
519209
|
+
const clauses = text.split(/[.!?;,\r\n]+|\s+[-\u2013\u2014]\s+/u).map(normalizeGroupNoise).filter(Boolean);
|
|
519210
|
+
return clauses.every((clause) => GROUP_REPLY_EMPTY_CLAUSES.some((pattern) => pattern.test(clause))
|
|
519211
|
+
|| GROUP_REPLY_NOTIFICATION_CONTEXT.test(clause));
|
|
518957
519212
|
}
|
|
518958
|
-
/**
|
|
518959
|
-
|
|
518960
|
-
|
|
518961
|
-
* Sprachen": on a read-only message King should stay silent, so ANY short reply
|
|
518962
|
-
* back is meta chatter regardless of language ("Silence.", "Tystnad.", "沈黙。").
|
|
518963
|
-
* Long substantive contributions still pass; the word-list catches longer DE/EN
|
|
518964
|
-
* meta lines. Addressed messages only hit the word-list, never the length rule.
|
|
518965
|
-
*/
|
|
518966
|
-
function isGroupSuppressed(text, contextOnly) {
|
|
518967
|
-
if (isGroupNoiseReply(text)) return true;
|
|
518968
|
-
if (contextOnly) {
|
|
518969
|
-
if (text.replace(/\s+/g, " ").trim().length <= 40) return true;
|
|
518970
|
-
}
|
|
518971
|
-
return false;
|
|
519213
|
+
/** Unknown content is preserved, including short useful context-only warnings. */
|
|
519214
|
+
function isGroupSuppressed(text, _contextOnly) {
|
|
519215
|
+
return isGroupNoiseReply(text);
|
|
518972
519216
|
}
|
|
518973
519217
|
/**
|
|
518974
519218
|
* Send `text` to `chatId` via the Bot API and log it to outbox.jsonl as
|
|
@@ -520803,6 +521047,7 @@ var BlunTUI = class {
|
|
|
520803
521047
|
const { workDir } = this.state.appState;
|
|
520804
521048
|
let session;
|
|
520805
521049
|
let shouldReplayHistory = false;
|
|
521050
|
+
const telegramBoundary = this.captureTelegramQueueBoundary();
|
|
520806
521051
|
const isResumeStartup = startup.sessionFlag !== void 0 || startup.continueLast;
|
|
520807
521052
|
const createSessionOptions = {
|
|
520808
521053
|
workDir,
|
|
@@ -520876,7 +521121,7 @@ var BlunTUI = class {
|
|
|
520876
521121
|
return false;
|
|
520877
521122
|
}
|
|
520878
521123
|
if (session === void 0) throw new Error(uiText("blunTui.startup.sessionNotInitialized"));
|
|
520879
|
-
await this.measureStartupPhase("session_bind_ms", () => this.setSession(session, { refreshPersonalMemory: false }));
|
|
521124
|
+
await this.measureStartupPhase("session_bind_ms", () => this.setSession(session, { refreshPersonalMemory: false, telegramSessionScope: { sessionId: session.id, mode: shouldReplayHistory ? "resume" : "new", boundary: telegramBoundary } }));
|
|
520880
521125
|
await this.measureStartupPhase("runtime_state_ms", () => this.syncRuntimeState(session));
|
|
520881
521126
|
this.applyStartupPermissionAndPlanToAppState();
|
|
520882
521127
|
this.state.startupState = "ready";
|
|
@@ -522115,6 +522360,7 @@ var BlunTUI = class {
|
|
|
522115
522360
|
text: focusedModelInput
|
|
522116
522361
|
}, imagePart] : focusedModelInput;
|
|
522117
522362
|
session.promptAccepted(promptInput, channelPromptOrigin(channelReportSources)).then((result) => {
|
|
522363
|
+
if (this.session !== session) return;
|
|
522118
522364
|
if (result.accepted) {
|
|
522119
522365
|
if (channelFocusId !== void 0) this.clearAddressedChannelFocusIds([channelFocusId]);
|
|
522120
522366
|
this.traceTelegramDelivery?.({ stage: "accepted", ...channelDeliveryTrace, route: "prompt", turnId: result.turnId });
|
|
@@ -522174,6 +522420,7 @@ var BlunTUI = class {
|
|
|
522174
522420
|
this.updateQueueDisplay();
|
|
522175
522421
|
this.state.ui.requestRender();
|
|
522176
522422
|
}).catch((error) => {
|
|
522423
|
+
if (this.session !== session) return;
|
|
522177
522424
|
this.failSessionRequest(uiText("blunTui.session.sendFailed", { error: formatErrorMessage$2(error) }));
|
|
522178
522425
|
});
|
|
522179
522426
|
this.updateQueueDisplay();
|
|
@@ -522662,8 +522909,8 @@ var BlunTUI = class {
|
|
|
522662
522909
|
onOwnershipLost: () => {
|
|
522663
522910
|
if (this.telegramChannel === controller) this.telegramChannel = void 0;
|
|
522664
522911
|
}
|
|
522665
|
-
|
|
522666
|
-
|
|
522912
|
+
}, telegramStateDir(), { sessionScope: () => this.deferUserMessages || this.session?.id !== this.telegramSessionScope?.sessionId ? undefined : this.telegramSessionScope });
|
|
522913
|
+
controller.start();
|
|
522667
522914
|
this.telegramChannel = controller;
|
|
522668
522915
|
this.showStatus(uiText("blunTui.telegram.attached", { pid: process.pid }));
|
|
522669
522916
|
}
|
|
@@ -523269,27 +523516,10 @@ var BlunTUI = class {
|
|
|
523269
523516
|
return this.harness.createSession(options);
|
|
523270
523517
|
}
|
|
523271
523518
|
async setSession(session, options = {}) {
|
|
523272
|
-
|
|
523273
|
-
|
|
523274
|
-
|
|
523275
|
-
|
|
523276
|
-
}
|
|
523277
|
-
await this.personalMemoryController.clear(this.session);
|
|
523278
|
-
const previous = this.unloadCurrentSession(approvalCancellationFeedback("switching_session"));
|
|
523279
|
-
await this.managedQuotaWarningPersistence;
|
|
523280
|
-
await this.flushGoalChannelReplyPersistence();
|
|
523281
|
-
await previous?.close();
|
|
523282
|
-
resetChannelPreambleState(this.channelPreamble);
|
|
523283
|
-
this.session = session;
|
|
523284
|
-
this.managedQuotaWarningController.restore(session.id, managedQuotaWarningThresholdFromMetadata(session.getResumeState()?.sessionMetadata?.custom));
|
|
523285
|
-
this.setAppState({ modelFallbackAllowed: false });
|
|
523286
|
-
this.harness.setTelemetryContext({ sessionId: session.id });
|
|
523287
|
-
this.registerSessionHandlers(session);
|
|
523288
|
-
this.syncAdditionalDirs(session);
|
|
523289
|
-
await this.refreshManagedImageReaderAvailability(session);
|
|
523290
|
-
await this.authFlow.applyManagedAccountContextToSession();
|
|
523291
|
-
if (options.refreshPersonalMemory !== false) await this.personalMemoryController.refresh();
|
|
523292
|
-
}
|
|
523519
|
+
await this.withTelegramQueuePaused(async boundary => {
|
|
523520
|
+
await this.setSessionRuntime(session, options, options.telegramSessionScope ?? { sessionId: session.id, mode: "resume", boundary });
|
|
523521
|
+
});
|
|
523522
|
+
}
|
|
523293
523523
|
async syncRuntimeState(session = this.requireSession()) {
|
|
523294
523524
|
const [status, goalResult, loopResult] = await Promise.all([session.getStatus(), session.getGoal(), session.getLoop()]);
|
|
523295
523525
|
this.setAppState({
|
|
@@ -523498,28 +523728,10 @@ var BlunTUI = class {
|
|
|
523498
523728
|
return true;
|
|
523499
523729
|
}
|
|
523500
523730
|
async switchToSession(session, statusMessage) {
|
|
523501
|
-
|
|
523502
|
-
|
|
523503
|
-
|
|
523504
|
-
|
|
523505
|
-
try {
|
|
523506
|
-
await this.refreshSkillCommands(this.session);
|
|
523507
|
-
await this.refreshPluginCommands(this.session);
|
|
523508
|
-
} catch {}
|
|
523509
|
-
this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
|
|
523510
|
-
try {
|
|
523511
|
-
await this.sessionReplay.hydrateFromReplay(session);
|
|
523512
|
-
} catch (error) {
|
|
523513
|
-
const msg = formatErrorMessage$2(error);
|
|
523514
|
-
this.showError(uiText("blunTui.session.replayFailed", { error: msg }));
|
|
523515
|
-
} finally {
|
|
523516
|
-
this.sessionEventHandler.startSubscription();
|
|
523517
|
-
}
|
|
523518
|
-
const resumeState = session.getResumeState();
|
|
523519
|
-
if (resumeState?.warning !== void 0) this.showStatus(uiText("blunTui.warning", { warning: resumeState.warning }), "warning");
|
|
523520
|
-
this.showStatus(statusMessage);
|
|
523521
|
-
this.showSessionWarnings(session);
|
|
523522
|
-
}
|
|
523731
|
+
await this.withTelegramQueuePaused(async (boundary) => {
|
|
523732
|
+
await this.switchToSessionRuntime(session, statusMessage, boundary);
|
|
523733
|
+
});
|
|
523734
|
+
}
|
|
523523
523735
|
async reloadCurrentSessionView(session, statusMessage) {
|
|
523524
523736
|
await this.personalMemoryController.clear(session);
|
|
523525
523737
|
if (this.goalChannelReplyScope?.session === session && this.goalChannelReplyScope.reloading) this.goalChannelReplyScope.restored = false;
|
|
@@ -523553,56 +523765,10 @@ var BlunTUI = class {
|
|
|
523553
523765
|
this.showSessionWarnings(session);
|
|
523554
523766
|
}
|
|
523555
523767
|
async createNewSession() {
|
|
523556
|
-
|
|
523557
|
-
|
|
523558
|
-
|
|
523559
|
-
|
|
523560
|
-
this.clearQueuedMessages();
|
|
523561
|
-
const activeSession = this.session;
|
|
523562
|
-
const activeTurn = activeSession !== void 0 && (this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting);
|
|
523563
|
-
if (activeTurn) {
|
|
523564
|
-
this.markCurrentRunCancelledByUser();
|
|
523565
|
-
try {
|
|
523566
|
-
await activeSession.cancel();
|
|
523567
|
-
} catch (error) {
|
|
523568
|
-
if (!(error instanceof BlunError && error.code === ErrorCodes.SESSION_NOT_FOUND)) {
|
|
523569
|
-
this.showError(formatErrorMessage$2(error));
|
|
523570
|
-
return;
|
|
523571
|
-
}
|
|
523572
|
-
}
|
|
523573
|
-
this.clearTurnCancellation();
|
|
523574
|
-
}
|
|
523575
|
-
let session;
|
|
523576
|
-
try {
|
|
523577
|
-
session = await this.createSessionFromCurrentState();
|
|
523578
|
-
} catch (error) {
|
|
523579
|
-
const msg = formatErrorMessage$2(error);
|
|
523580
|
-
this.showError(uiText("blunTui.session.createFailed", { error: msg }));
|
|
523581
|
-
return;
|
|
523582
|
-
}
|
|
523583
|
-
this.resetSessionRuntime();
|
|
523584
|
-
await this.setSession(session);
|
|
523585
|
-
this.setAppState({ sessionId: session.id });
|
|
523586
|
-
try {
|
|
523587
|
-
await this.activateRuntime();
|
|
523588
|
-
await this.syncRuntimeState(session);
|
|
523589
|
-
} catch (error) {
|
|
523590
|
-
this.sessionEventHandler.startSubscription();
|
|
523591
|
-
const msg = formatErrorMessage$2(error);
|
|
523592
|
-
this.showError(uiText("blunTui.session.postCreateFailed", { error: msg }));
|
|
523593
|
-
return;
|
|
523594
|
-
}
|
|
523595
|
-
try {
|
|
523596
|
-
await this.refreshSkillCommands(this.session);
|
|
523597
|
-
await this.refreshPluginCommands(this.session);
|
|
523598
|
-
} catch {}
|
|
523599
|
-
this.sessionEventHandler.startSubscription();
|
|
523600
|
-
this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
|
|
523601
|
-
this.scrollbackController.prepareNewSession(session);
|
|
523602
|
-
this.showStatus(uiText("blunTui.session.started", { sessionId: session.id }));
|
|
523603
|
-
this.showSessionWarnings(session);
|
|
523604
|
-
this.showConfigWarningsIfAny();
|
|
523605
|
-
}
|
|
523768
|
+
await this.withTelegramQueuePaused(async (boundary) => {
|
|
523769
|
+
await this.createNewSessionRuntime(boundary);
|
|
523770
|
+
});
|
|
523771
|
+
}
|
|
523606
523772
|
/** Surface config.toml load warnings (degraded or kept-previous config) in the status bar. */
|
|
523607
523773
|
async showConfigWarningsIfAny() {
|
|
523608
523774
|
try {
|
|
@@ -524574,6 +524740,133 @@ var BlunTUI = class {
|
|
|
524574
524740
|
this.patchLivePane({ pendingQuestion: null });
|
|
524575
524741
|
this.restoreEditor();
|
|
524576
524742
|
}
|
|
524743
|
+
|
|
524744
|
+
async switchToSessionRuntime(session, statusMessage, boundary) {
|
|
524745
|
+
this.resetSessionRuntime();
|
|
524746
|
+
await this.setSession(session, { telegramSessionScope: { sessionId: session.id, mode: "resume", boundary } });
|
|
524747
|
+
await this.syncRuntimeState(session);
|
|
524748
|
+
this.updateTerminalTitle();
|
|
524749
|
+
try {
|
|
524750
|
+
await this.refreshSkillCommands(this.session);
|
|
524751
|
+
await this.refreshPluginCommands(this.session);
|
|
524752
|
+
} catch {}
|
|
524753
|
+
this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
|
|
524754
|
+
try {
|
|
524755
|
+
await this.sessionReplay.hydrateFromReplay(session);
|
|
524756
|
+
} catch (error) {
|
|
524757
|
+
const msg = formatErrorMessage$2(error);
|
|
524758
|
+
this.showError(uiText("blunTui.session.replayFailed", { error: msg }));
|
|
524759
|
+
} finally {
|
|
524760
|
+
this.sessionEventHandler.startSubscription();
|
|
524761
|
+
}
|
|
524762
|
+
const resumeState = session.getResumeState();
|
|
524763
|
+
if (resumeState?.warning !== void 0) this.showStatus(uiText("blunTui.warning", { warning: resumeState.warning }), "warning");
|
|
524764
|
+
this.showStatus(statusMessage);
|
|
524765
|
+
this.showSessionWarnings(session);
|
|
524766
|
+
}
|
|
524767
|
+
|
|
524768
|
+
async createNewSessionRuntime(boundary) {
|
|
524769
|
+
if (this.state.appState.isReplaying) {
|
|
524770
|
+
this.showError(uiText("blunTui.session.createReplayBlocked"));
|
|
524771
|
+
return;
|
|
524772
|
+
}
|
|
524773
|
+
const activeSession = this.session;
|
|
524774
|
+
const activeTurn = activeSession !== void 0 && (this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting);
|
|
524775
|
+
if (activeTurn) {
|
|
524776
|
+
this.markCurrentRunCancelledByUser();
|
|
524777
|
+
try {
|
|
524778
|
+
await activeSession.cancel();
|
|
524779
|
+
} catch (error) {
|
|
524780
|
+
if (!(error instanceof BlunError && error.code === ErrorCodes.SESSION_NOT_FOUND)) {
|
|
524781
|
+
this.showError(formatErrorMessage$2(error));
|
|
524782
|
+
return;
|
|
524783
|
+
}
|
|
524784
|
+
}
|
|
524785
|
+
this.clearTurnCancellation();
|
|
524786
|
+
}
|
|
524787
|
+
let session;
|
|
524788
|
+
try {
|
|
524789
|
+
session = await this.createSessionFromCurrentState();
|
|
524790
|
+
} catch (error) {
|
|
524791
|
+
const msg = formatErrorMessage$2(error);
|
|
524792
|
+
this.showError(uiText("blunTui.session.createFailed", { error: msg }));
|
|
524793
|
+
return;
|
|
524794
|
+
}
|
|
524795
|
+
this.resetSessionRuntime();
|
|
524796
|
+
await this.setSession(session, { telegramSessionScope: { sessionId: session.id, mode: "new", boundary } });
|
|
524797
|
+
this.setAppState({ sessionId: session.id });
|
|
524798
|
+
try {
|
|
524799
|
+
await this.activateRuntime();
|
|
524800
|
+
await this.syncRuntimeState(session);
|
|
524801
|
+
} catch (error) {
|
|
524802
|
+
this.sessionEventHandler.startSubscription();
|
|
524803
|
+
const msg = formatErrorMessage$2(error);
|
|
524804
|
+
this.showError(uiText("blunTui.session.postCreateFailed", { error: msg }));
|
|
524805
|
+
return;
|
|
524806
|
+
}
|
|
524807
|
+
try {
|
|
524808
|
+
await this.refreshSkillCommands(this.session);
|
|
524809
|
+
await this.refreshPluginCommands(this.session);
|
|
524810
|
+
} catch {}
|
|
524811
|
+
this.sessionEventHandler.startSubscription();
|
|
524812
|
+
this.clearTranscriptAndRedraw({ preserveScrollbackArchive: true });
|
|
524813
|
+
this.scrollbackController.prepareNewSession(session);
|
|
524814
|
+
this.showStatus(uiText("blunTui.session.started", { sessionId: session.id }));
|
|
524815
|
+
this.showSessionWarnings(session);
|
|
524816
|
+
this.showConfigWarningsIfAny();
|
|
524817
|
+
}
|
|
524818
|
+
|
|
524819
|
+
async setSessionRuntime(session, options, queueScope) {
|
|
524820
|
+
const resumedStatus = await session.getStatus();
|
|
524821
|
+
if (!isManagedBlunModelAlias(resumedStatus.model ?? BLUN_KING_MODEL_ALIAS, this.state.appState.availableModels)) {
|
|
524822
|
+
await session.setModel(BLUN_KING_MODEL_ALIAS, { allowFallback: false });
|
|
524823
|
+
this.showStatus(uiText("config.model.unknownAlias", { alias: resumedStatus.model ?? "" }), "warning");
|
|
524824
|
+
}
|
|
524825
|
+
await this.personalMemoryController.clear(this.session);
|
|
524826
|
+
const previous = this.unloadCurrentSession(approvalCancellationFeedback("switching_session"));
|
|
524827
|
+
await this.managedQuotaWarningPersistence;
|
|
524828
|
+
await this.flushGoalChannelReplyPersistence();
|
|
524829
|
+
await previous?.close();
|
|
524830
|
+
if (previous?.id !== session.id) {
|
|
524831
|
+
this.state.queuedMessages = this.state.queuedMessages.filter(item => item.mode !== "channel" && item.mode !== "channel-command");
|
|
524832
|
+
this.queueFlushBatchRemaining = 0;
|
|
524833
|
+
this.queueSteerInFlight = undefined;
|
|
524834
|
+
this.channelPromptInFlight = undefined;
|
|
524835
|
+
this.deferredChannelAcknowledgements.clear();
|
|
524836
|
+
this.pendingChannelReplyGuard = undefined;
|
|
524837
|
+
this.syncChannelQueueDeadline();
|
|
524838
|
+
}
|
|
524839
|
+
resetChannelPreambleState(this.channelPreamble);
|
|
524840
|
+
this.session = session;
|
|
524841
|
+
this.managedQuotaWarningController.restore(session.id, managedQuotaWarningThresholdFromMetadata(session.getResumeState()?.sessionMetadata?.custom));
|
|
524842
|
+
this.setAppState({ modelFallbackAllowed: false });
|
|
524843
|
+
this.harness.setTelemetryContext({ sessionId: session.id });
|
|
524844
|
+
this.registerSessionHandlers(session);
|
|
524845
|
+
this.syncAdditionalDirs(session);
|
|
524846
|
+
await this.refreshManagedImageReaderAvailability(session);
|
|
524847
|
+
await this.authFlow.applyManagedAccountContextToSession();
|
|
524848
|
+
if (options.refreshPersonalMemory !== false) await this.personalMemoryController.refresh();
|
|
524849
|
+
|
|
524850
|
+
this.telegramSessionScope = queueScope;
|
|
524851
|
+
this.telegramChannel?.bindSessionQueue(queueScope);
|
|
524852
|
+
}
|
|
524853
|
+
|
|
524854
|
+
async withTelegramQueuePaused(work) {
|
|
524855
|
+
const boundary = this.captureTelegramQueueBoundary();
|
|
524856
|
+
const wasDeferred = this.deferUserMessages;
|
|
524857
|
+
this.deferUserMessages = true;
|
|
524858
|
+
try {
|
|
524859
|
+
return await work(boundary);
|
|
524860
|
+
}
|
|
524861
|
+
finally {
|
|
524862
|
+
this.deferUserMessages = wasDeferred;
|
|
524863
|
+
this.scheduleQueueDrain();
|
|
524864
|
+
}
|
|
524865
|
+
}
|
|
524866
|
+
|
|
524867
|
+
captureTelegramQueueBoundary() {
|
|
524868
|
+
return captureSessionQueueBoundary(join(telegramStateDir(), 'inbound-queue.jsonl')) ?? 'absent';
|
|
524869
|
+
}
|
|
524577
524870
|
};
|
|
524578
524871
|
//#endregion
|
|
524579
524872
|
//#region src/cli/run-shell.ts
|
|
@@ -525292,3 +525585,23 @@ async function logStartupFailure(operation, error) {
|
|
|
525292
525585
|
}
|
|
525293
525586
|
//#endregion
|
|
525294
525587
|
export { configureTelegramPluginLaunchMode, createBlunHarness, createBlunHostIdentity, handleMainCommand, isDirectCliEntry, main };
|
|
525588
|
+
|
|
525589
|
+
import { randomUUID as releasePromptUUID } from "node:crypto";
|
|
525590
|
+
function overlayRuntimeEnv(base, runtime) {
|
|
525591
|
+
const result = {};
|
|
525592
|
+
for (const [key, value] of Object.entries(base)) {
|
|
525593
|
+
if (value !== undefined)
|
|
525594
|
+
result[key] = value;
|
|
525595
|
+
}
|
|
525596
|
+
for (const [key, value] of Object.entries(runtime ?? {})) {
|
|
525597
|
+
// Node selects one casing on Windows; leaving both can preserve a stale value.
|
|
525598
|
+
if (process.platform === 'win32') {
|
|
525599
|
+
for (const existing of Object.keys(result)) {
|
|
525600
|
+
if (existing.toUpperCase() === key.toUpperCase())
|
|
525601
|
+
delete result[existing];
|
|
525602
|
+
}
|
|
525603
|
+
}
|
|
525604
|
+
result[key] = value;
|
|
525605
|
+
}
|
|
525606
|
+
return result;
|
|
525607
|
+
}
|