blun-king-cli 9.1.536 → 9.1.561
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LIESMICH.txt +13 -869
- package/README.md +41 -833
- package/bin/assistant-message-offload-policy.cjs +3 -1
- package/bin/compaction-transaction-policy.cjs +122 -0
- package/bin/context-performance-policy.cjs +2 -5
- package/bin/context-pressure-policy.cjs +20 -0
- package/bin/cron-run-output.cjs +45 -0
- package/bin/cron-run-store.cjs +145 -0
- package/bin/default-model-output-budget-policy.cjs +28 -0
- package/bin/durable-task-resume-policy.cjs +130 -0
- package/bin/durable-task-resume-runtime.cjs +117 -0
- package/bin/durable-task-resume-store.cjs +88 -0
- package/bin/editable-tool-approval-policy.cjs +540 -0
- package/bin/editable-tool-approval-runtime.cjs +99 -0
- package/bin/file-observation-policy.cjs +133 -0
- package/bin/html-to-research-markdown.cjs +146 -0
- package/bin/launcher-runtime.js +0 -1
- package/bin/micro-compaction-policy.cjs +64 -0
- package/bin/mnemo-connect-heartbeat.cjs +1 -3
- package/bin/programmatic-tool-runtime.mjs +330 -4
- package/bin/read-continuation-policy.cjs +36 -5
- package/bin/retry-checkpoint-policy.cjs +13 -0
- package/bin/scoped-cron-run-policy.cjs +358 -0
- package/bin/session-checkpoint-policy.cjs +25 -0
- package/bin/startup-preferences.cjs +4 -3
- package/bin/structured-agent-swarm-output.cjs +325 -0
- package/bin/subagent-context-fork-policy.cjs +155 -0
- package/bin/subagent-skill-policy.cjs +204 -0
- package/bin/telegram-approval-relay.cjs +2 -1
- package/bin/tool-file-persistence.cjs +141 -0
- package/bin/tool-result-offload-policy.cjs +25 -33
- package/bin/turn-thinking-policy.cjs +2 -26
- package/bin/update-notice.js +30 -18
- package/bin/user-message-offload-policy.cjs +3 -1
- package/blun.mjs +1564 -640
- package/codebase-index/README.md +12 -0
- package/codebase-index/codebase_index.py +129 -18
- package/package.json +23 -58
- package/telegram-plugin/bin/telegram-mnemo-capture.cjs +1 -3
- package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
- package/telegram-plugin/dist/bridge.mjs +8 -1
- package/CHANGELOG.md +0 -321
- package/agent-spine-plugin/CHANGELOG.md +0 -406
- package/agent-spine-plugin/CONTRIBUTING.md +0 -52
- package/agent-spine-plugin/README.md +0 -344
- package/agent-spine-plugin/SECURITY.md +0 -47
- package/agent-spine-plugin/docs/acceptance.md +0 -61
- package/agent-spine-plugin/docs/architecture.md +0 -183
- package/agent-spine-plugin/docs/attention.md +0 -121
- package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
- package/agent-spine-plugin/docs/channel-runtime.md +0 -92
- package/agent-spine-plugin/docs/coordination.md +0 -138
- package/agent-spine-plugin/docs/feed-transport.md +0 -99
- package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
- package/agent-spine-plugin/docs/harness-reference.md +0 -45
- package/agent-spine-plugin/docs/host-integration.md +0 -129
- package/agent-spine-plugin/docs/https-transport.md +0 -116
- package/agent-spine-plugin/docs/learning.md +0 -133
- package/agent-spine-plugin/docs/object-transport.md +0 -93
- package/agent-spine-plugin/docs/peer-transport.md +0 -88
- package/agent-spine-plugin/docs/preflight-recall.md +0 -69
- package/agent-spine-plugin/docs/preservation-contract.md +0 -53
- package/agent-spine-plugin/docs/quality-gates.md +0 -50
- package/agent-spine-plugin/docs/relationships.md +0 -73
- package/agent-spine-plugin/docs/releasing.md +0 -83
- package/agent-spine-plugin/docs/roadmap.md +0 -307
- package/agent-spine-plugin/docs/selfstarter.md +0 -88
- package/agent-spine-plugin/docs/session-briefing.md +0 -74
- package/agent-spine-plugin/docs/shared-memory.md +0 -259
- package/agent-spine-plugin/docs/source-roots.md +0 -86
- package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
- package/agent-spine-plugin/scripts/check-hosts.js +0 -195
- package/agent-spine-plugin/scripts/check-install.js +0 -569
- package/agent-spine-plugin/scripts/check-syntax.js +0 -29
- package/agent-spine-plugin/scripts/github-actions.js +0 -11
- package/agent-spine-plugin/scripts/release-check.js +0 -128
- package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
- package/agent-spine-plugin/scripts/run-checks.js +0 -46
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
- package/agent-spine-plugin/spine-example/1-identity.md +0 -12
- package/agent-spine-plugin/spine-example/2-voice.md +0 -6
- package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
- package/agent-spine-plugin/spine-example/4-history.md +0 -4
- package/bin/empty-response-retry-policy.cjs +0 -29
- package/bin/fredrik-glm-provider.cjs +0 -256
- package/bin/package-regression-policy.cjs +0 -77
- package/fredrik-glm-profile.toml.example +0 -26
- package/release-planned-removals.json +0 -15
- package/scripts/check-active-profile-plugin-startup.js +0 -36
- package/scripts/check-active-work-steer-regression.js +0 -46
- package/scripts/check-approval-observability-regression.js +0 -111
- package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
- package/scripts/check-bundled-agent-spine-regression.js +0 -48
- package/scripts/check-codebase-search-packaging-regression.js +0 -92
- package/scripts/check-copy-command-regression.js +0 -74
- package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
- package/scripts/check-current-turn-read-pin-regression.js +0 -94
- package/scripts/check-deepseek-native-max-regression.js +0 -49
- package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
- package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
- package/scripts/check-fredrik-glm-regression.js +0 -169
- package/scripts/check-historical-tool-result-preview-regression.js +0 -77
- package/scripts/check-history-pressure-offload-regression.js +0 -77
- package/scripts/check-mcp-startup-wait-budget.js +0 -48
- package/scripts/check-package-regression.js +0 -38
- package/scripts/check-plugin-startup-regression.js +0 -53
- package/scripts/check-programmatic-context-isolation-regression.js +0 -193
- package/scripts/check-programmatic-tool-regression.js +0 -294
- package/scripts/check-queue-controls-regression.js +0 -189
- package/scripts/check-release-metadata.js +0 -103
- package/scripts/check-reload-agent-spine-regression.js +0 -76
- package/scripts/check-resume-replay-regression.js +0 -102
- package/scripts/check-session-cancel-regression.js +0 -43
- package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
- package/scripts/check-session-start-hook-context-regression.js +0 -228
- package/scripts/check-shell-terminal-isolation-regression.js +0 -81
- package/scripts/check-slash-escape-regression.js +0 -89
- package/scripts/check-startup-swarm-command-regression.js +0 -24
- package/scripts/check-structured-subagent-output-regression.js +0 -331
- package/scripts/check-telegram-bridge-watchdog.js +0 -60
- package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
- package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
- package/scripts/check-todo-loop-regression.js +0 -78
- package/scripts/check-todo-progress-regression.js +0 -416
- package/scripts/check-todo-recovery-catalog-regression.js +0 -50
- package/scripts/check-tool-schema-capacity-regression.js +0 -40
- package/scripts/programmatic-tool-runtime.test.mjs +0 -365
- package/scripts/structured-subagent-output.test.cjs +0 -170
- /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
canonicalEditedToolInputJson,
|
|
5
|
+
validateEditedToolInputObject,
|
|
6
|
+
} = require('./editable-tool-approval-policy.cjs');
|
|
7
|
+
|
|
8
|
+
class EditableToolReprepareError extends Error {
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'EditableToolReprepareError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function fail(code, message) {
|
|
17
|
+
throw new EditableToolReprepareError(code, message);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function buildEditedToolCallPlan({
|
|
21
|
+
step,
|
|
22
|
+
call,
|
|
23
|
+
editedArgs,
|
|
24
|
+
editCount,
|
|
25
|
+
preflightToolCall,
|
|
26
|
+
} = {}) {
|
|
27
|
+
if (editCount !== 0) {
|
|
28
|
+
fail('EDITED_REPREPARE_ALREADY_EDITED', 'A tool call may be edited at most once.');
|
|
29
|
+
}
|
|
30
|
+
if (call?.kind !== 'runnable' || call.toolCall === null || typeof call.toolCall !== 'object' || typeof call.toolCall.id !== 'string' || typeof call.toolCall.name !== 'string') {
|
|
31
|
+
fail('EDITED_REPREPARE_CALL_INVALID', 'Only a runnable tool call can be re-prepared.');
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(step?.toolCalls) || typeof preflightToolCall !== 'function') {
|
|
34
|
+
fail('EDITED_REPREPARE_CONTEXT_INVALID', 'Edited re-preparation requires the active ordered tool-call list and preflight function.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const matchingIndexes = [];
|
|
38
|
+
for (let index = 0; index < step.toolCalls.length; index += 1) {
|
|
39
|
+
if (step.toolCalls[index]?.id === call.toolCall.id) matchingIndexes.push(index);
|
|
40
|
+
}
|
|
41
|
+
if (matchingIndexes.length === 0) {
|
|
42
|
+
fail('EDITED_REPREPARE_IDENTITY_MISSING', 'The edited tool call is absent from the active ordered call list.');
|
|
43
|
+
}
|
|
44
|
+
if (matchingIndexes.length !== 1) {
|
|
45
|
+
fail('EDITED_REPREPARE_IDENTITY_AMBIGUOUS', 'The edited tool-call ID is not unique in the active call list.');
|
|
46
|
+
}
|
|
47
|
+
const position = matchingIndexes[0];
|
|
48
|
+
if (step.toolCalls[position] !== call.toolCall || call.toolName !== call.toolCall.name) {
|
|
49
|
+
fail('EDITED_REPREPARE_IDENTITY_CHANGED', 'The active tool-call identity changed before re-preparation.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const safeEditedArgs = validateEditedToolInputObject(editedArgs);
|
|
53
|
+
const patchedToolCall = Object.freeze({
|
|
54
|
+
...call.toolCall,
|
|
55
|
+
id: call.toolCall.id,
|
|
56
|
+
name: call.toolCall.name,
|
|
57
|
+
arguments: canonicalEditedToolInputJson(safeEditedArgs),
|
|
58
|
+
});
|
|
59
|
+
const patchedToolCalls = step.toolCalls.slice();
|
|
60
|
+
patchedToolCalls[position] = patchedToolCall;
|
|
61
|
+
const editedStep = {
|
|
62
|
+
...step,
|
|
63
|
+
toolCalls: Object.freeze(patchedToolCalls),
|
|
64
|
+
};
|
|
65
|
+
const repreflight = preflightToolCall(editedStep, patchedToolCall);
|
|
66
|
+
if (repreflight === null || typeof repreflight !== 'object') {
|
|
67
|
+
fail('EDITED_REPREPARE_PREFLIGHT_INVALID', 'Edited tool preflight returned no result.');
|
|
68
|
+
}
|
|
69
|
+
if (repreflight.toolCall?.id !== call.toolCall.id || repreflight.toolCall?.name !== call.toolCall.name || repreflight.toolName !== call.toolName) {
|
|
70
|
+
fail('EDITED_REPREPARE_IDENTITY_CHANGED', 'Edited preflight changed the immutable tool identity.');
|
|
71
|
+
}
|
|
72
|
+
if (repreflight.kind === 'rejected') {
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
kind: 'rejected',
|
|
75
|
+
step: editedStep,
|
|
76
|
+
call: repreflight,
|
|
77
|
+
editCount: 1,
|
|
78
|
+
output: repreflight.output,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (repreflight.kind !== 'runnable') {
|
|
82
|
+
fail('EDITED_REPREPARE_PREFLIGHT_INVALID', 'Edited preflight returned an unsupported call state.');
|
|
83
|
+
}
|
|
84
|
+
if (repreflight.tool !== call.tool) {
|
|
85
|
+
fail('EDITED_REPREPARE_REGISTRY_CHANGED', 'The authoritative tool registry entry changed during edited approval.');
|
|
86
|
+
}
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
kind: 'runnable',
|
|
89
|
+
step: editedStep,
|
|
90
|
+
call: repreflight,
|
|
91
|
+
editCount: 1,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = Object.freeze({
|
|
96
|
+
EditableToolReprepareError,
|
|
97
|
+
buildEditedToolCallPlan,
|
|
98
|
+
});
|
|
99
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function versionFromStat(stat) {
|
|
4
|
+
if (!stat || typeof stat !== 'object') return null;
|
|
5
|
+
return [
|
|
6
|
+
stat.stDev ?? null,
|
|
7
|
+
stat.stIno ?? null,
|
|
8
|
+
stat.stSize ?? null,
|
|
9
|
+
stat.stMtime ?? null,
|
|
10
|
+
].join(':');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isNotFoundError(error) {
|
|
14
|
+
return error?.code === 'ENOENT' || error?.code === 'ENOTDIR';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createFileObservationPolicy(options = {}) {
|
|
18
|
+
const caseInsensitive = options.pathClass === 'win32';
|
|
19
|
+
const observations = new Map();
|
|
20
|
+
|
|
21
|
+
function key(filePath) {
|
|
22
|
+
const normalized = String(filePath).replaceAll('\\', '/');
|
|
23
|
+
return caseInsensitive ? normalized.toLowerCase() : normalized;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function recordPresent(filePath, stat) {
|
|
27
|
+
const version = versionFromStat(stat);
|
|
28
|
+
if (version === null) throw new TypeError('recordPresent requires a file stat');
|
|
29
|
+
observations.set(key(filePath), { kind: 'present', version });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function recordAbsent(filePath) {
|
|
33
|
+
observations.set(key(filePath), { kind: 'absent' });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function verifyStableRead(filePath, before, after) {
|
|
37
|
+
const beforeVersion = versionFromStat(before);
|
|
38
|
+
const afterVersion = versionFromStat(after);
|
|
39
|
+
if (beforeVersion === null || afterVersion === null || beforeVersion !== afterVersion) {
|
|
40
|
+
return {
|
|
41
|
+
allowed: false,
|
|
42
|
+
code: 'FS_STALE_VERSION',
|
|
43
|
+
error: `File changed while reading "${filePath}". Read it again before editing or overwriting it.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
recordPresent(filePath, after);
|
|
47
|
+
return { allowed: true, version: afterVersion };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function authorizeEdit(filePath, currentStat) {
|
|
51
|
+
const prior = observations.get(key(filePath));
|
|
52
|
+
if (currentStat === null) {
|
|
53
|
+
return {
|
|
54
|
+
allowed: false,
|
|
55
|
+
code: prior?.kind === 'absent' ? 'FS_NOT_FOUND' : 'FS_NOT_OBSERVED',
|
|
56
|
+
error: prior?.kind === 'absent'
|
|
57
|
+
? `Cannot edit "${filePath}": the last Read confirmed that it does not exist.`
|
|
58
|
+
: `Edit requires reading "${filePath}" first. Read the exact target path, then retry.`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (prior === undefined) {
|
|
62
|
+
return {
|
|
63
|
+
allowed: false,
|
|
64
|
+
code: 'FS_NOT_OBSERVED',
|
|
65
|
+
error: `Edit requires reading "${filePath}" first. Read the exact target path, then retry.`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (prior.kind !== 'present' || prior.version !== versionFromStat(currentStat)) {
|
|
69
|
+
return {
|
|
70
|
+
allowed: false,
|
|
71
|
+
code: 'FS_STALE_VERSION',
|
|
72
|
+
error: `"${filePath}" changed since the last Read. Read it again, then retry the Edit.`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { allowed: true, version: prior.version };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function authorizeWrite(filePath, currentStat) {
|
|
79
|
+
const prior = observations.get(key(filePath));
|
|
80
|
+
if (currentStat === null) {
|
|
81
|
+
if (prior?.kind === 'present') {
|
|
82
|
+
return {
|
|
83
|
+
allowed: false,
|
|
84
|
+
code: 'FS_STALE_VERSION',
|
|
85
|
+
error: `"${filePath}" disappeared since the last Read. Read it again before recreating it.`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return { allowed: true, version: null, create: true };
|
|
89
|
+
}
|
|
90
|
+
if (prior === undefined) {
|
|
91
|
+
return {
|
|
92
|
+
allowed: false,
|
|
93
|
+
code: 'FS_NOT_OBSERVED',
|
|
94
|
+
error: `Write would replace or append to existing file "${filePath}" without a prior Read. Read it first, then retry.`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (prior.kind !== 'present' || prior.version !== versionFromStat(currentStat)) {
|
|
98
|
+
return {
|
|
99
|
+
allowed: false,
|
|
100
|
+
code: 'FS_STALE_VERSION',
|
|
101
|
+
error: `"${filePath}" changed since the last Read. Read it again, then retry the Write.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { allowed: true, version: prior.version, create: false };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function verifyUnchanged(filePath, expectedVersion, currentStat) {
|
|
108
|
+
const currentVersion = versionFromStat(currentStat);
|
|
109
|
+
if (expectedVersion === currentVersion) return { allowed: true };
|
|
110
|
+
return {
|
|
111
|
+
allowed: false,
|
|
112
|
+
code: 'FS_STALE_VERSION',
|
|
113
|
+
error: expectedVersion === null
|
|
114
|
+
? `"${filePath}" appeared before the new file could be created. Read it before deciding whether to overwrite it.`
|
|
115
|
+
: `"${filePath}" changed before the mutation was written. Read it again, then retry.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
authorizeEdit,
|
|
121
|
+
authorizeWrite,
|
|
122
|
+
recordAbsent,
|
|
123
|
+
recordPresent,
|
|
124
|
+
verifyStableRead,
|
|
125
|
+
verifyUnchanged,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
createFileObservationPolicy,
|
|
131
|
+
isNotFoundError,
|
|
132
|
+
versionFromStat,
|
|
133
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Readability } = require('@mozilla/readability');
|
|
4
|
+
const { parseHTML } = require('linkedom');
|
|
5
|
+
const TurndownService = require('turndown');
|
|
6
|
+
const { tables } = require('turndown-plugin-gfm');
|
|
7
|
+
|
|
8
|
+
const MAX_LINK_LENGTH = 2_048;
|
|
9
|
+
const JUNK_SELECTORS = Object.freeze([
|
|
10
|
+
'script',
|
|
11
|
+
'style',
|
|
12
|
+
'noscript',
|
|
13
|
+
'nav',
|
|
14
|
+
'footer',
|
|
15
|
+
'header',
|
|
16
|
+
'aside',
|
|
17
|
+
'iframe',
|
|
18
|
+
'svg',
|
|
19
|
+
'form',
|
|
20
|
+
'button',
|
|
21
|
+
'input',
|
|
22
|
+
'[role="navigation"]',
|
|
23
|
+
'[role="banner"]',
|
|
24
|
+
'[role="contentinfo"]',
|
|
25
|
+
'[aria-hidden="true"]',
|
|
26
|
+
'[hidden]',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function canonicalBaseUrl(value) {
|
|
30
|
+
const parsed = new URL(value);
|
|
31
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
32
|
+
throw new Error('HTML-to-Markdown base URL must use http or https.');
|
|
33
|
+
}
|
|
34
|
+
return parsed.toString();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function safeResolvedLink(raw, baseUrl) {
|
|
38
|
+
if (typeof raw !== 'string' || raw.trim().length === 0) return null;
|
|
39
|
+
let resolved;
|
|
40
|
+
try {
|
|
41
|
+
resolved = new URL(raw, baseUrl);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
if (resolved.protocol !== 'http:' && resolved.protocol !== 'https:') return null;
|
|
46
|
+
const value = resolved.toString();
|
|
47
|
+
return value.length <= MAX_LINK_LENGTH ? value : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function removeAll(root, selector) {
|
|
51
|
+
for (const element of root.querySelectorAll(selector)) element.remove();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sanitizeTree(root, baseUrl) {
|
|
55
|
+
for (const selector of JUNK_SELECTORS) removeAll(root, selector);
|
|
56
|
+
|
|
57
|
+
for (const element of root.querySelectorAll('[style]')) {
|
|
58
|
+
const style = element.getAttribute('style') ?? '';
|
|
59
|
+
if (/display\s*:\s*none|visibility\s*:\s*hidden/iu.test(style)) element.remove();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const selector of [
|
|
63
|
+
'sup.reference',
|
|
64
|
+
'sup.noprint',
|
|
65
|
+
'.reflist',
|
|
66
|
+
'.references',
|
|
67
|
+
'.refbegin',
|
|
68
|
+
'.catlinks',
|
|
69
|
+
'.mw-authority-control',
|
|
70
|
+
]) removeAll(root, selector);
|
|
71
|
+
|
|
72
|
+
for (const anchor of root.querySelectorAll('a[href]')) {
|
|
73
|
+
const safe = safeResolvedLink(anchor.getAttribute('href'), baseUrl);
|
|
74
|
+
if (safe === null) anchor.removeAttribute('href');
|
|
75
|
+
else anchor.setAttribute('href', safe);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const image of root.querySelectorAll('img')) {
|
|
79
|
+
const alt = (image.getAttribute('alt') ?? '').trim();
|
|
80
|
+
if (alt.length > 0) image.replaceWith(image.ownerDocument.createTextNode(alt));
|
|
81
|
+
else image.remove();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createMarkdownService() {
|
|
86
|
+
const service = new TurndownService({
|
|
87
|
+
headingStyle: 'atx',
|
|
88
|
+
codeBlockStyle: 'fenced',
|
|
89
|
+
bulletListMarker: '-',
|
|
90
|
+
});
|
|
91
|
+
service.use(tables);
|
|
92
|
+
service.remove(['script', 'style', 'noscript', 'iframe', 'svg']);
|
|
93
|
+
return service;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function withTitle(title, markdown) {
|
|
97
|
+
const cleanTitle = title.trim();
|
|
98
|
+
if (cleanTitle.length === 0) return markdown;
|
|
99
|
+
const titleHeading = `# ${cleanTitle}`;
|
|
100
|
+
if (markdown === titleHeading || markdown.startsWith(`${titleHeading}\n`)) return markdown;
|
|
101
|
+
return `${titleHeading}\n\n${markdown}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderRoot(root, title, safeBaseUrl) {
|
|
105
|
+
sanitizeTree(root, safeBaseUrl);
|
|
106
|
+
const fallback = (root.textContent ?? '').replace(/\s+/gu, ' ').trim();
|
|
107
|
+
let markdown = createMarkdownService().turndown(root.innerHTML).trim();
|
|
108
|
+
if (markdown.length === 0) markdown = fallback;
|
|
109
|
+
markdown = markdown.replace(/\n{4,}/gu, '\n\n\n').trim();
|
|
110
|
+
return withTitle(title, markdown);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function htmlToResearchMarkdown(html, baseUrl) {
|
|
114
|
+
if (typeof html !== 'string' || html.trim().length === 0) return '';
|
|
115
|
+
const safeBaseUrl = canonicalBaseUrl(baseUrl);
|
|
116
|
+
const primary = parseHTML(html);
|
|
117
|
+
try {
|
|
118
|
+
const article = new Readability(primary.document, { charThreshold: 0 }).parse();
|
|
119
|
+
if (article !== null && (article.content ?? '').trim().length > 0) {
|
|
120
|
+
const extracted = parseHTML(article.content);
|
|
121
|
+
const extractedRoot = extracted.document.querySelector('body');
|
|
122
|
+
if (extractedRoot !== null) {
|
|
123
|
+
const markdown = renderRoot(extractedRoot, article.title ?? '', safeBaseUrl);
|
|
124
|
+
if (markdown.length > 0) return markdown;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
// Keep the existing fail-open container fallback.
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { document } = parseHTML(html);
|
|
132
|
+
const title = (document.querySelector('title')?.textContent ?? '').trim();
|
|
133
|
+
const root = document.querySelector('article')
|
|
134
|
+
?? document.querySelector('main')
|
|
135
|
+
?? document.querySelector('[role="main"]')
|
|
136
|
+
?? document.querySelector('body');
|
|
137
|
+
return root === null ? '' : renderRoot(root, title, safeBaseUrl);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = Object.freeze({
|
|
141
|
+
JUNK_SELECTORS,
|
|
142
|
+
MAX_LINK_LENGTH,
|
|
143
|
+
canonicalBaseUrl,
|
|
144
|
+
htmlToResearchMarkdown,
|
|
145
|
+
safeResolvedLink,
|
|
146
|
+
});
|
package/bin/launcher-runtime.js
CHANGED
|
@@ -539,7 +539,6 @@ async function runLauncher(options = {}) {
|
|
|
539
539
|
env.BLUN_SHARED_HOME = privatePaths.sharedHome;
|
|
540
540
|
env.BLUN_LOG_HOME = privatePaths.sharedHome;
|
|
541
541
|
env.BLUN_PROFILE = PROFILE.profileName;
|
|
542
|
-
env.BLUN_MODEL_MAX_COMPLETION_TOKENS = env.BLUN_MODEL_MAX_COMPLETION_TOKENS || '32768';
|
|
543
542
|
const mnemoConnect = startMnemoConnectHeartbeat({
|
|
544
543
|
env,
|
|
545
544
|
profileName: PROFILE.profileName,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createHash } = require('node:crypto');
|
|
4
|
+
|
|
3
5
|
const MICRO_COMPACTION_PRESSURE_RATIO = 0.75;
|
|
4
6
|
const MICRO_COMPACTION_MIN_ADVANCE_MESSAGES = 20;
|
|
5
7
|
const MICRO_COMPACTION_RECENT_MESSAGES = 4;
|
|
@@ -46,6 +48,66 @@ function selectMicroCompactionCutoff(options = {}) {
|
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
function redundantHistoricalToolResultIds(messages, cutoff) {
|
|
52
|
+
return new Set(redundantHistoricalToolResultReferences(messages, cutoff).keys());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function redundantHistoricalToolResultReferences(messages, cutoff) {
|
|
56
|
+
if (!Array.isArray(messages)) return new Map();
|
|
57
|
+
|
|
58
|
+
const historicalCutoff = Math.min(messages.length, nonNegativeInteger(cutoff));
|
|
59
|
+
const callSignatures = new Map();
|
|
60
|
+
for (const message of messages) {
|
|
61
|
+
if (message?.role !== 'assistant' || !Array.isArray(message.toolCalls)) continue;
|
|
62
|
+
for (const call of message.toolCalls) {
|
|
63
|
+
if (
|
|
64
|
+
typeof call?.id !== 'string'
|
|
65
|
+
|| typeof call.name !== 'string'
|
|
66
|
+
|| typeof call.arguments !== 'string'
|
|
67
|
+
) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
callSignatures.set(call.id, JSON.stringify([call.name, call.arguments]));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const resultsBySignature = new Map();
|
|
75
|
+
for (let index = 0; index < messages.length; index++) {
|
|
76
|
+
const message = messages[index];
|
|
77
|
+
if (message?.role !== 'tool' || typeof message.toolCallId !== 'string') continue;
|
|
78
|
+
const callSignature = callSignatures.get(message.toolCallId);
|
|
79
|
+
if (callSignature === undefined || !Array.isArray(message.content)) continue;
|
|
80
|
+
|
|
81
|
+
let resultSignature;
|
|
82
|
+
try {
|
|
83
|
+
resultSignature = createHash('sha256')
|
|
84
|
+
.update(callSignature)
|
|
85
|
+
.update('\0')
|
|
86
|
+
.update(JSON.stringify(message.content))
|
|
87
|
+
.digest('hex');
|
|
88
|
+
} catch {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const entries = resultsBySignature.get(resultSignature) ?? [];
|
|
93
|
+
entries.push({ index, toolCallId: message.toolCallId });
|
|
94
|
+
resultsBySignature.set(resultSignature, entries);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const redundantIds = new Map();
|
|
98
|
+
for (const entries of resultsBySignature.values()) {
|
|
99
|
+
if (entries.length < 2) continue;
|
|
100
|
+
const newestToolCallId = entries.at(-1)?.toolCallId;
|
|
101
|
+
if (newestToolCallId === undefined) continue;
|
|
102
|
+
for (const entry of entries.slice(0, -1)) {
|
|
103
|
+
if (entry.index < historicalCutoff) {
|
|
104
|
+
redundantIds.set(entry.toolCallId, newestToolCallId);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return redundantIds;
|
|
109
|
+
}
|
|
110
|
+
|
|
49
111
|
function finiteOr(value, fallback) {
|
|
50
112
|
const number = Number(value);
|
|
51
113
|
return Number.isFinite(number) ? number : fallback;
|
|
@@ -77,5 +139,7 @@ module.exports = {
|
|
|
77
139
|
MICRO_COMPACTION_RECENT_MESSAGES,
|
|
78
140
|
MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED,
|
|
79
141
|
MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS,
|
|
142
|
+
redundantHistoricalToolResultIds,
|
|
143
|
+
redundantHistoricalToolResultReferences,
|
|
80
144
|
selectMicroCompactionCutoff,
|
|
81
145
|
};
|
|
@@ -5,7 +5,6 @@ const os = require('node:os');
|
|
|
5
5
|
|
|
6
6
|
const DEFAULT_HEARTBEAT_MS = 60_000;
|
|
7
7
|
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
8
|
-
const DEFAULT_INTERNAL_HUB_URL = 'http://100.85.21.103:7117';
|
|
9
8
|
|
|
10
9
|
function resolveMnemoHubUrl(env = process.env) {
|
|
11
10
|
const raw = [
|
|
@@ -51,7 +50,7 @@ function resolveMnemoProfileConnectConfig(configPath) {
|
|
|
51
50
|
].some((value) => typeof value === 'string' && value.trim().length > 0);
|
|
52
51
|
const baseUrl = resolveMnemoHubUrl(server.env);
|
|
53
52
|
if (configuredUrl && !baseUrl) return null;
|
|
54
|
-
return { agentName, baseUrl
|
|
53
|
+
return baseUrl ? { agentName, baseUrl } : null;
|
|
55
54
|
} catch {
|
|
56
55
|
return null;
|
|
57
56
|
}
|
|
@@ -196,7 +195,6 @@ function startMnemoConnectHeartbeat(options = {}) {
|
|
|
196
195
|
|
|
197
196
|
module.exports = {
|
|
198
197
|
DEFAULT_HEARTBEAT_MS,
|
|
199
|
-
DEFAULT_INTERNAL_HUB_URL,
|
|
200
198
|
DEFAULT_TIMEOUT_MS,
|
|
201
199
|
postMnemoTool,
|
|
202
200
|
resolveMnemoAgentName,
|