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.
Files changed (129) 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/compaction-transaction-policy.cjs +122 -0
  5. package/bin/context-performance-policy.cjs +2 -5
  6. package/bin/context-pressure-policy.cjs +20 -0
  7. package/bin/cron-run-output.cjs +45 -0
  8. package/bin/cron-run-store.cjs +145 -0
  9. package/bin/default-model-output-budget-policy.cjs +28 -0
  10. package/bin/durable-task-resume-policy.cjs +130 -0
  11. package/bin/durable-task-resume-runtime.cjs +117 -0
  12. package/bin/durable-task-resume-store.cjs +88 -0
  13. package/bin/editable-tool-approval-policy.cjs +540 -0
  14. package/bin/editable-tool-approval-runtime.cjs +99 -0
  15. package/bin/file-observation-policy.cjs +133 -0
  16. package/bin/html-to-research-markdown.cjs +146 -0
  17. package/bin/launcher-runtime.js +0 -1
  18. package/bin/micro-compaction-policy.cjs +64 -0
  19. package/bin/mnemo-connect-heartbeat.cjs +1 -3
  20. package/bin/programmatic-tool-runtime.mjs +330 -4
  21. package/bin/read-continuation-policy.cjs +36 -5
  22. package/bin/retry-checkpoint-policy.cjs +13 -0
  23. package/bin/scoped-cron-run-policy.cjs +358 -0
  24. package/bin/session-checkpoint-policy.cjs +25 -0
  25. package/bin/startup-preferences.cjs +4 -3
  26. package/bin/structured-agent-swarm-output.cjs +325 -0
  27. package/bin/subagent-context-fork-policy.cjs +155 -0
  28. package/bin/subagent-skill-policy.cjs +204 -0
  29. package/bin/telegram-approval-relay.cjs +2 -1
  30. package/bin/tool-file-persistence.cjs +141 -0
  31. package/bin/tool-result-offload-policy.cjs +25 -33
  32. package/bin/turn-thinking-policy.cjs +2 -26
  33. package/bin/update-notice.js +30 -18
  34. package/bin/user-message-offload-policy.cjs +3 -1
  35. package/blun.mjs +1564 -640
  36. package/codebase-index/README.md +12 -0
  37. package/codebase-index/codebase_index.py +129 -18
  38. package/package.json +23 -58
  39. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +1 -3
  40. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
  41. package/telegram-plugin/dist/bridge.mjs +8 -1
  42. package/CHANGELOG.md +0 -321
  43. package/agent-spine-plugin/CHANGELOG.md +0 -406
  44. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  45. package/agent-spine-plugin/README.md +0 -344
  46. package/agent-spine-plugin/SECURITY.md +0 -47
  47. package/agent-spine-plugin/docs/acceptance.md +0 -61
  48. package/agent-spine-plugin/docs/architecture.md +0 -183
  49. package/agent-spine-plugin/docs/attention.md +0 -121
  50. package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
  51. package/agent-spine-plugin/docs/channel-runtime.md +0 -92
  52. package/agent-spine-plugin/docs/coordination.md +0 -138
  53. package/agent-spine-plugin/docs/feed-transport.md +0 -99
  54. package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
  55. package/agent-spine-plugin/docs/harness-reference.md +0 -45
  56. package/agent-spine-plugin/docs/host-integration.md +0 -129
  57. package/agent-spine-plugin/docs/https-transport.md +0 -116
  58. package/agent-spine-plugin/docs/learning.md +0 -133
  59. package/agent-spine-plugin/docs/object-transport.md +0 -93
  60. package/agent-spine-plugin/docs/peer-transport.md +0 -88
  61. package/agent-spine-plugin/docs/preflight-recall.md +0 -69
  62. package/agent-spine-plugin/docs/preservation-contract.md +0 -53
  63. package/agent-spine-plugin/docs/quality-gates.md +0 -50
  64. package/agent-spine-plugin/docs/relationships.md +0 -73
  65. package/agent-spine-plugin/docs/releasing.md +0 -83
  66. package/agent-spine-plugin/docs/roadmap.md +0 -307
  67. package/agent-spine-plugin/docs/selfstarter.md +0 -88
  68. package/agent-spine-plugin/docs/session-briefing.md +0 -74
  69. package/agent-spine-plugin/docs/shared-memory.md +0 -259
  70. package/agent-spine-plugin/docs/source-roots.md +0 -86
  71. package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
  72. package/agent-spine-plugin/scripts/check-hosts.js +0 -195
  73. package/agent-spine-plugin/scripts/check-install.js +0 -569
  74. package/agent-spine-plugin/scripts/check-syntax.js +0 -29
  75. package/agent-spine-plugin/scripts/github-actions.js +0 -11
  76. package/agent-spine-plugin/scripts/release-check.js +0 -128
  77. package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
  78. package/agent-spine-plugin/scripts/run-checks.js +0 -46
  79. package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
  80. package/agent-spine-plugin/spine-example/1-identity.md +0 -12
  81. package/agent-spine-plugin/spine-example/2-voice.md +0 -6
  82. package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
  83. package/agent-spine-plugin/spine-example/4-history.md +0 -4
  84. package/bin/empty-response-retry-policy.cjs +0 -29
  85. package/bin/fredrik-glm-provider.cjs +0 -256
  86. package/bin/package-regression-policy.cjs +0 -77
  87. package/fredrik-glm-profile.toml.example +0 -26
  88. package/release-planned-removals.json +0 -15
  89. package/scripts/check-active-profile-plugin-startup.js +0 -36
  90. package/scripts/check-active-work-steer-regression.js +0 -46
  91. package/scripts/check-approval-observability-regression.js +0 -111
  92. package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
  93. package/scripts/check-bundled-agent-spine-regression.js +0 -48
  94. package/scripts/check-codebase-search-packaging-regression.js +0 -92
  95. package/scripts/check-copy-command-regression.js +0 -74
  96. package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
  97. package/scripts/check-current-turn-read-pin-regression.js +0 -94
  98. package/scripts/check-deepseek-native-max-regression.js +0 -49
  99. package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
  100. package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
  101. package/scripts/check-fredrik-glm-regression.js +0 -169
  102. package/scripts/check-historical-tool-result-preview-regression.js +0 -77
  103. package/scripts/check-history-pressure-offload-regression.js +0 -77
  104. package/scripts/check-mcp-startup-wait-budget.js +0 -48
  105. package/scripts/check-package-regression.js +0 -38
  106. package/scripts/check-plugin-startup-regression.js +0 -53
  107. package/scripts/check-programmatic-context-isolation-regression.js +0 -193
  108. package/scripts/check-programmatic-tool-regression.js +0 -294
  109. package/scripts/check-queue-controls-regression.js +0 -189
  110. package/scripts/check-release-metadata.js +0 -103
  111. package/scripts/check-reload-agent-spine-regression.js +0 -76
  112. package/scripts/check-resume-replay-regression.js +0 -102
  113. package/scripts/check-session-cancel-regression.js +0 -43
  114. package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
  115. package/scripts/check-session-start-hook-context-regression.js +0 -228
  116. package/scripts/check-shell-terminal-isolation-regression.js +0 -81
  117. package/scripts/check-slash-escape-regression.js +0 -89
  118. package/scripts/check-startup-swarm-command-regression.js +0 -24
  119. package/scripts/check-structured-subagent-output-regression.js +0 -331
  120. package/scripts/check-telegram-bridge-watchdog.js +0 -60
  121. package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
  122. package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
  123. package/scripts/check-todo-loop-regression.js +0 -78
  124. package/scripts/check-todo-progress-regression.js +0 -416
  125. package/scripts/check-todo-recovery-catalog-regression.js +0 -50
  126. package/scripts/check-tool-schema-capacity-regression.js +0 -40
  127. package/scripts/programmatic-tool-runtime.test.mjs +0 -365
  128. package/scripts/structured-subagent-output.test.cjs +0 -170
  129. /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
@@ -1,94 +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 packageRoot = process.env.BLUN_PACKAGE_UNDER_TEST
8
- ? path.resolve(process.env.BLUN_PACKAGE_UNDER_TEST)
9
- : path.resolve(__dirname, '..');
10
- const policy = require(path.join(packageRoot, 'bin', 'tool-result-offload-policy.cjs'));
11
- const bundle = fs.readFileSync(path.join(packageRoot, 'blun.mjs'), 'utf8');
12
-
13
- function assert(condition, message) {
14
- if (!condition) throw new Error(`CURRENT_TURN_READ_PIN_REGRESSION: ${message}`);
15
- }
16
-
17
- assert(
18
- typeof policy.currentTurnReadToolResultIds === 'function',
19
- 'currentTurnReadToolResultIds policy is missing',
20
- );
21
- assert(
22
- typeof policy.currentUserTurnStartIndex === 'function',
23
- 'currentUserTurnStartIndex policy is missing',
24
- );
25
-
26
- const history = [
27
- { role: 'user', origin: { kind: 'user' }, content: [{ type: 'text', text: 'old task' }] },
28
- {
29
- role: 'assistant',
30
- toolCalls: [{ id: 'old-read', name: 'Read', arguments: JSON.stringify({ path: 'old.md' }) }],
31
- },
32
- { role: 'tool', toolCallId: 'old-read', content: [{ type: 'text', text: 'old result' }] },
33
- { role: 'user', origin: { kind: 'injection' }, content: [{ type: 'text', text: 'hook' }] },
34
- { role: 'user', origin: { kind: 'user' }, content: [{ type: 'text', text: 'current task' }] },
35
- {
36
- role: 'assistant',
37
- toolCalls: [
38
- { id: 'task-read', name: 'Read', arguments: JSON.stringify({ path: 'task.md' }) },
39
- { id: 'shell-call', name: 'Bash', arguments: JSON.stringify({ command: 'pwd' }) },
40
- ],
41
- },
42
- { role: 'tool', toolCallId: 'task-read', content: [{ type: 'text', text: 'complete task' }] },
43
- { role: 'tool', toolCallId: 'shell-call', content: [{ type: 'text', text: '/workspace' }] },
44
- {
45
- role: 'assistant',
46
- toolCalls: [{ id: 'source-read', name: 'Read', arguments: JSON.stringify({ path: 'source.js' }) }],
47
- },
48
- { role: 'tool', toolCallId: 'source-read', content: [{ type: 'text', text: 'source slice' }] },
49
- { role: 'assistant', toolCalls: [] },
50
- { role: 'tool', toolCallId: 'unrelated-result', content: [{ type: 'text', text: 'other' }] },
51
- ];
52
-
53
- const pinned = policy.currentTurnReadToolResultIds(history);
54
- assert(pinned instanceof Set, 'policy must return a Set');
55
- assert(pinned.has('task-read'), 'complete task Read was not pinned');
56
- assert(pinned.has('source-read'), 'current-turn source Read was not pinned');
57
- assert(!pinned.has('old-read'), 'Read from a completed older user turn stayed pinned');
58
- assert(!pinned.has('shell-call'), 'non-Read tool result was pinned');
59
- assert(
60
- policy.currentUserTurnStartIndex(history) === 4,
61
- 'latest real user message was not selected as the current turn start',
62
- );
63
-
64
- const withoutUserTurn = policy.currentTurnReadToolResultIds([
65
- { role: 'user', origin: { kind: 'injection' }, content: [{ type: 'text', text: 'hook' }] },
66
- {
67
- role: 'assistant',
68
- toolCalls: [{ id: 'injected-read', name: 'Read', arguments: '{}' }],
69
- },
70
- ]);
71
- assert(withoutUserTurn.size === 0, 'injection-only history must not create a pinned user turn');
72
- assert(
73
- policy.currentUserTurnStartIndex(withoutUserTurn) === -1,
74
- 'injection-only history must not create a current user turn',
75
- );
76
-
77
- assert(
78
- bundle.includes('const pinnedReadIds = currentTurnReadToolResultIds(history);'),
79
- 'batch offload does not calculate current-turn Read pins',
80
- );
81
- assert(
82
- bundle.includes('&& !pinnedReadIds.has(message.toolCallId)'),
83
- 'batch offload does not exclude pinned Read results',
84
- );
85
- assert(
86
- bundle.includes('const currentTurnStart = currentUserTurnStartIndex(history);'),
87
- 'user-message offload does not locate the current turn',
88
- );
89
- assert(
90
- bundle.includes('if (historyIndex === currentTurnStart) continue;'),
91
- 'current user instruction can still be offloaded during its own turn',
92
- );
93
-
94
- process.stdout.write('current-turn-read-pin-regression PASS\n');
@@ -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));