blun-king-cli 9.1.569 → 9.1.570
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
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 9.1.570
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Give each new prompt delivery a distinct hook correlation ID. Hooks for the same delivery retain the same ID; explicitly supplied IDs are preserved.
|
|
8
|
+
- Pass session-specific runtime environment values to hook subprocesses and MCP connections, including reconnects, without rewriting stored plugin configuration.
|
|
9
|
+
- Preserve native tool-result correlation across turns, including failed tools and duplicate calls.
|
|
10
|
+
- Match canonically equivalent Unicode research queries without conflating different characters. Stored sources, dates, versions and project boundaries are preserved.
|
|
11
|
+
- Keep `king --help` and `king --version` from changing Windows crash-dump registry settings.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Support negotiated partial web-search results with a visible source-failure notice. Empty or incompatible partial responses remain explicit errors. This client change does not activate or guarantee a hosted search service.
|
|
16
|
+
- Add the default-off `BLUN_EXPERIMENTAL_AGENT_SPINE_TIMELINE` transport. When explicitly enabled, it passes the actual persisted session-record path and protocol to the selected AgentSpine plugin, with a separate capability per session and a new capability after resume. This is transport support, not proof of recall or authorization by the receiving plugin.
|
|
17
|
+
|
|
18
|
+
### Preserved
|
|
19
|
+
|
|
20
|
+
- Includes the public 9.1.569 Telegram group mention routing, startup, permission persistence, research collection and crawler changes.
|
|
21
|
+
- The bundled AgentSpine implementation is unchanged from public 9.1.569. No Mnemo dependency is introduced.
|
|
22
|
+
- User profiles, permissions, conversations and plugin settings are not migrated or reset by this release.
|
|
23
|
+
|
|
24
|
+
### Updating
|
|
25
|
+
|
|
26
|
+
- Public-channel installations use `king update`.
|
|
27
|
+
- Old packages explicitly pinned to the internal update channel do not discover public releases. Close the relevant King sessions, then migrate once with `npm install -g blun-king-cli@9.1.570`.
|
|
28
|
+
- A Windows `EBUSY` rename failure means the installation did not complete. Identify the process holding the package before retrying; do not delete the package or terminate unrelated Node processes.
|
|
29
|
+
|
|
30
|
+
## 9.1.569
|
|
31
|
+
|
|
32
|
+
- Cumulative public release including Telegram group mention routing, startup and permission fixes, research collection, and crawler tooling.
|
|
33
|
+
- Published artifacts are immutable. Later changes ship under a new version.
|
package/bin/core-bootstrap.js
CHANGED
|
@@ -12,7 +12,8 @@ const CORE_LOADED_MESSAGE = 'blun-core-bootstrap-loaded';
|
|
|
12
12
|
async function runCoreBootstrap() {
|
|
13
13
|
const packageRoot = path.resolve(__dirname, '..');
|
|
14
14
|
const mainPath = path.join(packageRoot, 'blun.mjs');
|
|
15
|
-
|
|
15
|
+
const informational = process.argv.slice(2).some(arg => ['-h', '--help', '-V', '--version'].includes(arg));
|
|
16
|
+
if (!informational) installWindowsNodeCrashDump({ homeDir: process.env.BLUN_HOME });
|
|
16
17
|
const leaseResult = await acquireSharedRuntimeLease({ packageRoot });
|
|
17
18
|
if (!leaseResult.acquired) throw new Error('RUNTIME_PROTECTION_UNAVAILABLE');
|
|
18
19
|
|
package/blun.mjs
CHANGED
|
@@ -21351,7 +21351,15 @@ var init_types$14 = __esmMin((() => {}));
|
|
|
21351
21351
|
//#region ../../packages/agent-core/src/flags/registry.ts
|
|
21352
21352
|
var FLAG_DEFINITIONS;
|
|
21353
21353
|
var init_registry$1 = __esmMin((() => {
|
|
21354
|
-
FLAG_DEFINITIONS = [
|
|
21354
|
+
FLAG_DEFINITIONS = [
|
|
21355
|
+
{
|
|
21356
|
+
id: 'agent_spine_timeline',
|
|
21357
|
+
title: 'AgentSpine session transport',
|
|
21358
|
+
description: 'Pass the current native record source to the enabled AgentSpine plugin.',
|
|
21359
|
+
env: 'BLUN_EXPERIMENTAL_AGENT_SPINE_TIMELINE',
|
|
21360
|
+
default: false,
|
|
21361
|
+
surface: 'core',
|
|
21362
|
+
},{
|
|
21355
21363
|
id: "vision_reader",
|
|
21356
21364
|
title: "Vision reader",
|
|
21357
21365
|
description: "Enrich user images with observations from a configured internal service.",
|
|
@@ -231318,10 +231326,9 @@ async function runHook(command, input, options) {
|
|
|
231318
231326
|
cwd: options.cwd,
|
|
231319
231327
|
stdio: "pipe",
|
|
231320
231328
|
detached: process.platform !== "win32",
|
|
231321
|
-
env: options.env
|
|
231322
|
-
|
|
231323
|
-
|
|
231324
|
-
} : void 0
|
|
231329
|
+
env: options.env || options.runtimeEnv
|
|
231330
|
+
? overlayRuntimeEnv({ ...process.env, ...options.env }, options.runtimeEnv)
|
|
231331
|
+
: undefined
|
|
231325
231332
|
});
|
|
231326
231333
|
} catch (error) {
|
|
231327
231334
|
return allowResult({ stderr: errorMessage$11(error) });
|
|
@@ -231659,28 +231666,44 @@ async prepareToolUse(args = {}) {
|
|
|
231659
231666
|
await Promise.allSettled([...this.pendingTriggers]);
|
|
231660
231667
|
}
|
|
231661
231668
|
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
|
-
|
|
231669
|
+
const matcherValue = matcherValueText(args.matcherValue);
|
|
231670
|
+
const inputData = toHookInputData({
|
|
231671
|
+
hookEventName: event,
|
|
231672
|
+
sessionId: this.options.sessionId ?? '',
|
|
231673
|
+
cwd: this.options.cwd ?? '',
|
|
231674
|
+
...args.inputData,
|
|
231675
|
+
});
|
|
231676
|
+
const matched = this.matchingHooks(event, matcherValue);
|
|
231677
|
+
if (matched.length === 0)
|
|
231678
|
+
return [];
|
|
231679
|
+
if (event === 'UserPromptSubmit'
|
|
231680
|
+
&& !Object.hasOwn(inputData, 'event_id') && !Object.hasOwn(inputData, 'hook_event_id')) {
|
|
231681
|
+
// One logical delivery, shared by every hook; explicit IDs preserve redelivery identity.
|
|
231682
|
+
inputData['event_id'] = releasePromptUUID();
|
|
231683
|
+
}
|
|
231684
|
+
this.emitTriggered(event, matcherValue, matched.length);
|
|
231685
|
+
const startedAt = Date.now();
|
|
231686
|
+
const results = await Promise.all(matched.map(async (hook) => {
|
|
231687
|
+
let runtimeEnv;
|
|
231688
|
+
try {
|
|
231689
|
+
runtimeEnv = this.options.runtimeEnv?.(hook);
|
|
231690
|
+
}
|
|
231691
|
+
catch {
|
|
231692
|
+
// A resolver error may contain private values. Other hooks still run.
|
|
231693
|
+
return { action: 'allow', stderr: 'Hook runtime environment is unavailable.' };
|
|
231694
|
+
}
|
|
231695
|
+
return runHook(hook.command, inputData, {
|
|
231696
|
+
timeout: hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS,
|
|
231697
|
+
cwd: hook.cwd ?? (this.options.cwd === '' ? undefined : this.options.cwd),
|
|
231698
|
+
env: hook.env,
|
|
231699
|
+
runtimeEnv,
|
|
231700
|
+
signal: args.signal,
|
|
231701
|
+
});
|
|
231702
|
+
}));
|
|
231703
|
+
const { action, reason } = aggregateResults(event, results);
|
|
231704
|
+
this.emitResolved(event, matcherValue, action, reason, Date.now() - startedAt);
|
|
231705
|
+
return results;
|
|
231706
|
+
}
|
|
231684
231707
|
matchingHooks(event, matcherValue) {
|
|
231685
231708
|
const seen = /* @__PURE__ */ new Set();
|
|
231686
231709
|
const matched = [];
|
|
@@ -236096,6 +236119,13 @@ var init_records = __esmMin((() => {
|
|
|
236096
236119
|
persistence;
|
|
236097
236120
|
_restoring = null;
|
|
236098
236121
|
metadataInitialized = false;
|
|
236122
|
+
persistedProtocolVersion;
|
|
236123
|
+
get fileSource() {
|
|
236124
|
+
if (!(this.persistence instanceof FileSystemAgentRecordPersistence)
|
|
236125
|
+
|| this.persistedProtocolVersion === undefined)
|
|
236126
|
+
return undefined;
|
|
236127
|
+
return { path: this.persistence.filePath, protocolVersion: this.persistedProtocolVersion };
|
|
236128
|
+
}
|
|
236099
236129
|
resumeCheckpointRestored = false;
|
|
236100
236130
|
messageTimes = new WeakMap();
|
|
236101
236131
|
constructor(agent, persistence) {
|
|
@@ -236119,8 +236149,15 @@ var init_records = __esmMin((() => {
|
|
|
236119
236149
|
created_at: Date.now()
|
|
236120
236150
|
});
|
|
236121
236151
|
this.metadataInitialized = true;
|
|
236122
|
-
|
|
236123
|
-
|
|
236152
|
+
|
|
236153
|
+
this.persistedProtocolVersion = "1.4";
|
|
236154
|
+
}
|
|
236155
|
+
if (stamped.type === 'metadata') {
|
|
236156
|
+
if (!this.metadataInitialized) {
|
|
236157
|
+
this.persistedProtocolVersion = stamped.protocol_version;
|
|
236158
|
+
}
|
|
236159
|
+
this.metadataInitialized = true;
|
|
236160
|
+
}
|
|
236124
236161
|
this.persistence?.append(stamped);
|
|
236125
236162
|
}
|
|
236126
236163
|
restore(record) {
|
|
@@ -236134,7 +236171,8 @@ var init_records = __esmMin((() => {
|
|
|
236134
236171
|
}
|
|
236135
236172
|
}
|
|
236136
236173
|
async replay(options = {}) {
|
|
236137
|
-
|
|
236174
|
+
this.persistedProtocolVersion = undefined;
|
|
236175
|
+
if (!this.persistence) throw new Error("No persistence provided for AgentRecords");
|
|
236138
236176
|
this.resumeCheckpointRestored = false;
|
|
236139
236177
|
const rewriteMigratedRecords = options.rewriteMigratedRecords ?? true;
|
|
236140
236178
|
if (rewriteMigratedRecords && await this.tryReplayResumeCheckpoint()) return {};
|
|
@@ -236152,6 +236190,7 @@ var init_records = __esmMin((() => {
|
|
|
236152
236190
|
hasMetadata = true;
|
|
236153
236191
|
this.metadataInitialized = true;
|
|
236154
236192
|
const readVersion = record.protocol_version;
|
|
236193
|
+
this.persistedProtocolVersion = readVersion;
|
|
236155
236194
|
if (isNewerWireVersion(readVersion)) {
|
|
236156
236195
|
warning = `Session wire protocol version ${readVersion} is newer than the current version 1.4. Records will be replayed without migration.`;
|
|
236157
236196
|
shouldRewrite = false;
|
|
@@ -236174,6 +236213,7 @@ var init_records = __esmMin((() => {
|
|
|
236174
236213
|
if (completed && shouldRewrite && replayedRecords !== void 0) {
|
|
236175
236214
|
this.persistence.rewrite(replayedRecords);
|
|
236176
236215
|
await this.persistence.flush();
|
|
236216
|
+
this.persistedProtocolVersion = "1.4";
|
|
236177
236217
|
}
|
|
236178
236218
|
if (completed && this.persistence instanceof FileSystemAgentRecordPersistence) this.persistence.persistedRecordCount = replayedRecordCount;
|
|
236179
236219
|
if (completed && this.agent.blobStore !== void 0) for (const msg of this.agent.context.history) await this.agent.blobStore.rehydrateParts(msg.content);
|
|
@@ -236184,8 +236224,17 @@ var init_records = __esmMin((() => {
|
|
|
236184
236224
|
if (this.agent.replayBuilder.options.range !== void 0) return false;
|
|
236185
236225
|
const checkpoint = await loadResumeCheckpoint({ wirePath: this.persistence.filePath });
|
|
236186
236226
|
if (!checkpoint.ok) return false;
|
|
236227
|
+
let sourceProtocol;
|
|
236228
|
+
for await (const metadata of this.persistence.read()) {
|
|
236229
|
+
if (metadata.type !== 'metadata' || metadata.protocol_version !== "1.4") return false;
|
|
236230
|
+
sourceProtocol = metadata.protocol_version;
|
|
236231
|
+
break;
|
|
236232
|
+
}
|
|
236233
|
+
if (sourceProtocol === undefined) return false;
|
|
236234
|
+
|
|
236187
236235
|
if (!restoreAgentResumeSnapshot(this.agent, checkpoint.snapshot)) return false;
|
|
236188
236236
|
this.metadataInitialized = true;
|
|
236237
|
+
this.persistedProtocolVersion = sourceProtocol;
|
|
236189
236238
|
let tailRecordCount = 0;
|
|
236190
236239
|
for await (const record of this.persistence.readFrom(checkpoint.offset, checkpoint.recordCount)) {
|
|
236191
236240
|
this.restore(record);
|
|
@@ -265678,34 +265727,52 @@ var init_web_search = __esmMin((() => {
|
|
|
265678
265727
|
execute: (ctx) => this.execution(args, ctx)
|
|
265679
265728
|
};
|
|
265680
265729
|
}
|
|
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
|
-
|
|
265730
|
+
async execution(args, { toolCallId, }) {
|
|
265731
|
+
try {
|
|
265732
|
+
let failedSources = 0;
|
|
265733
|
+
const opts = { toolCallId, onNotice: notice => {
|
|
265734
|
+
if (notice.code === 'partial-sources' && Number.isInteger(notice.failedSources)
|
|
265735
|
+
&& notice.failedSources > 0 && notice.failedSources <= 128) {
|
|
265736
|
+
failedSources = Math.max(failedSources, notice.failedSources);
|
|
265737
|
+
}
|
|
265738
|
+
} };
|
|
265739
|
+
const results = await this.provider.search(args.query, opts);
|
|
265740
|
+
const builder = new ToolResultBuilder({ maxLineLength: null });
|
|
265741
|
+
if (failedSources > 0) {
|
|
265742
|
+
if (results.length === 0)
|
|
265743
|
+
throw new Error('Search sources failed without usable results.');
|
|
265744
|
+
builder.write(`Some search sources failed (${failedSources}). Results below are partial, not a complete search.\n\n`);
|
|
265745
|
+
}
|
|
265746
|
+
if (results.length === 0) {
|
|
265747
|
+
builder.write('No search results found.');
|
|
265748
|
+
return builder.ok();
|
|
265749
|
+
}
|
|
265750
|
+
let first = true;
|
|
265751
|
+
for (const result of results) {
|
|
265752
|
+
if (!first)
|
|
265753
|
+
builder.write('---\n\n');
|
|
265754
|
+
first = false;
|
|
265755
|
+
builder.write(`Title: ${result.title}\n`);
|
|
265756
|
+
if (result.siteName)
|
|
265757
|
+
builder.write(`Site: ${result.siteName}\n`);
|
|
265758
|
+
if (result.date)
|
|
265759
|
+
builder.write(`Date: ${result.date}\n`);
|
|
265760
|
+
builder.write(`URL: ${result.url}\n`);
|
|
265761
|
+
builder.write(`Snippet: ${result.snippet}\n\n`);
|
|
265762
|
+
}
|
|
265763
|
+
// Keep the citation reminder next to the data (not just in the static tool
|
|
265764
|
+
// description), so it is present on every search. Cite the page actually
|
|
265765
|
+
// relied on — after a FetchURL follow-up, that is the fetched page.
|
|
265766
|
+
builder.write('When you rely on a result in your answer, cite it inline as a markdown link, e.g. [title](url).');
|
|
265767
|
+
return builder.ok();
|
|
265768
|
+
}
|
|
265769
|
+
catch (error) {
|
|
265770
|
+
return {
|
|
265771
|
+
isError: true,
|
|
265772
|
+
output: classifySearchError(error),
|
|
265773
|
+
};
|
|
265774
|
+
}
|
|
265775
|
+
}
|
|
265709
265776
|
};
|
|
265710
265777
|
}));
|
|
265711
265778
|
//#endregion
|
|
@@ -298151,13 +298218,11 @@ function resolveStdioCwd(configCwd, defaultCwd) {
|
|
|
298151
298218
|
if (defaultCwd !== void 0 && !isAbsolute$2(configCwd)) return resolve$2(defaultCwd, configCwd);
|
|
298152
298219
|
return configCwd;
|
|
298153
298220
|
}
|
|
298154
|
-
function mergeStdioEnv(configEnv, parentEnv = process.env) {
|
|
298155
|
-
|
|
298156
|
-
|
|
298157
|
-
|
|
298158
|
-
|
|
298159
|
-
reconcileChildNoProxy(merged, configEnv);
|
|
298160
|
-
return merged;
|
|
298221
|
+
function mergeStdioEnv(configEnv, parentEnv = process.env, runtimeEnv) {
|
|
298222
|
+
const merged = overlayRuntimeEnv({ ...parentEnv, ...configEnv }, runtimeEnv);
|
|
298223
|
+
Object.assign(merged, proxyEnvForChild(merged));
|
|
298224
|
+
reconcileChildNoProxy(merged, overlayRuntimeEnv(configEnv ?? {}, runtimeEnv));
|
|
298225
|
+
return merged;
|
|
298161
298226
|
}
|
|
298162
298227
|
var STDERR_BUFFER_CAPACITY, StdioMcpClient, BoundedTail;
|
|
298163
298228
|
var init_client_stdio = __esmMin((() => {
|
|
@@ -298187,7 +298252,7 @@ var init_client_stdio = __esmMin((() => {
|
|
|
298187
298252
|
this.transport = new StdioClientTransport({
|
|
298188
298253
|
command: config.command,
|
|
298189
298254
|
args: config.args,
|
|
298190
|
-
env: mergeStdioEnv(config.env, config.inheritEnv === false ? {} : process.env),
|
|
298255
|
+
env: mergeStdioEnv(config.env, config.inheritEnv === false ? {} : process.env, options.runtimeEnv),
|
|
298191
298256
|
cwd: resolveStdioCwd(config.cwd, options.defaultCwd),
|
|
298192
298257
|
stderr: "pipe"
|
|
298193
298258
|
});
|
|
@@ -298689,22 +298754,32 @@ var init_connection_manager = __esmMin((() => {
|
|
|
298689
298754
|
return entry.attemptId;
|
|
298690
298755
|
}
|
|
298691
298756
|
createClient(config, name) {
|
|
298692
|
-
|
|
298693
|
-
|
|
298694
|
-
|
|
298695
|
-
|
|
298696
|
-
|
|
298697
|
-
|
|
298698
|
-
|
|
298699
|
-
|
|
298700
|
-
|
|
298701
|
-
|
|
298702
|
-
|
|
298703
|
-
|
|
298704
|
-
|
|
298705
|
-
|
|
298706
|
-
|
|
298707
|
-
|
|
298757
|
+
const toolCallTimeoutMs = config.toolTimeoutMs;
|
|
298758
|
+
if (config.transport === 'stdio') {
|
|
298759
|
+
let runtimeEnv;
|
|
298760
|
+
try {
|
|
298761
|
+
runtimeEnv = this.options.stdioRuntimeEnv?.(name, config);
|
|
298762
|
+
}
|
|
298763
|
+
catch {
|
|
298764
|
+
throw new Error('MCP runtime environment is unavailable.');
|
|
298765
|
+
}
|
|
298766
|
+
return new StdioMcpClient(config, {
|
|
298767
|
+
toolCallTimeoutMs, defaultCwd: this.options.stdioCwd, runtimeEnv,
|
|
298768
|
+
});
|
|
298769
|
+
}
|
|
298770
|
+
if (config.transport === 'sse') {
|
|
298771
|
+
return new SseMcpClient(config, {
|
|
298772
|
+
toolCallTimeoutMs,
|
|
298773
|
+
envLookup: this.options.envLookup,
|
|
298774
|
+
oauthProvider: this.resolveOAuthProvider(config, name),
|
|
298775
|
+
});
|
|
298776
|
+
}
|
|
298777
|
+
return new HttpMcpClient(config, {
|
|
298778
|
+
toolCallTimeoutMs,
|
|
298779
|
+
envLookup: this.options.envLookup,
|
|
298780
|
+
oauthProvider: this.resolveOAuthProvider(config, name),
|
|
298781
|
+
});
|
|
298782
|
+
}
|
|
298708
298783
|
resolveOAuthProvider(config, name) {
|
|
298709
298784
|
const oauthService = this.oauthService;
|
|
298710
298785
|
if (oauthService === void 0) return void 0;
|
|
@@ -299417,6 +299492,43 @@ var init_lsp = __esmMin((() => {
|
|
|
299417
299492
|
init_manager$1();
|
|
299418
299493
|
}));
|
|
299419
299494
|
//#endregion
|
|
299495
|
+
|
|
299496
|
+
import { randomBytes as releaseRecordRandomBytes } from "node:crypto";
|
|
299497
|
+
const EMPTY_RUNTIME = Object.freeze({
|
|
299498
|
+
AGENTSPINE_KING_TIMELINE_SOURCE: '',
|
|
299499
|
+
AGENTSPINE_KING_WIRE_PROTOCOL_VERSION: '',
|
|
299500
|
+
AGENTSPINE_TIMELINE_TRANSPORT_SESSION_ID: '',
|
|
299501
|
+
AGENTSPINE_TIMELINE_SESSION_CAPABILITY: '',
|
|
299502
|
+
});
|
|
299503
|
+
class AgentSpineSessionRuntime {
|
|
299504
|
+
#env = EMPTY_RUNTIME;
|
|
299505
|
+
#hooks;
|
|
299506
|
+
#servers;
|
|
299507
|
+
constructor(recipients) {
|
|
299508
|
+
this.#hooks = new Set(recipients?.hooks);
|
|
299509
|
+
this.#servers = new Map(Object.entries(recipients?.mcpServers ?? {}));
|
|
299510
|
+
}
|
|
299511
|
+
get hasRecipients() {
|
|
299512
|
+
return this.#hooks.size > 0 || this.#servers.size > 0;
|
|
299513
|
+
}
|
|
299514
|
+
bind(sessionId, source) {
|
|
299515
|
+
this.#env = EMPTY_RUNTIME;
|
|
299516
|
+
if (!sessionId || !/^[A-Za-z0-9][A-Za-z0-9:_.@/-]{0,127}$/.test(sessionId) || !source)
|
|
299517
|
+
return;
|
|
299518
|
+
this.#env = Object.freeze({
|
|
299519
|
+
AGENTSPINE_KING_TIMELINE_SOURCE: source.path,
|
|
299520
|
+
AGENTSPINE_KING_WIRE_PROTOCOL_VERSION: source.protocolVersion,
|
|
299521
|
+
AGENTSPINE_TIMELINE_TRANSPORT_SESSION_ID: sessionId,
|
|
299522
|
+
AGENTSPINE_TIMELINE_SESSION_CAPABILITY: `astc_${releaseRecordRandomBytes(32).toString('base64url')}`,
|
|
299523
|
+
});
|
|
299524
|
+
}
|
|
299525
|
+
forHook(hook) {
|
|
299526
|
+
return this.#hooks.has(hook) ? this.#env : EMPTY_RUNTIME;
|
|
299527
|
+
}
|
|
299528
|
+
forMcp(name, config) {
|
|
299529
|
+
return config.transport === 'stdio' && this.#servers.get(name) === config ? this.#env : EMPTY_RUNTIME;
|
|
299530
|
+
}
|
|
299531
|
+
}
|
|
299420
299532
|
//#region ../../packages/agent-core/src/session/index.ts
|
|
299421
299533
|
async function waitForSettlementOrTimeout(promise, timeoutMs) {
|
|
299422
299534
|
let timeout;
|
|
@@ -299497,6 +299609,16 @@ var init_session$1 = __esmMin((() => {
|
|
|
299497
299609
|
writeMetadataPromise = Promise.resolve();
|
|
299498
299610
|
agentsMdWarning;
|
|
299499
299611
|
accountMemoryContext = "";
|
|
299612
|
+
agentSpineRuntime;
|
|
299613
|
+
mcpLoadStarted = false;
|
|
299614
|
+
startMcpServers() {
|
|
299615
|
+
if (this.mcpLoadStarted)
|
|
299616
|
+
return;
|
|
299617
|
+
this.mcpLoadStarted = true;
|
|
299618
|
+
void this.loadMcpServers().catch((error) => {
|
|
299619
|
+
this.emitInitialMcpLoadError(error);
|
|
299620
|
+
});
|
|
299621
|
+
}
|
|
299500
299622
|
constructor(options) {
|
|
299501
299623
|
this.options = options;
|
|
299502
299624
|
this.logHandle = options.id === void 0 ? void 0 : getRootLogger().attachSession({
|
|
@@ -299506,6 +299628,8 @@ var init_session$1 = __esmMin((() => {
|
|
|
299506
299628
|
this.log = this.logHandle?.logger ?? (options.id === void 0 ? log : log.createChild({ sessionId: options.id }));
|
|
299507
299629
|
this.rpc = options.rpc;
|
|
299508
299630
|
this.experimentalFlags = options.experimentalFlags ?? new FlagResolver();
|
|
299631
|
+
this.agentSpineRuntime = this.experimentalFlags.enabled('agent_spine_timeline')
|
|
299632
|
+
? new AgentSpineSessionRuntime(options.agentSpineRuntime) : undefined;
|
|
299509
299633
|
this.visionReader = createManagedMediaVisionReader(options.toolServices?.media) ?? (this.experimentalFlags.enabled("vision_reader") ? createVisionReader(options.config?.services?.visionReader) : void 0);
|
|
299510
299634
|
this.hookEngine = new HookEngine(options.hooks, {
|
|
299511
299635
|
cwd: options.kaos.getcwd(),
|
|
@@ -299534,8 +299658,13 @@ var init_session$1 = __esmMin((() => {
|
|
|
299534
299658
|
code: "HOST_HOOK_WARNING",
|
|
299535
299659
|
message
|
|
299536
299660
|
});
|
|
299537
|
-
}
|
|
299538
|
-
|
|
299661
|
+
},
|
|
299662
|
+
runtimeEnv: (hook) => {
|
|
299663
|
+
const hostEnv = options.hookRuntimeEnv?.(hook);
|
|
299664
|
+
const timelineEnv = this.agentSpineRuntime?.forHook(hook);
|
|
299665
|
+
return timelineEnv === undefined ? hostEnv : { ...hostEnv, ...timelineEnv };
|
|
299666
|
+
}
|
|
299667
|
+
});
|
|
299539
299668
|
this.telemetry = options.telemetry ?? noopTelemetryClient;
|
|
299540
299669
|
this.toolKaos = options.kaos;
|
|
299541
299670
|
this.persistenceKaos = options.persistenceKaos ?? options.kaos;
|
|
@@ -299546,8 +299675,13 @@ var init_session$1 = __esmMin((() => {
|
|
|
299546
299675
|
this.mcp = new McpConnectionManager({
|
|
299547
299676
|
oauthService: new McpOAuthService({ blunHomeDir: resolveSharedBlunHome(options.blunHomeDir) }),
|
|
299548
299677
|
log: this.log,
|
|
299549
|
-
stdioCwd: options.kaos.getcwd()
|
|
299550
|
-
|
|
299678
|
+
stdioCwd: options.kaos.getcwd(),
|
|
299679
|
+
stdioRuntimeEnv: (name, config) => {
|
|
299680
|
+
const hostEnv = options.mcpRuntimeEnv?.(name, config);
|
|
299681
|
+
const timelineEnv = this.agentSpineRuntime?.forMcp(name, config);
|
|
299682
|
+
return timelineEnv === undefined ? hostEnv : { ...hostEnv, ...timelineEnv };
|
|
299683
|
+
}
|
|
299684
|
+
});
|
|
299551
299685
|
this.lsp = options.lspServers === void 0 || Object.keys(options.lspServers).length === 0 ? void 0 : new LspManager({
|
|
299552
299686
|
cwd: options.kaos.getcwd(),
|
|
299553
299687
|
servers: options.lspServers
|
|
@@ -299560,9 +299694,7 @@ var init_session$1 = __esmMin((() => {
|
|
|
299560
299694
|
}).then(() => {
|
|
299561
299695
|
this.refreshAgentBuiltinTools();
|
|
299562
299696
|
});
|
|
299563
|
-
this.
|
|
299564
|
-
this.emitInitialMcpLoadError(error);
|
|
299565
|
-
});
|
|
299697
|
+
if (!this.agentSpineRuntime?.hasRecipients) this.startMcpServers();
|
|
299566
299698
|
}
|
|
299567
299699
|
setToolKaos(kaos) {
|
|
299568
299700
|
this.toolKaos = kaos;
|
|
@@ -300208,7 +300340,18 @@ var init_session$1 = __esmMin((() => {
|
|
|
300208
300340
|
});
|
|
300209
300341
|
}
|
|
300210
300342
|
async triggerSessionStart(source) {
|
|
300211
|
-
|
|
300343
|
+
if (this.agentSpineRuntime?.hasRecipients && !this.mcpLoadStarted) {
|
|
300344
|
+
try {
|
|
300345
|
+
const main = this.getReadyAgent('main');
|
|
300346
|
+
await main?.records.flush();
|
|
300347
|
+
this.agentSpineRuntime.bind(this.options.id, main?.records.fileSource);
|
|
300348
|
+
}
|
|
300349
|
+
catch {
|
|
300350
|
+
this.log.warn('Native session record source is unavailable.');
|
|
300351
|
+
}
|
|
300352
|
+
this.startMcpServers();
|
|
300353
|
+
}
|
|
300354
|
+
const results = await this.hookEngine.trigger("SessionStart", {
|
|
300212
300355
|
matcherValue: source,
|
|
300213
300356
|
inputData: { source }
|
|
300214
300357
|
});
|
|
@@ -315095,43 +315238,80 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315095
315238
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
315096
315239
|
}
|
|
315097
315240
|
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
|
-
|
|
315241
|
+
const body = { text_query: query };
|
|
315242
|
+
const bodyJson = JSON.stringify(body);
|
|
315243
|
+
const toolCallId = options?.toolCallId;
|
|
315244
|
+
const supportsNotices = typeof options?.onNotice === 'function';
|
|
315245
|
+
const response = await this.post(bodyJson, toolCallId, supportsNotices);
|
|
315246
|
+
if (response.status === 401) {
|
|
315247
|
+
const detail = await safeReadText(response);
|
|
315248
|
+
throw new Error(`Blun search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
|
|
315249
|
+
}
|
|
315250
|
+
if (response.status !== 200) {
|
|
315251
|
+
const detail = await safeReadText(response);
|
|
315252
|
+
throw new Error(`Blun search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
315253
|
+
}
|
|
315254
|
+
const json = (await response.json());
|
|
315255
|
+
const raw = Array.isArray(json.search_results) ? json.search_results : [];
|
|
315256
|
+
const status = json.search_status;
|
|
315257
|
+
if (status !== undefined) {
|
|
315258
|
+
if (!status || typeof status !== 'object' || !Array.isArray(json.search_results)
|
|
315259
|
+
|| !Number.isInteger(status.failed_sources) || typeof status.failed_sources !== 'number'
|
|
315260
|
+
|| status.failed_sources < 0 || status.failed_sources > 128
|
|
315261
|
+
|| (status.state !== 'complete' && status.state !== 'partial')
|
|
315262
|
+
|| (status.state === 'complete' && status.failed_sources !== 0)
|
|
315263
|
+
|| (status.state === 'partial' && (status.failed_sources === 0 || raw.length === 0))) {
|
|
315264
|
+
throw new Error('Invalid search status response.');
|
|
315265
|
+
}
|
|
315266
|
+
if (!raw.every(row => {
|
|
315267
|
+
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315268
|
+
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315269
|
+
return false;
|
|
315270
|
+
try {
|
|
315271
|
+
const url = new URL(row.url);
|
|
315272
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315273
|
+
}
|
|
315274
|
+
catch {
|
|
315275
|
+
return false;
|
|
315276
|
+
}
|
|
315277
|
+
}))
|
|
315278
|
+
throw new Error('Invalid search results response.');
|
|
315279
|
+
if (status.state === 'partial') {
|
|
315280
|
+
if (!supportsNotices)
|
|
315281
|
+
throw new Error('Partial search results require a notice consumer.');
|
|
315282
|
+
options?.onNotice?.({ code: 'partial-sources', failedSources: status.failed_sources });
|
|
315283
|
+
}
|
|
315284
|
+
}
|
|
315285
|
+
return raw.map((r) => {
|
|
315286
|
+
const out = {
|
|
315287
|
+
title: r.title ?? '',
|
|
315288
|
+
url: r.url ?? '',
|
|
315289
|
+
snippet: r.snippet ?? '',
|
|
315290
|
+
};
|
|
315291
|
+
if (typeof r.date === 'string' && r.date.length > 0)
|
|
315292
|
+
out.date = r.date;
|
|
315293
|
+
if (typeof r.site_name === 'string' && r.site_name.length > 0)
|
|
315294
|
+
out.siteName = r.site_name;
|
|
315295
|
+
return out;
|
|
315296
|
+
});
|
|
315297
|
+
}
|
|
315298
|
+
async post(bodyJson, toolCallId, supportsNotices) {
|
|
315299
|
+
const accessToken = await this.resolveApiKey();
|
|
315300
|
+
return this.fetchImpl(this.baseUrl, {
|
|
315301
|
+
method: 'POST',
|
|
315302
|
+
headers: {
|
|
315303
|
+
...this.defaultHeaders,
|
|
315304
|
+
Authorization: `Bearer ${accessToken}`,
|
|
315305
|
+
'Content-Type': 'application/json',
|
|
315306
|
+
...(toolCallId !== undefined && toolCallId.length > 0
|
|
315307
|
+
? { 'X-Msh-Tool-Call-Id': toolCallId }
|
|
315308
|
+
: {}),
|
|
315309
|
+
...this.customHeaders,
|
|
315310
|
+
...(supportsNotices ? { 'X-BLUN-Search-Contract': 'partial-v1' } : {}),
|
|
315311
|
+
},
|
|
315312
|
+
body: bodyJson,
|
|
315313
|
+
});
|
|
315314
|
+
}
|
|
315135
315315
|
async resolveApiKey() {
|
|
315136
315316
|
if (this.tokenProvider !== void 0) try {
|
|
315137
315317
|
return await this.tokenProvider.getAccessToken();
|
|
@@ -318903,6 +319083,7 @@ var init_core_impl = __esmMin((() => {
|
|
|
318903
319083
|
const pluginSessionStarts = this.plugins.enabledSessionStarts();
|
|
318904
319084
|
const pluginCommands = await this.plugins.enabledCommands();
|
|
318905
319085
|
const mcpConfig = this.mergePluginMcpConfig(withCallerMcp);
|
|
319086
|
+
const pluginHooks = this.plugins.enabledHooks();
|
|
318906
319087
|
const runtime = await this.resolveRuntime(config);
|
|
318907
319088
|
const session = new Session$1({
|
|
318908
319089
|
kaos: parentKaos.withCwd(workDir),
|
|
@@ -318915,7 +319096,8 @@ var init_core_impl = __esmMin((() => {
|
|
|
318915
319096
|
rpc: proxyWithExtraPayload(await this.sdk, { sessionId: summary.id }),
|
|
318916
319097
|
providerManager: this.resolveProviderManager(summary.id),
|
|
318917
319098
|
background: config.background,
|
|
318918
|
-
hooks: [...config.hooks ?? [], ...
|
|
319099
|
+
hooks: [...(config.hooks ?? []), ...pluginHooks],
|
|
319100
|
+
agentSpineRuntime: this.agentSpineRuntimeRecipients(pluginHooks, mcpConfig),
|
|
318919
319101
|
permissionRules: config.permission?.rules,
|
|
318920
319102
|
skills: this.resolveSessionSkillConfig(config),
|
|
318921
319103
|
mcpConfig,
|
|
@@ -318998,6 +319180,7 @@ var init_core_impl = __esmMin((() => {
|
|
|
318998
319180
|
const pluginSessionStarts = this.plugins.enabledSessionStarts();
|
|
318999
319181
|
const pluginCommands = await this.plugins.enabledCommands();
|
|
319000
319182
|
const mcpConfig = this.mergePluginMcpConfig(withCallerMcp);
|
|
319183
|
+
const pluginHooks = this.plugins.enabledHooks();
|
|
319001
319184
|
const runtime = await this.resolveRuntime(config);
|
|
319002
319185
|
const parentKaos = parentKaosForRead;
|
|
319003
319186
|
const persistenceKaos = overrides.persistenceKaos ?? parentKaos;
|
|
@@ -319012,7 +319195,8 @@ var init_core_impl = __esmMin((() => {
|
|
|
319012
319195
|
rpc: proxyWithExtraPayload(await this.sdk, { sessionId: summary.id }),
|
|
319013
319196
|
providerManager: this.resolveProviderManager(summary.id),
|
|
319014
319197
|
background: config.background,
|
|
319015
|
-
hooks: [...config.hooks ?? [], ...
|
|
319198
|
+
hooks: [...(config.hooks ?? []), ...pluginHooks],
|
|
319199
|
+
agentSpineRuntime: this.agentSpineRuntimeRecipients(pluginHooks, mcpConfig),
|
|
319016
319200
|
permissionRules: config.permission?.rules,
|
|
319017
319201
|
skills: this.resolveSessionSkillConfig(config),
|
|
319018
319202
|
mcpConfig,
|
|
@@ -319468,6 +319652,21 @@ var init_core_impl = __esmMin((() => {
|
|
|
319468
319652
|
}
|
|
319469
319653
|
};
|
|
319470
319654
|
}
|
|
319655
|
+
agentSpineRuntimeRecipients(pluginHooks, mcpConfig) {
|
|
319656
|
+
if (!this.experimentalFlags.enabled('agent_spine_timeline'))
|
|
319657
|
+
return undefined;
|
|
319658
|
+
const plugin = this.plugins.get('agent-spine');
|
|
319659
|
+
if (plugin?.state !== 'ok' || !plugin.enabled)
|
|
319660
|
+
return undefined;
|
|
319661
|
+
// These objects came from PluginManager, not caller hooks or session metadata.
|
|
319662
|
+
const hooks = pluginHooks.filter(hook => hook.env?.['BLUN_PLUGIN_ROOT'] === plugin.root);
|
|
319663
|
+
const mcpServers = Object.fromEntries(Object.entries(mcpConfig?.servers ?? {}).filter(([name, config]) => {
|
|
319664
|
+
const origin = mcpConfig?.origins?.[name];
|
|
319665
|
+
return origin?.source === 'plugin' && origin.pluginId === plugin.id && origin.pluginEnabled
|
|
319666
|
+
&& config.transport === 'stdio' && config.enabled !== false;
|
|
319667
|
+
}));
|
|
319668
|
+
return hooks.length > 0 || Object.keys(mcpServers).length > 0 ? { hooks, mcpServers } : undefined;
|
|
319669
|
+
}
|
|
319471
319670
|
async resolveCurrentMcpConfig(session) {
|
|
319472
319671
|
const snapshot = session.mcpConfigSnapshot();
|
|
319473
319672
|
const callerServers = Object.fromEntries(Object.entries(snapshot?.servers ?? {}).filter(([name]) => snapshot?.origins?.[name]?.source === "session"));
|
|
@@ -525292,3 +525491,23 @@ async function logStartupFailure(operation, error) {
|
|
|
525292
525491
|
}
|
|
525293
525492
|
//#endregion
|
|
525294
525493
|
export { configureTelegramPluginLaunchMode, createBlunHarness, createBlunHostIdentity, handleMainCommand, isDirectCliEntry, main };
|
|
525494
|
+
|
|
525495
|
+
import { randomUUID as releasePromptUUID } from "node:crypto";
|
|
525496
|
+
function overlayRuntimeEnv(base, runtime) {
|
|
525497
|
+
const result = {};
|
|
525498
|
+
for (const [key, value] of Object.entries(base)) {
|
|
525499
|
+
if (value !== undefined)
|
|
525500
|
+
result[key] = value;
|
|
525501
|
+
}
|
|
525502
|
+
for (const [key, value] of Object.entries(runtime ?? {})) {
|
|
525503
|
+
// Node selects one casing on Windows; leaving both can preserve a stale value.
|
|
525504
|
+
if (process.platform === 'win32') {
|
|
525505
|
+
for (const existing of Object.keys(result)) {
|
|
525506
|
+
if (existing.toUpperCase() === key.toUpperCase())
|
|
525507
|
+
delete result[existing];
|
|
525508
|
+
}
|
|
525509
|
+
}
|
|
525510
|
+
result[key] = value;
|
|
525511
|
+
}
|
|
525512
|
+
return result;
|
|
525513
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blun-king-cli",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.570",
|
|
4
4
|
"description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"agent-spine-plugin/.codex-plugin/",
|
|
40
40
|
"agent-spine-plugin/.mcp.json",
|
|
41
41
|
"telegram-plugin/",
|
|
42
|
-
"LIESMICH.txt"
|
|
42
|
+
"LIESMICH.txt",
|
|
43
|
+
"CHANGELOG.md"
|
|
43
44
|
],
|
|
44
45
|
"keywords": [
|
|
45
46
|
"blun",
|
|
@@ -151,7 +151,7 @@ function save({ home, project, report }) {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
const terms = text => [...new Set(text.toLocaleLowerCase('en').match(/[\p{L}\p{N}]{2,}/gu) || [])];
|
|
154
|
+
const terms = text => [...new Set(text.normalize('NFC').toLocaleLowerCase('en').match(/[\p{L}\p{N}]{2,}/gu) || [])];
|
|
155
155
|
function query({ home, project, query: question, limit = 5, maxChars = 8000, offset = 0, expectedCollectionSha256 }) {
|
|
156
156
|
const output = { schema: 'blun.research-recall/v1', availability: 'unavailable', matches: [], diagnostics: [],
|
|
157
157
|
sourceVerified: false, retrieval: 'lexical-artifact-search', bytesRead: 0, limited: false,
|