blun-king-cli 9.1.536 → 9.1.550

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.
Files changed (117) hide show
  1. package/LIESMICH.txt +13 -869
  2. package/README.md +41 -833
  3. package/bin/assistant-message-offload-policy.cjs +3 -1
  4. package/bin/context-performance-policy.cjs +2 -5
  5. package/bin/context-pressure-policy.cjs +20 -0
  6. package/bin/cron-run-output.cjs +45 -0
  7. package/bin/cron-run-store.cjs +145 -0
  8. package/bin/durable-task-resume-policy.cjs +130 -0
  9. package/bin/durable-task-resume-runtime.cjs +117 -0
  10. package/bin/durable-task-resume-store.cjs +88 -0
  11. package/bin/editable-tool-approval-policy.cjs +540 -0
  12. package/bin/editable-tool-approval-runtime.cjs +99 -0
  13. package/bin/html-to-research-markdown.cjs +146 -0
  14. package/bin/programmatic-tool-runtime.mjs +330 -4
  15. package/bin/read-continuation-policy.cjs +36 -5
  16. package/bin/scoped-cron-run-policy.cjs +358 -0
  17. package/bin/startup-preferences.cjs +3 -3
  18. package/bin/structured-agent-swarm-output.cjs +325 -0
  19. package/bin/subagent-context-fork-policy.cjs +155 -0
  20. package/bin/subagent-skill-policy.cjs +204 -0
  21. package/bin/telegram-approval-relay.cjs +2 -1
  22. package/bin/tool-result-offload-policy.cjs +14 -1
  23. package/bin/update-notice.js +14 -18
  24. package/bin/user-message-offload-policy.cjs +3 -1
  25. package/blun.mjs +1089 -477
  26. package/codebase-index/README.md +12 -0
  27. package/codebase-index/codebase_index.py +129 -18
  28. package/package.json +23 -58
  29. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
  30. package/telegram-plugin/dist/bridge.mjs +8 -1
  31. package/CHANGELOG.md +0 -321
  32. package/agent-spine-plugin/CHANGELOG.md +0 -406
  33. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  34. package/agent-spine-plugin/README.md +0 -344
  35. package/agent-spine-plugin/SECURITY.md +0 -47
  36. package/agent-spine-plugin/docs/acceptance.md +0 -61
  37. package/agent-spine-plugin/docs/architecture.md +0 -183
  38. package/agent-spine-plugin/docs/attention.md +0 -121
  39. package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
  40. package/agent-spine-plugin/docs/channel-runtime.md +0 -92
  41. package/agent-spine-plugin/docs/coordination.md +0 -138
  42. package/agent-spine-plugin/docs/feed-transport.md +0 -99
  43. package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
  44. package/agent-spine-plugin/docs/harness-reference.md +0 -45
  45. package/agent-spine-plugin/docs/host-integration.md +0 -129
  46. package/agent-spine-plugin/docs/https-transport.md +0 -116
  47. package/agent-spine-plugin/docs/learning.md +0 -133
  48. package/agent-spine-plugin/docs/object-transport.md +0 -93
  49. package/agent-spine-plugin/docs/peer-transport.md +0 -88
  50. package/agent-spine-plugin/docs/preflight-recall.md +0 -69
  51. package/agent-spine-plugin/docs/preservation-contract.md +0 -53
  52. package/agent-spine-plugin/docs/quality-gates.md +0 -50
  53. package/agent-spine-plugin/docs/relationships.md +0 -73
  54. package/agent-spine-plugin/docs/releasing.md +0 -83
  55. package/agent-spine-plugin/docs/roadmap.md +0 -307
  56. package/agent-spine-plugin/docs/selfstarter.md +0 -88
  57. package/agent-spine-plugin/docs/session-briefing.md +0 -74
  58. package/agent-spine-plugin/docs/shared-memory.md +0 -259
  59. package/agent-spine-plugin/docs/source-roots.md +0 -86
  60. package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
  61. package/agent-spine-plugin/scripts/check-hosts.js +0 -195
  62. package/agent-spine-plugin/scripts/check-install.js +0 -569
  63. package/agent-spine-plugin/scripts/check-syntax.js +0 -29
  64. package/agent-spine-plugin/scripts/github-actions.js +0 -11
  65. package/agent-spine-plugin/scripts/release-check.js +0 -128
  66. package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
  67. package/agent-spine-plugin/scripts/run-checks.js +0 -46
  68. package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
  69. package/agent-spine-plugin/spine-example/1-identity.md +0 -12
  70. package/agent-spine-plugin/spine-example/2-voice.md +0 -6
  71. package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
  72. package/agent-spine-plugin/spine-example/4-history.md +0 -4
  73. package/bin/fredrik-glm-provider.cjs +0 -256
  74. package/bin/package-regression-policy.cjs +0 -77
  75. package/fredrik-glm-profile.toml.example +0 -26
  76. package/release-planned-removals.json +0 -15
  77. package/scripts/check-active-profile-plugin-startup.js +0 -36
  78. package/scripts/check-active-work-steer-regression.js +0 -46
  79. package/scripts/check-approval-observability-regression.js +0 -111
  80. package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
  81. package/scripts/check-bundled-agent-spine-regression.js +0 -48
  82. package/scripts/check-codebase-search-packaging-regression.js +0 -92
  83. package/scripts/check-copy-command-regression.js +0 -74
  84. package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
  85. package/scripts/check-current-turn-read-pin-regression.js +0 -94
  86. package/scripts/check-deepseek-native-max-regression.js +0 -49
  87. package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
  88. package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
  89. package/scripts/check-fredrik-glm-regression.js +0 -169
  90. package/scripts/check-historical-tool-result-preview-regression.js +0 -77
  91. package/scripts/check-history-pressure-offload-regression.js +0 -77
  92. package/scripts/check-mcp-startup-wait-budget.js +0 -48
  93. package/scripts/check-package-regression.js +0 -38
  94. package/scripts/check-plugin-startup-regression.js +0 -53
  95. package/scripts/check-programmatic-context-isolation-regression.js +0 -193
  96. package/scripts/check-programmatic-tool-regression.js +0 -294
  97. package/scripts/check-queue-controls-regression.js +0 -189
  98. package/scripts/check-release-metadata.js +0 -103
  99. package/scripts/check-reload-agent-spine-regression.js +0 -76
  100. package/scripts/check-resume-replay-regression.js +0 -102
  101. package/scripts/check-session-cancel-regression.js +0 -43
  102. package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
  103. package/scripts/check-session-start-hook-context-regression.js +0 -228
  104. package/scripts/check-shell-terminal-isolation-regression.js +0 -81
  105. package/scripts/check-slash-escape-regression.js +0 -89
  106. package/scripts/check-startup-swarm-command-regression.js +0 -24
  107. package/scripts/check-structured-subagent-output-regression.js +0 -331
  108. package/scripts/check-telegram-bridge-watchdog.js +0 -60
  109. package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
  110. package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
  111. package/scripts/check-todo-loop-regression.js +0 -78
  112. package/scripts/check-todo-progress-regression.js +0 -416
  113. package/scripts/check-todo-recovery-catalog-regression.js +0 -50
  114. package/scripts/check-tool-schema-capacity-regression.js +0 -40
  115. package/scripts/programmatic-tool-runtime.test.mjs +0 -365
  116. package/scripts/structured-subagent-output.test.cjs +0 -170
  117. /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
@@ -1,49 +0,0 @@
1
- const fs = require('node:fs');
2
- const path = require('node:path');
3
-
4
- const packageRoot = path.resolve(__dirname, '..');
5
- const bundlePath = path.join(packageRoot, 'blun.mjs');
6
-
7
- function checkBundle(bundle) {
8
- const required = [
9
- 'max: "Max"',
10
- 'if (effort !== void 0) this.opts.onSessionOnlySelect(effort);',
11
- 'if (this.isBlunSwarmSelected) this.opts.onBlunSwarmSelect?.();',
12
- 'if (effort !== void 0) this.opts.onSelect(effort);',
13
- 'state.swarmMode && state.swarmModeEntry === "effort"',
14
- ];
15
- for (const marker of required) {
16
- if (!bundle.includes(marker)) throw new Error(`missing native-max marker: ${marker}`);
17
- }
18
-
19
- const forbidden = [
20
- 'max: "Swarm"',
21
- 'usesLegacyServerStageScale',
22
- ];
23
- for (const marker of forbidden) {
24
- if (bundle.includes(marker)) throw new Error(`legacy max-to-swarm path remains: ${marker}`);
25
- }
26
- }
27
-
28
- const bundle = fs.readFileSync(bundlePath, 'utf8');
29
- checkBundle(bundle);
30
-
31
- const mutations = [
32
- bundle.replace('max: "Max"', 'max: "Swarm"'),
33
- bundle.replace(
34
- 'if (effort !== void 0) this.opts.onSelect(effort);',
35
- 'if (effort === "max" && this.usesLegacyServerStageScale) this.opts.onBlunSwarmSelect();',
36
- ),
37
- ];
38
-
39
- for (const [index, mutation] of mutations.entries()) {
40
- let rejected = false;
41
- try {
42
- checkBundle(mutation);
43
- } catch {
44
- rejected = true;
45
- }
46
- if (!rejected) throw new Error(`mutation ${index + 1} escaped native-max gate`);
47
- }
48
-
49
- console.log('DeepSeek native max regression gate passed (2 mutations rejected).');
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const assert = require('node:assert/strict');
5
- const fs = require('node:fs');
6
- const path = require('node:path');
7
-
8
- const packageRoot = process.env.BLUN_PACKAGE_UNDER_TEST
9
- ? path.resolve(process.env.BLUN_PACKAGE_UNDER_TEST)
10
- : path.resolve(__dirname, '..');
11
- const policy = require(path.join(packageRoot, 'bin', 'empty-response-retry-policy.cjs'));
12
- const bundle = fs.readFileSync(path.join(packageRoot, 'blun.mjs'), 'utf8');
13
-
14
- const exhausted = (thinkingEffort) => policy.nextThinkingEffortForExhaustedEmpty({
15
- emptyResponseKind: 'length',
16
- maxCompletionTokens: 32_768,
17
- completionTokens: 32_768,
18
- thinkingEffort,
19
- });
20
-
21
- assert.equal(exhausted('max'), 'high');
22
- assert.equal(exhausted('xhigh'), 'high');
23
- assert.equal(exhausted('high'), 'low');
24
- assert.equal(exhausted('low'), 'off');
25
- assert.equal(policy.nextThinkingEffortForExhaustedEmpty({
26
- emptyResponseKind: 'length',
27
- maxCompletionTokens: 32_768,
28
- completionTokens: 2_000,
29
- thinkingEffort: 'max',
30
- }), undefined, 'a non-exhausted response must not downgrade thinking');
31
- assert.equal(policy.nextThinkingEffortForExhaustedEmpty({
32
- emptyResponseKind: 'stop',
33
- maxCompletionTokens: 32_768,
34
- completionTokens: 32_768,
35
- thinkingEffort: 'max',
36
- }), undefined, 'a stop response must not be treated as budget exhaustion');
37
-
38
- for (const needle of [
39
- 'thinkingEffortRetry = nextThinkingEffortForExhaustedEmpty({',
40
- 'thinkingEffort: input.llm.thinkingEffort',
41
- 'const requestProvider = params.thinkingEffortRetry === void 0 ? this.provider : this.provider.withThinking(params.thinkingEffortRetry);',
42
- 'provider: requestProvider,',
43
- 'retryThinkingEffort',
44
- ]) {
45
- assert.ok(bundle.includes(needle), `empty-response retry wiring missing: ${needle}`);
46
- }
47
-
48
- process.stdout.write('empty-response-effort-downgrade-regression PASS\n');
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
- const { REQUIRED_BUNDLE_MARKERS, validateBundleText } = require('./check-fredrik-glm-regression.js');
7
-
8
- const bundlePath = path.resolve(__dirname, '..', 'blun.mjs');
9
- const source = fs.readFileSync(bundlePath, 'utf8');
10
- assert.deepEqual(validateBundleText(source), []);
11
-
12
- for (const marker of REQUIRED_BUNDLE_MARKERS) {
13
- const mutated = source.replaceAll(marker, `MUTATED_${marker.length}`);
14
- assert.notEqual(mutated, source, `mutation marker missing from source: ${marker}`);
15
- assert.notDeepEqual(validateBundleText(mutated), [], `gate accepted mutation: ${marker}`);
16
- }
17
-
18
- process.stdout.write(`fredrik-glm-mutation-regression: ok (${REQUIRED_BUNDLE_MARKERS.length} mutations)\n`);
@@ -1,169 +0,0 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const os = require('node:os');
6
- const path = require('node:path');
7
- const { spawnSync } = require('node:child_process');
8
- const policy = require('../bin/fredrik-glm-provider.cjs');
9
-
10
- const PACKAGE_ROOT = path.resolve(__dirname, '..');
11
- const BUNDLE_PATH = path.join(PACKAGE_ROOT, 'blun.mjs');
12
- const TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'fredrik-glm-profile.toml.example');
13
-
14
- const REQUIRED_BUNDLE_MARKERS = Object.freeze([
15
- 'literal("openai_compatible")',
16
- 'apiKeyFile: string().optional()',
17
- 'providerName !== FREDRIK_GLM_PROVIDER_ID || !isConfiguredFredrikGlmAlias(model, alias)',
18
- 'type: providerConfig.type',
19
- 'apiKey: readFredrikGlmApiKeyFile(provider)',
20
- 'openAICompatible: true',
21
- 'const controls = fredrikGlmRequestControls(effectiveThinkingEffort ?? "max")',
22
- 'requestExtraBody["thinking"] = controls.thinking',
23
- 'requestExtraBody["reasoning_effort"] = controls.reasoning_effort',
24
- 'name: "model"',
25
- 'await handleModelCommand(host, args)',
26
- 'performModelSwitch(host, selection.alias, selection.thinking, false)',
27
- '...configuredFredrikGlmModels(models)',
28
- '[FREDRIK_GLM_PROVIDER_ID]: fredrikGlm',
29
- 'model: status.model ?? BLUN_KING_MODEL_ALIAS',
30
- '!isAllowedFredrikRuntimeAlias(resumedStatus.model ?? BLUN_KING_MODEL_ALIAS, this.state.appState.availableModels)',
31
- ]);
32
-
33
- const FORBIDDEN_BUNDLE_MARKERS = Object.freeze([
34
- '..."model" in patch && patch.model?.trim().length ? { model: BLUN_KING_MODEL_ALIAS }',
35
- '..."activeResponderModel" in patch && patch.activeResponderModel != null ? { activeResponderModel: BLUN_KING_MODEL_ALIAS }',
36
- ]);
37
-
38
- function validateBundleText(source) {
39
- const failures = [];
40
- for (const marker of REQUIRED_BUNDLE_MARKERS) {
41
- if (!source.includes(marker)) failures.push(`missing:${marker}`);
42
- }
43
- for (const marker of FORBIDDEN_BUNDLE_MARKERS) {
44
- if (source.includes(marker)) failures.push(`forbidden:${marker}`);
45
- }
46
- const setSessionBlock = source.slice(source.indexOf('\n\tasync setSession(session, options = {}) {'), source.indexOf('\n\tasync syncRuntimeState(', source.indexOf('\n\tasync setSession(session, options = {}) {')));
47
- if (!setSessionBlock.includes('!isAllowedFredrikRuntimeAlias(resumedStatus.model ?? BLUN_KING_MODEL_ALIAS')) failures.push('resume-missing-profile-guard');
48
- const reloadBlock = source.slice(source.indexOf('\n\tasync reloadCurrentSessionView('), source.indexOf('\n\tasync restoreSession(', source.indexOf('\n\tasync reloadCurrentSessionView(')));
49
- if (reloadBlock.includes('session.setModel(BLUN_KING_MODEL_ALIAS')) failures.push('reload-forces-king');
50
- return failures;
51
- }
52
-
53
- function exactModels() {
54
- return {
55
- 'fredrik/glm-5.3-flash': {
56
- provider: policy.PROVIDER_ID,
57
- model: 'glm-5.3-flash',
58
- displayName: 'GLM 5.3 Flash',
59
- maxContextSize: 1000000,
60
- maxOutputSize: 128000,
61
- capabilities: ['tool_use', 'always_thinking'],
62
- supportEfforts: ['low', 'high', 'max'],
63
- defaultEffort: 'max',
64
- },
65
- 'fredrik/glm-5.3': {
66
- provider: policy.PROVIDER_ID,
67
- model: 'glm-5.3',
68
- displayName: 'GLM 5.3',
69
- maxContextSize: 1000000,
70
- maxOutputSize: 128000,
71
- capabilities: ['tool_use', 'always_thinking'],
72
- supportEfforts: ['low', 'high', 'max'],
73
- defaultEffort: 'max',
74
- },
75
- };
76
- }
77
-
78
- function run() {
79
- const source = fs.readFileSync(BUNDLE_PATH, 'utf8');
80
- assert.deepEqual(validateBundleText(source), []);
81
-
82
- assert.equal(policy.BASE_URL, 'https://api.z.ai/api/coding/paas/v4');
83
- assert.deepEqual([...policy.MODEL_EFFORTS], ['low', 'high', 'max']);
84
- assert.deepEqual(policy.requestControls('low'), { thinking: { type: 'enabled' }, reasoning_effort: 'low' });
85
- assert.deepEqual(policy.requestControls('max'), { thinking: { type: 'enabled' }, reasoning_effort: 'max' });
86
- assert.throws(() => policy.requestControls('off'), /only low, high, or max/u);
87
- assert.equal(policy.normalizeBaseUrl(`${policy.BASE_URL}/`), policy.BASE_URL);
88
- for (const bad of ['http://api.z.ai/api/coding/paas/v4', 'https://api.z.ai/api/paas/v4', 'https://api.z.ai/api/coding/paas/v4?key=x', 'https://example.com/api/coding/paas/v4']) {
89
- assert.throws(() => policy.normalizeBaseUrl(bad), /approved api\.z\.ai Coding endpoint/u);
90
- }
91
-
92
- const models = exactModels();
93
- assert.deepEqual(Object.keys(policy.configuredModels(models)), ['fredrik/glm-5.3-flash', 'fredrik/glm-5.3']);
94
- assert.equal(policy.isAllowedRuntimeAlias('blun/king', models), true);
95
- assert.equal(policy.isAllowedRuntimeAlias('fredrik/glm-5.3', models), true);
96
- assert.equal(policy.isAllowedRuntimeAlias('werner/glm-5.3', models), false);
97
- const mutated = structuredClone(models);
98
- mutated['fredrik/glm-5.3'].supportEfforts = ['low', 'max'];
99
- assert.equal(policy.isConfiguredAlias('fredrik/glm-5.3', mutated['fredrik/glm-5.3']), false);
100
-
101
- const providers = {
102
- [policy.PROVIDER_ID]: {
103
- type: policy.PROVIDER_TYPE,
104
- baseUrl: policy.BASE_URL,
105
- apiKeyFile: 'secrets/zai-api-key.txt',
106
- },
107
- };
108
- assert.equal(policy.configuredProvider(providers), providers[policy.PROVIDER_ID]);
109
- const diagnostic = policy.safeDiagnostic('fredrik/glm-5.3', models, providers, true);
110
- assert.deepEqual(diagnostic, {
111
- alias: 'fredrik/glm-5.3',
112
- alias_source: 'profile-config',
113
- provider: 'fredrik:zai',
114
- provider_type: 'openai_compatible',
115
- model: 'glm-5.3',
116
- base_host: 'api.z.ai',
117
- credential_source: 'profile-file',
118
- credential_present: true,
119
- credential_fingerprint: null,
120
- wire_format: 'openai-chat-completions',
121
- health: 'untested',
122
- });
123
- assert.equal(JSON.stringify(diagnostic).includes('zai-api-key.txt'), false);
124
-
125
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'blun-fredrik-glm-'));
126
- try {
127
- const secretDir = path.join(root, 'secrets');
128
- fs.mkdirSync(secretDir);
129
- const secretPath = path.join(secretDir, 'zai-api-key.txt');
130
- const canary = 'secret-canary-never-print';
131
- fs.writeFileSync(secretPath, `${canary}\n`, { encoding: 'utf8', mode: 0o600 });
132
- const read = policy.readApiKeyFile(providers[policy.PROVIDER_ID], { BLUN_HOME: root });
133
- assert.equal(read, canary);
134
- assert.throws(() => policy.readApiKeyFile({ ...providers[policy.PROVIDER_ID], apiKey: canary }, { BLUN_HOME: root }), /only api_key_file/u);
135
- assert.throws(() => policy.readApiKeyFile({ ...providers[policy.PROVIDER_ID], apiKeyFile: '..\\outside.txt' }, { BLUN_HOME: root }), /escapes the profile/u);
136
- fs.writeFileSync(secretPath, `${canary}\nsecond\n`, 'utf8');
137
- assert.throws(() => policy.readApiKeyFile(providers[policy.PROVIDER_ID], { BLUN_HOME: root }), /exactly one non-empty line/u);
138
- fs.writeFileSync(secretPath, `${canary}\n`, 'utf8');
139
- if (process.platform === 'win32') {
140
- const aclMutation = spawnSync('icacls.exe', [secretPath, '/grant', '*S-1-1-0:(M)'], { encoding: 'utf8', windowsHide: true });
141
- assert.equal(aclMutation.status, 0, 'ACL mutation setup failed');
142
- assert.throws(() => policy.readApiKeyFile(providers[policy.PROVIDER_ID], { BLUN_HOME: root }), /ACL is too permissive/u);
143
- }
144
- const missingProvider = { ...providers[policy.PROVIDER_ID], apiKeyFile: 'secrets/zai-api-key.txt' };
145
- fs.rmSync(secretPath, { force: true });
146
- assert.throws(() => policy.readApiKeyFile(missingProvider, { BLUN_HOME: root }), (error) => {
147
- assert.equal(error.message.includes(root), false);
148
- return /could not be resolved safely/u.test(error.message);
149
- });
150
- } finally {
151
- fs.rmSync(root, { recursive: true, force: true });
152
- }
153
-
154
- const template = fs.readFileSync(TEMPLATE_PATH, 'utf8');
155
- assert.match(template, /\[providers\."fredrik:zai"\]/u);
156
- assert.match(template, /api_key_file = "secrets\/zai-api-key\.txt"/u);
157
- assert.equal(template.includes('api_key ='), false);
158
- assert.equal(template.includes('default_model'), false);
159
- assert.equal(template.includes('werner'), false);
160
- assert.equal(template.includes('manfred'), false);
161
-
162
- const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
163
- assert.equal(pkg.version, '9.1.536');
164
- process.stdout.write('fredrik-glm-regression: ok\n');
165
- }
166
-
167
- module.exports = { REQUIRED_BUNDLE_MARKERS, validateBundleText };
168
-
169
- if (require.main === module) run();
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
-
7
- const bundlePath = process.env.BLUN_BUNDLE_UNDER_TEST
8
- ? path.resolve(process.env.BLUN_BUNDLE_UNDER_TEST)
9
- : path.resolve(__dirname, '..', 'blun.mjs');
10
- const bundle = fs.readFileSync(bundlePath, 'utf8');
11
-
12
- function assert(condition, message) {
13
- if (!condition) throw new Error(message);
14
- }
15
-
16
- const rendererMatch = bundle.match(
17
- /function renderHistoricalToolResultReference\(toolName, toolCallId, text, outputPath, storageMode\) \{[\s\S]*?\n\}/,
18
- );
19
- assert(rendererMatch, 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: renderer is missing');
20
- const policy = require('../bin/tool-result-offload-policy.cjs');
21
-
22
- const renderHistoricalToolResultReference = Function(
23
- 'Buffer',
24
- 'TOOL_RESULT_OFFLOAD_MARKER',
25
- 'TOOL_RESULT_RECOVERY_PAGE_LINES',
26
- 'TOOL_RESULT_HISTORICAL_PREVIEW_CHARS',
27
- 'readContinuationLineOffset',
28
- 'createToolResultPreview',
29
- `return (${rendererMatch[0]});`,
30
- )(
31
- Buffer,
32
- '[Tool result offloaded]',
33
- 20,
34
- policy.TOOL_RESULT_HISTORICAL_PREVIEW_CHARS,
35
- () => undefined,
36
- (text, maxChars) => `${text.slice(0, maxChars / 2)}\n[preview omitted]\n${text.slice(-maxChars / 2)}`,
37
- );
38
-
39
- const text = `HEAD-${'a'.repeat(900)}-TAIL`;
40
- const rendered = renderHistoricalToolResultReference(
41
- 'Bash',
42
- 'call-preview',
43
- text,
44
- 'C:/archive/call-preview.txt',
45
- 'private_archive',
46
- );
47
-
48
- assert(
49
- rendered.includes('[preview: head and tail]'),
50
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: historical offload has no preview marker',
51
- );
52
- assert(
53
- rendered.includes('HEAD-') && rendered.includes('-TAIL'),
54
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: historical offload does not preserve both ends',
55
- );
56
- assert(
57
- rendered.includes('output_path: C:/archive/call-preview.txt'),
58
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: recovery path was lost',
59
- );
60
- const compacted = policy.compactPersistedToolResultReference([
61
- { type: 'text', text: rendered },
62
- ]);
63
- const compactedText = compacted[0].text;
64
- assert(
65
- compactedText.includes('[preview: head and tail]'),
66
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: context compaction removed the preview',
67
- );
68
- assert(
69
- compactedText.includes('HEAD-') && compactedText.includes('-TAIL'),
70
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: compact preview lost an endpoint',
71
- );
72
- assert(
73
- compactedText.length < rendered.length,
74
- 'HISTORICAL_TOOL_RESULT_PREVIEW_REGRESSION: compact preview is not bounded',
75
- );
76
-
77
- process.stdout.write('historical-tool-result-preview-regression PASS\n');
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const assert = require('node:assert/strict');
5
- const path = require('node:path');
6
-
7
- const packageRoot = process.env.BLUN_PACKAGE_UNDER_TEST
8
- ? path.resolve(process.env.BLUN_PACKAGE_UNDER_TEST)
9
- : path.resolve(__dirname, '..');
10
- const userPolicy = require(path.join(packageRoot, 'bin', 'user-message-offload-policy.cjs'));
11
- const assistantPolicy = require(path.join(packageRoot, 'bin', 'assistant-message-offload-policy.cjs'));
12
- const skillPolicy = require(path.join(packageRoot, 'bin', 'skill-activation-performance-policy.cjs'));
13
-
14
- function textMessage(role, text, origin) {
15
- return { role, content: [{ type: 'text', text }], toolCalls: [], ...(origin ? { origin } : {}) };
16
- }
17
-
18
- const smallHistory = Array.from({ length: 25 }, (_, index) => textMessage(
19
- index === 0 ? 'user' : 'assistant',
20
- 'x'.repeat(5_000),
21
- index === 0
22
- ? { kind: 'skill_activation', skillName: 'test-skill', skillPath: 'C:/test/SKILL.md' }
23
- : undefined,
24
- ));
25
- const smallChars = userPolicy.historyTextChars(smallHistory);
26
- assert.equal(userPolicy.shouldOffloadHistoricalUserMessage({
27
- historyIndex: 0,
28
- historyLength: smallHistory.length,
29
- historyChars: smallChars,
30
- textChars: 5_000,
31
- }), false, 'message 21 alone must not trigger user-message offload');
32
- assert.equal(assistantPolicy.shouldOffloadHistoricalAssistantMessage({
33
- historyIndex: 1,
34
- historyLength: smallHistory.length,
35
- historyChars: smallChars,
36
- textChars: 5_000,
37
- }), false, 'message 21 alone must not trigger assistant-message offload');
38
- assert.equal(assistantPolicy.shouldCompactHistoricalAssistantToolNarration({
39
- historyIndex: 1,
40
- historyLength: smallHistory.length,
41
- historyChars: smallChars,
42
- textChars: 500,
43
- toolCallCount: 1,
44
- completedToolCallCount: 1,
45
- }), false, 'message 21 alone must not compact completed tool narration');
46
- assert.strictEqual(
47
- skillPolicy.compactHistoricalSkillActivations(smallHistory),
48
- smallHistory,
49
- 'message 21 alone must not compact active skill instructions',
50
- );
51
-
52
- const largeHistory = Array.from({ length: 70 }, (_, index) => textMessage(
53
- index === 0 ? 'user' : 'assistant',
54
- 'y'.repeat(20_000),
55
- index === 0
56
- ? { kind: 'skill_activation', skillName: 'test-skill', skillPath: 'C:/test/SKILL.md' }
57
- : undefined,
58
- ));
59
- const largeChars = userPolicy.historyTextChars(largeHistory);
60
- assert.ok(largeChars >= 1_000_000);
61
- assert.equal(userPolicy.shouldOffloadHistoricalUserMessage({
62
- historyIndex: 0,
63
- historyLength: largeHistory.length,
64
- historyChars: largeChars,
65
- textChars: 20_000,
66
- }), true, 'real history pressure must retain archival offload');
67
- assert.equal(assistantPolicy.shouldOffloadHistoricalAssistantMessage({
68
- historyIndex: 1,
69
- historyLength: largeHistory.length,
70
- historyChars: largeChars,
71
- textChars: 20_000,
72
- }), true, 'real history pressure must retain assistant archival offload');
73
- const compactedSkills = skillPolicy.compactHistoricalSkillActivations(largeHistory);
74
- assert.notStrictEqual(compactedSkills, largeHistory);
75
- assert.match(compactedSkills[0].content[0].text, /Earlier skill activation compacted/u);
76
-
77
- process.stdout.write('history-pressure-offload-regression PASS\n');
@@ -1,48 +0,0 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
- const vm = require('node:vm');
7
-
8
- const bundlePath = path.resolve(__dirname, '..', 'blun.mjs');
9
- const source = fs.readFileSync(bundlePath, 'utf8');
10
- const match = source.match(
11
- /async function blunWaitForInitialMcpBudget\(mcp, signal\) \{[\s\S]*?\n\}/u,
12
- );
13
- assert.ok(match, 'the bundle must contain the bounded initial MCP wait helper');
14
- assert.match(
15
- source,
16
- /await blunWaitForInitialMcpBudget\(this\.agent\.mcp, signal\)/u,
17
- 'tool-capable turns must use the bounded initial MCP wait helper',
18
- );
19
-
20
- const context = vm.createContext({ setTimeout });
21
- vm.runInContext(`${match[0]}; globalThis.waitForInitialMcp = blunWaitForInitialMcpBudget;`, context);
22
-
23
- let initialLoadFinished = false;
24
- const mcp = {
25
- waitForInitialLoad() {
26
- return new Promise((resolve) => {
27
- const timer = setTimeout(() => {
28
- initialLoadFinished = true;
29
- resolve();
30
- }, 20_000);
31
- timer.unref();
32
- });
33
- },
34
- };
35
-
36
- (async () => {
37
- const startedAt = performance.now();
38
- await context.waitForInitialMcp(mcp, undefined);
39
- const elapsedMs = performance.now() - startedAt;
40
-
41
- assert.ok(elapsedMs >= 2_300, `wait budget returned too early: ${elapsedMs} ms`);
42
- assert.ok(elapsedMs < 3_200, `slow optional MCP blocked the turn: ${elapsedMs} ms`);
43
- assert.equal(initialLoadFinished, false, 'the slow MCP must continue loading in the background');
44
- process.stdout.write(`MCP startup wait regression: PASS (${Math.round(elapsedMs)} ms)\n`);
45
- })().catch((error) => {
46
- process.stderr.write(`${error.stack || error}\n`);
47
- process.exitCode = 1;
48
- });
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('node:fs');
5
- const crypto = require('node:crypto');
6
- const path = require('node:path');
7
- const {
8
- comparePackagePaths,
9
- listTarGzPaths,
10
- } = require('../bin/package-regression-policy.cjs');
11
-
12
- const [, , previousTarball, currentTarball, allowlistPath = path.join(__dirname, '..', 'release-planned-removals.json')] = process.argv;
13
- if (!previousTarball || !currentTarball) {
14
- console.error('Usage: node scripts/check-package-regression.js <previous.tgz> <current.tgz> [planned-removals.json]');
15
- process.exit(2);
16
- }
17
-
18
- const planned = JSON.parse(fs.readFileSync(allowlistPath, 'utf8'));
19
- const comparison = comparePackagePaths(
20
- listTarGzPaths(previousTarball),
21
- listTarGzPaths(currentTarball),
22
- planned,
23
- );
24
- const sha256 = (filePath) => crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
25
- const result = {
26
- ok: comparison.unexpectedRemovals.length === 0,
27
- previous: { path: path.resolve(previousTarball), sha256: sha256(previousTarball) },
28
- current: { path: path.resolve(currentTarball), sha256: sha256(currentTarball) },
29
- checkedStablePaths: comparison.checkedStablePaths,
30
- currentStablePaths: comparison.currentStablePaths,
31
- removedCount: comparison.unexpectedRemovals.length,
32
- unexpectedRemovals: comparison.unexpectedRemovals,
33
- };
34
- if (!result.ok) {
35
- console.error(JSON.stringify(result, null, 2));
36
- process.exit(1);
37
- }
38
- console.log(JSON.stringify(result, null, 2));
@@ -1,53 +0,0 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const os = require('node:os');
6
- const path = require('node:path');
7
-
8
- const { installManagedAgentSpinePlugin } = require('../bin/plugin-bootstrap');
9
-
10
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'blun-plugin-startup-'));
11
- const packageRoot = path.join(root, 'package');
12
- const sourceRoot = path.join(packageRoot, 'agent-spine-plugin');
13
- const profileA = path.join(root, 'profiles', 'a');
14
- const profileB = path.join(root, 'profiles', 'b');
15
-
16
- fs.mkdirSync(path.join(sourceRoot, 'src'), { recursive: true });
17
- fs.writeFileSync(path.join(sourceRoot, 'package.json'), '{"name":"agent-spine"}\n');
18
- fs.writeFileSync(path.join(sourceRoot, 'src', 'mcp.js'), 'export const ready = true;\n');
19
-
20
- const originalReadFileSync = fs.readFileSync;
21
- let sourceReads = 0;
22
- fs.readFileSync = function countedReadFileSync(filePath, ...args) {
23
- const absolutePath = path.resolve(String(filePath));
24
- if (absolutePath === sourceRoot || absolutePath.startsWith(`${sourceRoot}${path.sep}`)) {
25
- sourceReads += 1;
26
- }
27
- return originalReadFileSync.call(this, filePath, ...args);
28
- };
29
-
30
- try {
31
- const first = installManagedAgentSpinePlugin({ packageRoot, blunDir: profileA });
32
- installManagedAgentSpinePlugin({ packageRoot, blunDir: profileA });
33
- installManagedAgentSpinePlugin({ packageRoot, blunDir: profileB });
34
- installManagedAgentSpinePlugin({ packageRoot, blunDir: profileB });
35
-
36
- assert.equal(
37
- sourceReads,
38
- 2,
39
- 'one launcher process must read each managed source file only once',
40
- );
41
-
42
- fs.writeFileSync(path.join(first.pluginRoot, 'src', 'mcp.js'), 'tampered\n');
43
- assert.throws(
44
- () => installManagedAgentSpinePlugin({ packageRoot, blunDir: profileA }),
45
- /digest mismatch/,
46
- 'source caching must not weaken installed-tree verification',
47
- );
48
-
49
- process.stdout.write('Plugin startup regression gate: PASS\n');
50
- } finally {
51
- fs.readFileSync = originalReadFileSync;
52
- fs.rmSync(root, { recursive: true, force: true });
53
- }