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
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
const assert = require('node:assert/strict');
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const { enqueueTelegramAddressed } = require('../bin/telegram-addressed-priority.cjs');
|
|
5
|
-
const { enqueueTelegramBotPriority } = require('../bin/telegram-bot-priority.cjs');
|
|
6
|
-
const { enqueueTelegramDirect } = require('../bin/telegram-direct-focus-policy.cjs');
|
|
7
|
-
const { enqueueTelegramUrgent } = require('../bin/telegram-urgent-policy.cjs');
|
|
8
|
-
|
|
9
|
-
const bundle = fs.readFileSync(path.join(__dirname, '..', 'blun.mjs'), 'utf8');
|
|
10
|
-
|
|
11
|
-
function between(start, end) {
|
|
12
|
-
const startIndex = bundle.indexOf(start);
|
|
13
|
-
assert.notEqual(startIndex, -1, `missing bundle marker: ${start}`);
|
|
14
|
-
const endIndex = bundle.indexOf(end, startIndex + start.length);
|
|
15
|
-
assert.notEqual(endIndex, -1, `missing bundle marker: ${end}`);
|
|
16
|
-
return bundle.slice(startIndex, endIndex);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function method(name, nextName) {
|
|
20
|
-
const source = between(`\n\t${name}(`, `\n\t${nextName}(`).trim();
|
|
21
|
-
return Function(`"use strict"; return ({${source}}).${name};`)();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const ctrlS = between('\n\t\teditor.onCtrlS = () => {', '\n\t\teditor.onCtrlB =');
|
|
25
|
-
assert.match(ctrlS, /host\.flushQueuedMessages\(/);
|
|
26
|
-
assert.doesNotMatch(ctrlS, /toggleTodoPanelExpansion|shortcut_todo_expand/);
|
|
27
|
-
|
|
28
|
-
const escape = between('\n\t\teditor.onEscape =', '\n\t\teditor.onShiftTab =');
|
|
29
|
-
const releaseIndex = escape.indexOf('host.releaseNextQueuedMessage()');
|
|
30
|
-
const cancelIndex = escape.indexOf('host.markCurrentRunCancelledByUser()');
|
|
31
|
-
assert.ok(releaseIndex >= 0, 'Escape must release queued work');
|
|
32
|
-
assert.ok(cancelIndex > releaseIndex, 'Escape must release queued work before cancelling the run');
|
|
33
|
-
|
|
34
|
-
const releaseNextQueuedMessage = method('releaseNextQueuedMessage', 'markCurrentRunCancelledByUser');
|
|
35
|
-
const releaseHost = {
|
|
36
|
-
queueSteerInFlight: undefined,
|
|
37
|
-
queueFlushBatchRemaining: 0,
|
|
38
|
-
state: {
|
|
39
|
-
queuedMessages: [{ text: 'one' }, { text: 'two' }, { text: 'three' }],
|
|
40
|
-
ui: { requestRender() {} },
|
|
41
|
-
},
|
|
42
|
-
steerQueueFlushPrefixCalls: 0,
|
|
43
|
-
updateQueueDisplayCalls: 0,
|
|
44
|
-
steerQueueFlushPrefix() { this.steerQueueFlushPrefixCalls += 1; },
|
|
45
|
-
updateQueueDisplay() { this.updateQueueDisplayCalls += 1; },
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
assert.equal(releaseNextQueuedMessage.call(releaseHost), true);
|
|
49
|
-
assert.equal(releaseHost.queueFlushBatchRemaining, 1);
|
|
50
|
-
assert.equal(releaseNextQueuedMessage.call(releaseHost), true);
|
|
51
|
-
assert.equal(releaseHost.queueFlushBatchRemaining, 2);
|
|
52
|
-
assert.equal(releaseNextQueuedMessage.call(releaseHost), true);
|
|
53
|
-
assert.equal(releaseHost.queueFlushBatchRemaining, 3);
|
|
54
|
-
assert.equal(releaseNextQueuedMessage.call(releaseHost), false);
|
|
55
|
-
assert.equal(releaseHost.queueFlushBatchRemaining, 3);
|
|
56
|
-
assert.equal(releaseHost.steerQueueFlushPrefixCalls, 3);
|
|
57
|
-
|
|
58
|
-
const flushQueuedMessages = method('flushQueuedMessages', 'steerQueueFlushPrefix');
|
|
59
|
-
const flushHost = {
|
|
60
|
-
queueSteerInFlight: { items: [{ text: 'already accepted' }] },
|
|
61
|
-
queueFlushBatchRemaining: 0,
|
|
62
|
-
state: {
|
|
63
|
-
queuedMessages: [{ text: 'one' }, { text: 'two' }, { text: 'three' }],
|
|
64
|
-
editor: { setText() {}, inputMode: 'prompt' },
|
|
65
|
-
},
|
|
66
|
-
steerQueueFlushPrefixCalls: 0,
|
|
67
|
-
scheduleQueueDrainCalls: 0,
|
|
68
|
-
steerQueueFlushPrefix() { this.steerQueueFlushPrefixCalls += 1; },
|
|
69
|
-
scheduleQueueDrain() { this.scheduleQueueDrainCalls += 1; },
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
flushQueuedMessages.call(flushHost, '', 'prompt');
|
|
73
|
-
assert.equal(flushHost.queueFlushBatchRemaining, 4);
|
|
74
|
-
assert.equal(flushHost.steerQueueFlushPrefixCalls, 1);
|
|
75
|
-
assert.equal(flushHost.scheduleQueueDrainCalls, 1);
|
|
76
|
-
|
|
77
|
-
const commitQueuedSteer = method('commitQueuedSteer', 'recoverRejectedActiveSteer');
|
|
78
|
-
let commits = 0;
|
|
79
|
-
const inFlight = {
|
|
80
|
-
items: [{ text: 'telegram message' }],
|
|
81
|
-
confirmationTimer: undefined,
|
|
82
|
-
onCommit() { commits += 1; },
|
|
83
|
-
};
|
|
84
|
-
const commitHost = {
|
|
85
|
-
queueSteerInFlight: inFlight,
|
|
86
|
-
updateQueueDisplay() {},
|
|
87
|
-
state: { ui: { requestRender() {} } },
|
|
88
|
-
steerQueueFlushPrefix() {},
|
|
89
|
-
scheduleQueueDrain() {},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
commitQueuedSteer.call(commitHost, inFlight);
|
|
93
|
-
commitQueuedSteer.call(commitHost, inFlight);
|
|
94
|
-
assert.equal(commits, 1);
|
|
95
|
-
assert.equal(commitHost.queueSteerInFlight, undefined);
|
|
96
|
-
|
|
97
|
-
const acceptQueuedSteer = method('acceptQueuedSteer', 'commitQueuedSteerIfReady');
|
|
98
|
-
let acceptedCommits = 0;
|
|
99
|
-
const acceptedInFlight = {
|
|
100
|
-
items: [{ channelDeliveryTrace: { messageId: '63508' } }],
|
|
101
|
-
accepted: false,
|
|
102
|
-
stepStarted: false,
|
|
103
|
-
turnEnded: false,
|
|
104
|
-
onCommit() { acceptedCommits += 1; },
|
|
105
|
-
};
|
|
106
|
-
const acceptedHost = {
|
|
107
|
-
queueSteerInFlight: acceptedInFlight,
|
|
108
|
-
traced: [],
|
|
109
|
-
traceTelegramDelivery(event) { this.traced.push(event); },
|
|
110
|
-
commitQueuedSteer(item) {
|
|
111
|
-
assert.equal(item, acceptedInFlight);
|
|
112
|
-
if (this.queueSteerInFlight !== item) return;
|
|
113
|
-
this.queueSteerInFlight = undefined;
|
|
114
|
-
item.onCommit();
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
assert.equal(acceptQueuedSteer.call(acceptedHost, acceptedInFlight, {
|
|
119
|
-
accepted: true,
|
|
120
|
-
duplicate: false,
|
|
121
|
-
turnId: 77,
|
|
122
|
-
}, true), true);
|
|
123
|
-
assert.equal(acceptedCommits, 1, 'durably accepted steer must commit before a delayed step event');
|
|
124
|
-
assert.equal(acceptedHost.queueSteerInFlight, undefined);
|
|
125
|
-
assert.equal(acceptedHost.traced.length, 1);
|
|
126
|
-
assert.equal(acceptedHost.traced[0].stage, 'accepted');
|
|
127
|
-
|
|
128
|
-
const recoverRejectedActiveSteer = method('recoverRejectedActiveSteer', 'quarantineQueuedSteer');
|
|
129
|
-
let restored = 0;
|
|
130
|
-
const rejectedInFlight = {
|
|
131
|
-
items: [{ text: 'keep me queued', channelDeliveryFailures: 2 }],
|
|
132
|
-
onRestore() { restored += 1; },
|
|
133
|
-
};
|
|
134
|
-
const rejectedHost = {
|
|
135
|
-
queueSteerInFlight: rejectedInFlight,
|
|
136
|
-
state: { queuedMessages: [], ui: { requestRender() {} } },
|
|
137
|
-
traced: [],
|
|
138
|
-
traceTelegramDelivery(event) { this.traced.push(event); },
|
|
139
|
-
updateQueueDisplay() {},
|
|
140
|
-
streamingUI: { hasActiveTurn() { return true; } },
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
assert.equal(recoverRejectedActiveSteer.call(rejectedHost, rejectedInFlight), false);
|
|
144
|
-
assert.equal(restored, 1);
|
|
145
|
-
assert.equal(rejectedHost.state.queuedMessages.length, 1, 'rejected steer must remain queued');
|
|
146
|
-
assert.equal(rejectedHost.state.queuedMessages[0].text, 'keep me queued');
|
|
147
|
-
assert.equal('channelDeliveryFailures' in rejectedHost.state.queuedMessages[0], false);
|
|
148
|
-
|
|
149
|
-
const channelDelivery = between('\n\tasync deliverQueuedChannelHead() {', '\n\tinstallChannelReplyGuard(');
|
|
150
|
-
assert.match(
|
|
151
|
-
channelDelivery,
|
|
152
|
-
/if \(!result\.accepted\) return this\.recoverRejectedActiveSteer\(inFlight\);[\s\S]*return this\.acceptQueuedSteer\(inFlight, result, true\);/,
|
|
153
|
-
'channel delivery must commit a durably accepted active steer',
|
|
154
|
-
);
|
|
155
|
-
assert.doesNotMatch(
|
|
156
|
-
channelDelivery,
|
|
157
|
-
/channel steer confirmation timed out|CHANNEL_STEER_CONFIRM_TIMEOUT_MS/,
|
|
158
|
-
'durably accepted channel messages must not be restored by a wall-clock timeout',
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
const ctrlSDelivery = between('\n\tsteerQueueFlushPrefix() {', '\n\tdrainOneQueuedMessage() {');
|
|
162
|
-
assert.match(
|
|
163
|
-
ctrlSDelivery,
|
|
164
|
-
/if \(!result\.accepted\) this\.recoverRejectedActiveSteer\(inFlight\);[\s\S]*else this\.acceptQueuedSteer\(inFlight, result\);/,
|
|
165
|
-
'Ctrl+S delivery must use the same durable acceptance rule',
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
const priorityQueue = [{ id: 'normal-a' }, { id: 'normal-b' }];
|
|
169
|
-
enqueueTelegramBotPriority(priorityQueue, { id: 'bot-a', channelBotPriority: true });
|
|
170
|
-
enqueueTelegramBotPriority(priorityQueue, { id: 'bot-b', channelBotPriority: true });
|
|
171
|
-
enqueueTelegramAddressed(priorityQueue, { id: 'addressed-a', channelAddressed: true });
|
|
172
|
-
enqueueTelegramAddressed(priorityQueue, { id: 'addressed-b', channelAddressed: true });
|
|
173
|
-
enqueueTelegramDirect(priorityQueue, { id: 'direct-a', channelDirect: true });
|
|
174
|
-
enqueueTelegramDirect(priorityQueue, { id: 'direct-b', channelDirect: true });
|
|
175
|
-
enqueueTelegramUrgent(priorityQueue, { id: 'urgent-a', channelUrgent: true });
|
|
176
|
-
enqueueTelegramUrgent(priorityQueue, { id: 'urgent-b', channelUrgent: true });
|
|
177
|
-
assert.deepEqual(
|
|
178
|
-
priorityQueue.map((item) => item.id),
|
|
179
|
-
[
|
|
180
|
-
'urgent-a', 'urgent-b',
|
|
181
|
-
'direct-a', 'direct-b',
|
|
182
|
-
'addressed-a', 'addressed-b',
|
|
183
|
-
'bot-a', 'bot-b',
|
|
184
|
-
'normal-a', 'normal-b',
|
|
185
|
-
],
|
|
186
|
-
'Telegram queue priority must be urgent, direct, addressed, bot, normal with stable FIFO inside each class',
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
console.log('queue controls regression: PASS');
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
|
|
6
|
-
const DEFAULT_MANIFEST_URL = 'https://chat.blun.ai/blun-code-version.json';
|
|
7
|
-
const DEFAULT_DOCS_URL = 'https://docs.blun.ai/changelog.html';
|
|
8
|
-
const DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
9
|
-
const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
10
|
-
|
|
11
|
-
function assert(condition, message) {
|
|
12
|
-
if (!condition) throw new Error(message);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function readUtf8(filePath) {
|
|
16
|
-
return fs.readFileSync(filePath, 'utf8');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function firstChangelogVersion(changelog) {
|
|
20
|
-
return /^##\s+(\d+\.\d+\.\d+)\b/mu.exec(changelog)?.[1] || null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function checkLocalReleaseMetadata(root = path.resolve(__dirname, '..')) {
|
|
24
|
-
const packageJson = JSON.parse(readUtf8(path.join(root, 'package.json')));
|
|
25
|
-
const version = String(packageJson.version || '');
|
|
26
|
-
const packageName = String(packageJson.name || '');
|
|
27
|
-
assert(/^\d+\.\d+\.\d+$/u.test(version), 'RELEASE_VERSION_INVALID');
|
|
28
|
-
assert(packageName.length > 0, 'RELEASE_PACKAGE_NAME_MISSING');
|
|
29
|
-
|
|
30
|
-
const changelogVersion = firstChangelogVersion(readUtf8(path.join(root, 'CHANGELOG.md')));
|
|
31
|
-
assert(changelogVersion === version, `CHANGELOG_VERSION_MISMATCH:${changelogVersion || 'missing'}!=${version}`);
|
|
32
|
-
|
|
33
|
-
const installNeedle = `${packageName}@${version}`;
|
|
34
|
-
for (const fileName of ['README.md', 'LIESMICH.txt']) {
|
|
35
|
-
assert(readUtf8(path.join(root, fileName)).includes(installNeedle), `INSTALL_VERSION_MISMATCH:${fileName}`);
|
|
36
|
-
}
|
|
37
|
-
return { packageName, version };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function fetchLimited(url, kind) {
|
|
41
|
-
const response = await fetch(url, {
|
|
42
|
-
headers: { accept: kind === 'json' ? 'application/json' : 'text/html' },
|
|
43
|
-
redirect: 'follow',
|
|
44
|
-
signal: AbortSignal.timeout(15_000),
|
|
45
|
-
});
|
|
46
|
-
assert(response.ok, `RELEASE_FETCH_FAILED:${response.status}:${url}`);
|
|
47
|
-
const declaredLength = Number(response.headers.get('content-length'));
|
|
48
|
-
assert(!Number.isFinite(declaredLength) || declaredLength <= MAX_RESPONSE_BYTES, `RELEASE_RESPONSE_TOO_LARGE:${url}`);
|
|
49
|
-
const body = await response.text();
|
|
50
|
-
assert(Buffer.byteLength(body, 'utf8') <= MAX_RESPONSE_BYTES, `RELEASE_RESPONSE_TOO_LARGE:${url}`);
|
|
51
|
-
return kind === 'json' ? JSON.parse(body) : body;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function checkExternalReleaseMetadata(local, options = {}) {
|
|
55
|
-
const cacheBust = `cb=${Date.now()}`;
|
|
56
|
-
const registryUrl = options.registryUrl || DEFAULT_REGISTRY_URL;
|
|
57
|
-
const manifestUrl = `${options.manifestUrl || DEFAULT_MANIFEST_URL}?${cacheBust}`;
|
|
58
|
-
const docsUrl = `${options.docsUrl || DEFAULT_DOCS_URL}?${cacheBust}`;
|
|
59
|
-
const encodedName = encodeURIComponent(local.packageName);
|
|
60
|
-
const [tags, metadata, manifest, docs] = await Promise.all([
|
|
61
|
-
fetchLimited(`${registryUrl}/-/package/${encodedName}/dist-tags`, 'json'),
|
|
62
|
-
fetchLimited(`${registryUrl}/${encodedName}/${local.version}`, 'json'),
|
|
63
|
-
fetchLimited(manifestUrl, 'json'),
|
|
64
|
-
fetchLimited(docsUrl, 'html'),
|
|
65
|
-
]);
|
|
66
|
-
|
|
67
|
-
assert(tags.latest === local.version, `NPM_LATEST_MISMATCH:${tags.latest || 'missing'}!=${local.version}`);
|
|
68
|
-
assert(metadata.version === local.version, `NPM_VERSION_MISMATCH:${metadata.version || 'missing'}!=${local.version}`);
|
|
69
|
-
assert(manifest.name === local.packageName, `MANIFEST_PACKAGE_MISMATCH:${manifest.name || 'missing'}!=${local.packageName}`);
|
|
70
|
-
assert(manifest.latest === local.version, `MANIFEST_VERSION_MISMATCH:${manifest.latest || 'missing'}!=${local.version}`);
|
|
71
|
-
assert(typeof metadata.dist?.integrity === 'string' && metadata.dist.integrity.length > 0, 'NPM_INTEGRITY_MISSING');
|
|
72
|
-
assert(manifest.integrity === metadata.dist.integrity, 'MANIFEST_INTEGRITY_MISMATCH');
|
|
73
|
-
assert(docs.includes(`<title>BLUN King ${local.version} `), `PUBLIC_CHANGELOG_VERSION_MISMATCH:${local.version}`);
|
|
74
|
-
return { ...local, integrity: metadata.dist.integrity };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function parseArgs(argv) {
|
|
78
|
-
const external = argv.includes('--external');
|
|
79
|
-
const rootArg = argv.find((arg) => arg.startsWith('--root='));
|
|
80
|
-
return { external, root: rootArg ? path.resolve(rootArg.slice('--root='.length)) : undefined };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function main(argv = process.argv.slice(2)) {
|
|
84
|
-
const options = parseArgs(argv);
|
|
85
|
-
const local = checkLocalReleaseMetadata(options.root);
|
|
86
|
-
const result = options.external ? await checkExternalReleaseMetadata(local) : local;
|
|
87
|
-
process.stdout.write(`release-metadata PASS ${JSON.stringify(result)}\n`);
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (require.main === module) {
|
|
92
|
-
main().catch((error) => {
|
|
93
|
-
process.stderr.write(`release-metadata BLOCK ${error.message}\n`);
|
|
94
|
-
process.exitCode = 1;
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
module.exports = {
|
|
99
|
-
checkExternalReleaseMetadata,
|
|
100
|
-
checkLocalReleaseMetadata,
|
|
101
|
-
firstChangelogVersion,
|
|
102
|
-
main,
|
|
103
|
-
};
|
|
@@ -1,76 +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 rootArg = process.argv.find((value) => value.startsWith('--root='));
|
|
9
|
-
const packageRoot = rootArg ? path.resolve(rootArg.slice('--root='.length)) : path.resolve(__dirname, '..');
|
|
10
|
-
const bundle = fs.readFileSync(path.join(packageRoot, 'blun.mjs'), 'utf8');
|
|
11
|
-
const reloadStart = bundle.indexOf('async reloadSession(input) {');
|
|
12
|
-
const bootstrapCall = bundle.indexOf('installBundledAgentSpineForReload({ blunDir: this.homeDir });', reloadStart);
|
|
13
|
-
const pluginReload = bundle.indexOf('await this.reloadPlugins({});', reloadStart);
|
|
14
|
-
|
|
15
|
-
assert.notEqual(reloadStart, -1, 'reloadSession must exist');
|
|
16
|
-
assert.ok(
|
|
17
|
-
bootstrapCall > reloadStart && bootstrapCall < pluginReload,
|
|
18
|
-
'/reload must materialize bundled AgentSpine before reloading the plugin catalogue',
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
const helperPath = path.join(packageRoot, 'bin', 'reload-plugin-bootstrap.cjs');
|
|
22
|
-
assert.ok(fs.existsSync(helperPath), 'reload plugin bootstrap helper must be packaged');
|
|
23
|
-
const { installBundledAgentSpineForReload } = require(helperPath);
|
|
24
|
-
|
|
25
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'blun-reload-agent-spine-'));
|
|
26
|
-
const profile = path.join(root, 'profile');
|
|
27
|
-
const pluginsDir = path.join(profile, 'plugins');
|
|
28
|
-
const installedFile = path.join(pluginsDir, 'installed.json');
|
|
29
|
-
const unrelatedRoot = path.join(root, 'unrelated-plugin');
|
|
30
|
-
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
31
|
-
fs.mkdirSync(unrelatedRoot, { recursive: true });
|
|
32
|
-
fs.writeFileSync(installedFile, `${JSON.stringify({
|
|
33
|
-
version: 1,
|
|
34
|
-
plugins: [{
|
|
35
|
-
id: 'unrelated',
|
|
36
|
-
root: unrelatedRoot,
|
|
37
|
-
source: 'local-path',
|
|
38
|
-
enabled: false,
|
|
39
|
-
capabilities: {},
|
|
40
|
-
}],
|
|
41
|
-
}, null, 2)}\n`);
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const first = installBundledAgentSpineForReload({
|
|
45
|
-
packageRoot,
|
|
46
|
-
blunDir: profile,
|
|
47
|
-
now: '2026-09-01T00:00:00.000Z',
|
|
48
|
-
});
|
|
49
|
-
const installed = JSON.parse(fs.readFileSync(installedFile, 'utf8'));
|
|
50
|
-
const agentSpine = installed.plugins.find((plugin) => plugin.id === 'agent-spine');
|
|
51
|
-
const unrelated = installed.plugins.find((plugin) => plugin.id === 'unrelated');
|
|
52
|
-
|
|
53
|
-
assert.ok(agentSpine, 'reload bootstrap must add bundled AgentSpine');
|
|
54
|
-
assert.equal(agentSpine.root, first.pluginRoot);
|
|
55
|
-
assert.equal(agentSpine.enabled, true);
|
|
56
|
-
assert.equal(agentSpine.capabilities.mcpServers['agent-spine'].enabled, true);
|
|
57
|
-
assert.equal(unrelated.root, unrelatedRoot, 'reload bootstrap must preserve unrelated plugins');
|
|
58
|
-
assert.equal(unrelated.enabled, false, 'reload bootstrap must preserve unrelated plugin state');
|
|
59
|
-
assert.equal(
|
|
60
|
-
fs.readFileSync(path.join(agentSpine.root, 'package.json'), 'utf8'),
|
|
61
|
-
fs.readFileSync(path.join(packageRoot, 'agent-spine-plugin', 'package.json'), 'utf8'),
|
|
62
|
-
'managed AgentSpine must come from the current package bundle',
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const beforeSecondRun = fs.readFileSync(installedFile, 'utf8');
|
|
66
|
-
const second = installBundledAgentSpineForReload({
|
|
67
|
-
packageRoot,
|
|
68
|
-
blunDir: profile,
|
|
69
|
-
now: '2026-09-01T00:01:00.000Z',
|
|
70
|
-
});
|
|
71
|
-
assert.equal(second.reused, true, 'an unchanged bundle must be reused');
|
|
72
|
-
assert.equal(fs.readFileSync(installedFile, 'utf8'), beforeSecondRun, 'idempotent reload must not rewrite installed.json');
|
|
73
|
-
process.stdout.write('Reload AgentSpine regression gate: PASS\n');
|
|
74
|
-
} finally {
|
|
75
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
76
|
-
}
|
|
@@ -1,102 +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
|
-
const { isWireOnlyProgrammaticEvent } = require('../bin/programmatic-context-isolation.cjs');
|
|
8
|
-
|
|
9
|
-
const bundlePath = process.env.BLUN_BUNDLE_UNDER_TEST
|
|
10
|
-
? path.resolve(process.env.BLUN_BUNDLE_UNDER_TEST)
|
|
11
|
-
: path.join(__dirname, '..', 'blun.mjs');
|
|
12
|
-
const bundle = fs.readFileSync(bundlePath, 'utf8');
|
|
13
|
-
|
|
14
|
-
function between(start, end) {
|
|
15
|
-
const startIndex = bundle.indexOf(start);
|
|
16
|
-
assert.notEqual(startIndex, -1, `missing bundle marker: ${start}`);
|
|
17
|
-
const endIndex = bundle.indexOf(end, startIndex + start.length);
|
|
18
|
-
assert.notEqual(endIndex, -1, `missing bundle marker: ${end}`);
|
|
19
|
-
return bundle.slice(startIndex, endIndex);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function compileMethod(name, nextName, dependencies = {}) {
|
|
23
|
-
const source = between(`\n\t\t${name}(`, `\n\t\t${nextName}(`).trim();
|
|
24
|
-
const names = Object.keys(dependencies);
|
|
25
|
-
const values = Object.values(dependencies);
|
|
26
|
-
return Function(...names, `"use strict"; return ({${source}}).${name};`)(...values);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function resolveStepEventUuid(openSteps, event) {
|
|
30
|
-
if (event.stepUuid !== undefined) return event.stepUuid;
|
|
31
|
-
let latest;
|
|
32
|
-
for (const uuid of openSteps.keys()) latest = uuid;
|
|
33
|
-
return latest;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveCompletedStepUuid(openSteps, event) {
|
|
37
|
-
if (event.uuid !== undefined) return event.uuid;
|
|
38
|
-
let latest;
|
|
39
|
-
for (const uuid of openSteps.keys()) latest = uuid;
|
|
40
|
-
return latest;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const recoverMissingOpenStep = compileMethod(
|
|
44
|
-
'recoverMissingOpenStep',
|
|
45
|
-
'appendLoopEvent',
|
|
46
|
-
{ resolveStepEventUuid },
|
|
47
|
-
);
|
|
48
|
-
const appendLoopEvent = compileMethod(
|
|
49
|
-
'appendLoopEvent',
|
|
50
|
-
'appendMessage',
|
|
51
|
-
{
|
|
52
|
-
projectLoopEventForRecord: (event) => event,
|
|
53
|
-
isWireOnlyProgrammaticEvent,
|
|
54
|
-
resolveCompletedStepUuid,
|
|
55
|
-
createToolMessage: (toolCallId, content) => ({ toolCallId, content, toolCalls: [] }),
|
|
56
|
-
toolResultOutputForModel: (result) => result.output,
|
|
57
|
-
estimateTokensForMessages: () => 0,
|
|
58
|
-
},
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const records = [];
|
|
62
|
-
const warnings = [];
|
|
63
|
-
const host = {
|
|
64
|
-
_history: [],
|
|
65
|
-
_tokenCount: 0,
|
|
66
|
-
tokenCountCoveredMessageCount: 0,
|
|
67
|
-
openSteps: new Map(),
|
|
68
|
-
pendingToolResultIds: new Set(),
|
|
69
|
-
agent: {
|
|
70
|
-
records: {
|
|
71
|
-
restoring: { time: Date.now() },
|
|
72
|
-
logRecord(record) { records.push(record); },
|
|
73
|
-
},
|
|
74
|
-
log: { warn(message, fields) { warnings.push({ message, fields }); } },
|
|
75
|
-
},
|
|
76
|
-
recoverMissingOpenStep,
|
|
77
|
-
pushHistory(...messages) { this._history.push(...messages); },
|
|
78
|
-
markPendingTokenEstimateDirty() {},
|
|
79
|
-
flushDeferredMessagesIfToolExchangeClosed() {},
|
|
80
|
-
closePendingToolResults() { return []; },
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// Exact shape measured after context.apply_compaction: no step.begin survives,
|
|
84
|
-
// then text, a tool call/result pair, and step.end continue the interrupted turn.
|
|
85
|
-
appendLoopEvent.call(host, { type: 'content.part', part: { type: 'text', text: 'continued after compaction' } });
|
|
86
|
-
appendLoopEvent.call(host, { type: 'tool.call', toolCallId: 'tool-1', name: 'Read', args: { path: 'SPEC.md' } });
|
|
87
|
-
appendLoopEvent.call(host, { type: 'tool.result', toolCallId: 'tool-1', result: { output: 'ok', isError: false } });
|
|
88
|
-
appendLoopEvent.call(host, { type: 'step.end', contextTokens: 100 });
|
|
89
|
-
|
|
90
|
-
assert.equal(warnings.length, 1, 'one missing step must produce one bounded repair warning');
|
|
91
|
-
assert.equal(warnings[0].fields.eventType, 'content.part');
|
|
92
|
-
assert.equal(warnings[0].fields.restoring, true);
|
|
93
|
-
assert.equal(host._history.length, 2, 'repair must retain the assistant continuation and its tool result');
|
|
94
|
-
assert.deepEqual(host._history[0].content, [{ type: 'text', text: 'continued after compaction' }]);
|
|
95
|
-
assert.equal(host._history[0].toolCalls.length, 1);
|
|
96
|
-
assert.equal(host._history[0].toolCalls[0].id, 'tool-1');
|
|
97
|
-
assert.equal(host._history[1].toolCallId, 'tool-1');
|
|
98
|
-
assert.equal(host.pendingToolResultIds.size, 0);
|
|
99
|
-
assert.equal(host.openSteps.size, 0);
|
|
100
|
-
assert.equal(records.filter((record) => record.type === 'context.append_loop_event').length, 4);
|
|
101
|
-
|
|
102
|
-
process.stdout.write('resume-replay-regression PASS\n');
|
|
@@ -1,43 +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 bundlePath = process.env.BLUN_BUNDLE_UNDER_TEST
|
|
9
|
-
? path.resolve(process.env.BLUN_BUNDLE_UNDER_TEST)
|
|
10
|
-
: path.join(__dirname, '..', 'blun.mjs');
|
|
11
|
-
const bundle = fs.readFileSync(bundlePath, 'utf8');
|
|
12
|
-
|
|
13
|
-
function between(start, end) {
|
|
14
|
-
const startIndex = bundle.indexOf(start);
|
|
15
|
-
assert.notEqual(startIndex, -1, `missing bundle marker: ${start}`);
|
|
16
|
-
const endIndex = bundle.indexOf(end, startIndex + start.length);
|
|
17
|
-
assert.notEqual(endIndex, -1, `missing bundle marker: ${end}`);
|
|
18
|
-
return bundle.slice(startIndex, endIndex);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function assertSafeCancel(source, label) {
|
|
22
|
-
assert.match(source, /\.cancel\(\)\.catch\(\(error\)\s*=>/,
|
|
23
|
-
`${label} must observe the cancellation promise`);
|
|
24
|
-
assert.match(source, /error instanceof BlunError && error\.code === ErrorCodes\.SESSION_NOT_FOUND/,
|
|
25
|
-
`${label} must treat an already-missing session as cancelled`);
|
|
26
|
-
assert.match(source, /showError\(formatErrorMessage\$2\(error\)\)/,
|
|
27
|
-
`${label} must still surface non-session errors`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
assertSafeCancel(
|
|
31
|
-
between('\n\tcancelCurrentStream() {', '\n\tcancelCurrentCompaction() {'),
|
|
32
|
-
'keyboard stream cancellation',
|
|
33
|
-
);
|
|
34
|
-
assertSafeCancel(
|
|
35
|
-
between('\n\tinjectTelegramRemoteCommand(', '\n\trunTelegramRemoteCommand('),
|
|
36
|
-
'preemptive Telegram reload cancellation',
|
|
37
|
-
);
|
|
38
|
-
assertSafeCancel(
|
|
39
|
-
between('\n\tenqueueSlashCommand(', '\n\tenqueueNormalUserInput('),
|
|
40
|
-
'preemptive slash-command cancellation',
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
process.stdout.write('session-cancel-regression PASS\n');
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
const assert = require('node:assert/strict');
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
|
|
5
|
-
const bundlePath = process.env.BLUN_BUNDLE_UNDER_TEST
|
|
6
|
-
? path.resolve(process.env.BLUN_BUNDLE_UNDER_TEST)
|
|
7
|
-
: path.join(__dirname, '..', 'blun.mjs');
|
|
8
|
-
const bundle = fs.readFileSync(bundlePath, 'utf8');
|
|
9
|
-
|
|
10
|
-
const rowsMatch = /function sessionRowsForPicker\(sessions, currentSessionId, currentSessionHasContent\) \{([\s\S]*?)\n\}/.exec(bundle);
|
|
11
|
-
assert(rowsMatch, 'SESSION_PICKER_RESUME_METRICS: sessionRowsForPicker is missing');
|
|
12
|
-
const sessionRowsForPicker = Function(
|
|
13
|
-
`return function sessionRowsForPicker(sessions, currentSessionId, currentSessionHasContent) {${rowsMatch[1]}\n};`,
|
|
14
|
-
)();
|
|
15
|
-
|
|
16
|
-
const rows = sessionRowsForPicker([{
|
|
17
|
-
id: 'session-a',
|
|
18
|
-
title: 'A',
|
|
19
|
-
lastPrompt: 'work',
|
|
20
|
-
workDir: 'C:/work',
|
|
21
|
-
updatedAt: 1,
|
|
22
|
-
metadata: {},
|
|
23
|
-
sessionDir: 'C:/state/session-a',
|
|
24
|
-
}], 'other', true);
|
|
25
|
-
assert.equal(rows[0].session_dir, 'C:/state/session-a');
|
|
26
|
-
|
|
27
|
-
const metricsMatch = /async function readSessionResumeMetrics\(session\) \{([\s\S]*?)\n\}\nasync function enrichSessionRowsForPicker/.exec(bundle);
|
|
28
|
-
assert(metricsMatch, 'SESSION_PICKER_RESUME_METRICS: readSessionResumeMetrics is missing');
|
|
29
|
-
|
|
30
|
-
function buildReader({ resumeStat, wireStat, checkpoint }) {
|
|
31
|
-
return Function(
|
|
32
|
-
'join',
|
|
33
|
-
'statSessionFile',
|
|
34
|
-
'loadResumeCheckpoint',
|
|
35
|
-
'SESSION_RESUME_STALE_AFTER_MS',
|
|
36
|
-
`return async function readSessionResumeMetrics(session) {${metricsMatch[1]}\n};`,
|
|
37
|
-
)(
|
|
38
|
-
path.join,
|
|
39
|
-
async (filePath) => filePath.endsWith('resume-checkpoint.json') ? resumeStat : wireStat,
|
|
40
|
-
async () => checkpoint,
|
|
41
|
-
60_000,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
(async () => {
|
|
46
|
-
const missing = await buildReader({ resumeStat: null, wireStat: null, checkpoint: null })({
|
|
47
|
-
session_dir: 'C:/state/session-a',
|
|
48
|
-
});
|
|
49
|
-
assert.deepEqual(missing, { status: 'missing' });
|
|
50
|
-
|
|
51
|
-
const invalid = await buildReader({
|
|
52
|
-
resumeStat: { size: 1_024, mtimeMs: 1_000 },
|
|
53
|
-
wireStat: { size: 2_048, mtimeMs: 1_000 },
|
|
54
|
-
checkpoint: { ok: false },
|
|
55
|
-
})({ session_dir: 'C:/state/session-a' });
|
|
56
|
-
assert.deepEqual(invalid, { status: 'invalid' });
|
|
57
|
-
|
|
58
|
-
const ok = await buildReader({
|
|
59
|
-
resumeStat: { size: 1_024, mtimeMs: 1_000 },
|
|
60
|
-
wireStat: { size: 2_048, mtimeMs: 1_500 },
|
|
61
|
-
checkpoint: {
|
|
62
|
-
ok: true,
|
|
63
|
-
offset: 2_048,
|
|
64
|
-
snapshot: { context: { history: [{}, {}, {}], tokenCount: 1_200 } },
|
|
65
|
-
},
|
|
66
|
-
})({ session_dir: 'C:/state/session-a' });
|
|
67
|
-
assert.deepEqual(ok, {
|
|
68
|
-
status: 'ok',
|
|
69
|
-
resumeBytes: 1_024,
|
|
70
|
-
wireBytes: 2_048,
|
|
71
|
-
messages: 3,
|
|
72
|
-
tokens: 1_200,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const stale = await buildReader({
|
|
76
|
-
resumeStat: { size: 1_024, mtimeMs: 1_000 },
|
|
77
|
-
wireStat: { size: 4_096, mtimeMs: 62_000 },
|
|
78
|
-
checkpoint: {
|
|
79
|
-
ok: true,
|
|
80
|
-
offset: 2_048,
|
|
81
|
-
snapshot: { context: { history: [{}], tokenCount: 500 } },
|
|
82
|
-
},
|
|
83
|
-
})({ session_dir: 'C:/state/session-a' });
|
|
84
|
-
assert.equal(stale.status, 'stale');
|
|
85
|
-
assert.equal(stale.wireBytes, 4_096);
|
|
86
|
-
|
|
87
|
-
const renderBlock = /const resume = session\.resume;([\s\S]*?)\n\t\treturn card;/.exec(bundle);
|
|
88
|
-
assert(renderBlock, 'SESSION_PICKER_RESUME_METRICS: resume metrics are not rendered');
|
|
89
|
-
assert.match(renderBlock[1], /formatByteSize\(resume\.resumeBytes\)/);
|
|
90
|
-
assert.match(renderBlock[1], /formatByteSize\(resume\.wireBytes\)/);
|
|
91
|
-
assert.match(renderBlock[1], /sessionPicker\.resume\.stale/);
|
|
92
|
-
|
|
93
|
-
console.log('session-picker-resume-metrics-regression PASS');
|
|
94
|
-
})().catch((error) => {
|
|
95
|
-
console.error(error);
|
|
96
|
-
process.exitCode = 1;
|
|
97
|
-
});
|