blun-king-cli 9.1.526 → 9.1.536
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/LIESMICH.txt +36 -1
- package/README.md +35 -1
- package/bin/agent-resume-snapshot.cjs +31 -0
- package/bin/assistant-message-offload-policy.cjs +21 -2
- package/bin/codebase-search-runtime.cjs +23 -0
- package/bin/empty-response-retry-policy.cjs +29 -0
- package/bin/fredrik-glm-provider.cjs +256 -0
- package/bin/history-offload-pressure-policy.cjs +33 -0
- package/bin/programmatic-context-isolation.cjs +25 -0
- package/bin/programmatic-tool-runtime.mjs +301 -0
- package/bin/skill-activation-performance-policy.cjs +9 -0
- package/bin/structured-subagent-output.cjs +252 -0
- package/bin/telegram-direct-focus-policy.cjs +25 -1
- package/bin/todo-list-turn-policy.cjs +111 -1
- package/bin/tool-result-offload-policy.cjs +29 -0
- package/bin/turn-thinking-policy.cjs +6 -15
- package/bin/turn-tool-performance-policy.cjs +5 -4
- package/bin/user-message-offload-policy.cjs +10 -1
- package/blun.mjs +709 -127
- package/codebase-index/README.md +70 -0
- package/codebase-index/codebase_index.py +358 -0
- package/fredrik-glm-profile.toml.example +26 -0
- package/package.json +25 -3
- package/scripts/check-active-work-steer-regression.js +46 -0
- package/scripts/check-codebase-search-packaging-regression.js +92 -0
- package/scripts/check-copy-command-regression.js +74 -0
- package/scripts/check-current-turn-read-pin-mutation-regression.js +72 -0
- package/scripts/check-current-turn-read-pin-regression.js +94 -0
- package/scripts/check-deepseek-native-max-regression.js +49 -0
- package/scripts/check-empty-response-effort-downgrade-regression.js +48 -0
- package/scripts/check-fredrik-glm-mutation-regression.js +18 -0
- package/scripts/check-fredrik-glm-regression.js +169 -0
- package/scripts/check-history-pressure-offload-regression.js +77 -0
- package/scripts/check-programmatic-context-isolation-regression.js +193 -0
- package/scripts/check-programmatic-tool-regression.js +294 -0
- package/scripts/check-resume-replay-regression.js +2 -0
- package/scripts/check-startup-swarm-command-regression.js +24 -0
- package/scripts/check-structured-subagent-output-regression.js +331 -0
- package/scripts/check-telegram-direct-work-resume-regression.js +53 -0
- package/scripts/check-todo-progress-regression.js +416 -0
- package/scripts/check-tool-schema-capacity-regression.js +40 -0
- package/scripts/programmatic-tool-runtime.test.mjs +365 -0
- package/scripts/structured-subagent-output.test.cjs +170 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
const { readFileSync } = require('node:fs');
|
|
6
|
+
const { join } = require('node:path');
|
|
7
|
+
|
|
8
|
+
const root = join(__dirname, '..');
|
|
9
|
+
const bundlePath = join(root, 'blun.mjs');
|
|
10
|
+
|
|
11
|
+
function extractBetween(source, startNeedle, endNeedle) {
|
|
12
|
+
const start = source.indexOf(startNeedle);
|
|
13
|
+
assert.notEqual(start, -1, `missing source start: ${startNeedle}`);
|
|
14
|
+
const end = source.indexOf(endNeedle, start + startNeedle.length);
|
|
15
|
+
assert.notEqual(end, -1, `missing source end: ${endNeedle}`);
|
|
16
|
+
return source.slice(start, end);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function assertBundleContract(source) {
|
|
20
|
+
assert.match(source, /response_format: object\(\{/u, 'Agent input must expose response_format');
|
|
21
|
+
assert.match(
|
|
22
|
+
source,
|
|
23
|
+
/prepareStructuredResponseFormat\(args\.response_format, compileToolArgsValidator\)/u,
|
|
24
|
+
'response_format must reuse BLUN AJV compilation',
|
|
25
|
+
);
|
|
26
|
+
const preparationIndex = source.indexOf('prepareStructuredResponseFormat(args.response_format, compileToolArgsValidator)');
|
|
27
|
+
const launchIndex = source.indexOf('await this.subagentHost.spawn({', preparationIndex);
|
|
28
|
+
assert.ok(preparationIndex >= 0 && launchIndex > preparationIndex, 'schema compilation must precede child creation');
|
|
29
|
+
assert.match(source, /response_format is supported for foreground Agent runs only/u, 'structured background runs must be rejected');
|
|
30
|
+
assert.match(
|
|
31
|
+
source,
|
|
32
|
+
/const runInBackground = responseFormat === void 0 \? resolvedRunMode\.runInBackground : false/u,
|
|
33
|
+
'structured output must select a foreground run',
|
|
34
|
+
);
|
|
35
|
+
assert.match(
|
|
36
|
+
source,
|
|
37
|
+
/appendSystemReminder\(buildStructuredResponseFormatReminder\(options\.responseFormat\)/u,
|
|
38
|
+
'the schema reminder must remain child-local',
|
|
39
|
+
);
|
|
40
|
+
assert.match(
|
|
41
|
+
source,
|
|
42
|
+
/completeStructuredSubagentResult\(child, childId, profileName, options\.responseFormat, options\.signal\)/u,
|
|
43
|
+
'structured completion must validate before returning',
|
|
44
|
+
);
|
|
45
|
+
assert.match(
|
|
46
|
+
source,
|
|
47
|
+
/buildStructuredResponseRepairPrompt\(responseFormat, validation\.error\)[\s\S]{0,500}completeChildTurnWithMaxTokensHandoff\(child, signal\)/u,
|
|
48
|
+
'one repair turn must use the normal child completion path',
|
|
49
|
+
);
|
|
50
|
+
assert.match(
|
|
51
|
+
source,
|
|
52
|
+
/signal\.throwIfAborted\(\);[\s\S]{0,180}buildStructuredResponseRepairPrompt/u,
|
|
53
|
+
'cancellation must be checked before repair',
|
|
54
|
+
);
|
|
55
|
+
assert.match(source, /responseFormat: this\.responseFormat/u, 'timeout continuation must preserve the response format');
|
|
56
|
+
assert.match(
|
|
57
|
+
source,
|
|
58
|
+
/responseFormat === void 0 \? formatForegroundAgentSuccess\(handle, output\) : output/u,
|
|
59
|
+
'validated structured output must not be wrapped in free-form text',
|
|
60
|
+
);
|
|
61
|
+
assert.match(
|
|
62
|
+
source,
|
|
63
|
+
/message\.includes\(STRUCTURED_SUBAGENT_OUTPUT_ERROR\)/u,
|
|
64
|
+
'a second invalid response must preserve the resume hint',
|
|
65
|
+
);
|
|
66
|
+
const swarmRegion = extractBetween(
|
|
67
|
+
source,
|
|
68
|
+
'//#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-swarm.ts',
|
|
69
|
+
'//#region ../../packages/agent-core/src/tools/builtin/collaboration/ask-user.md?raw',
|
|
70
|
+
);
|
|
71
|
+
assert.doesNotMatch(swarmRegion, /response_format/u, 'AgentSwarm must not silently accept response_format');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildAgentToolClass(source) {
|
|
75
|
+
const assignment = extractBetween(source, 'AgentTool = class {', '\n\t};\n\tUSER_INTERRUPTED_SUBAGENT_MESSAGE');
|
|
76
|
+
const classSource = `${assignment.slice('AgentTool = '.length)}\n\t}`;
|
|
77
|
+
return Function(
|
|
78
|
+
'AgentToolInputSchema',
|
|
79
|
+
'toInputJsonSchema',
|
|
80
|
+
'buildSubagentDescriptions',
|
|
81
|
+
'agent_default',
|
|
82
|
+
'agent_background_enabled_default',
|
|
83
|
+
'agent_background_disabled_default',
|
|
84
|
+
'resolveSubagentRunMode',
|
|
85
|
+
'ToolAccesses',
|
|
86
|
+
'matchesGlobRuleSubject',
|
|
87
|
+
'prepareStructuredResponseFormat',
|
|
88
|
+
'compileToolArgsValidator',
|
|
89
|
+
'BACKGROUND_AGENT_UNAVAILABLE',
|
|
90
|
+
'formatForegroundAgentSuccess',
|
|
91
|
+
'formatForegroundAgentFailure',
|
|
92
|
+
'USER_INTERRUPTED_SUBAGENT_MESSAGE',
|
|
93
|
+
'launchErrorMessage',
|
|
94
|
+
`"use strict"; return (${classSource});`,
|
|
95
|
+
)(
|
|
96
|
+
{},
|
|
97
|
+
() => ({}),
|
|
98
|
+
() => '',
|
|
99
|
+
'agent',
|
|
100
|
+
'background enabled',
|
|
101
|
+
'background disabled',
|
|
102
|
+
({ allowBackground }) => ({ runInBackground: allowBackground === true }),
|
|
103
|
+
{ none: () => ({}) },
|
|
104
|
+
() => true,
|
|
105
|
+
require('../bin/structured-subagent-output.cjs').prepareStructuredResponseFormat,
|
|
106
|
+
() => () => true,
|
|
107
|
+
'background unavailable',
|
|
108
|
+
(handle, result) => [
|
|
109
|
+
`agent_id: ${handle.agentId}`,
|
|
110
|
+
`actual_subagent_type: ${handle.profileName}`,
|
|
111
|
+
'status: completed',
|
|
112
|
+
'',
|
|
113
|
+
'[summary]',
|
|
114
|
+
result,
|
|
115
|
+
].join('\n'),
|
|
116
|
+
() => 'failed',
|
|
117
|
+
'interrupted',
|
|
118
|
+
(error) => String(error),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function exerciseBundledAgentTool(source) {
|
|
123
|
+
const AgentTool = buildAgentToolClass(source);
|
|
124
|
+
const backgroundManager = {
|
|
125
|
+
getTask: () => ({ status: 'completed' }),
|
|
126
|
+
readOutput: async () => 'free text',
|
|
127
|
+
};
|
|
128
|
+
const tool = new AgentTool({}, backgroundManager, {}, { allowBackground: true });
|
|
129
|
+
const unstructured = await tool.resolveExecution({ prompt: 'x', description: 'free' });
|
|
130
|
+
assert.equal(unstructured.display.background, true);
|
|
131
|
+
const structuredArgs = {
|
|
132
|
+
prompt: 'x',
|
|
133
|
+
description: 'structured',
|
|
134
|
+
response_format: { name: 'answer', schema: { type: 'object' } },
|
|
135
|
+
};
|
|
136
|
+
const structured = await tool.resolveExecution(structuredArgs);
|
|
137
|
+
assert.equal(structured.display.background, false);
|
|
138
|
+
|
|
139
|
+
const handle = { agentId: 'agent-1', profileName: 'coder' };
|
|
140
|
+
assert.equal(
|
|
141
|
+
(await tool.formatForegroundResult('task-1', handle, undefined)).output,
|
|
142
|
+
'agent_id: agent-1\nactual_subagent_type: coder\nstatus: completed\n\n[summary]\nfree text',
|
|
143
|
+
'free-form output must remain byte-compatible',
|
|
144
|
+
);
|
|
145
|
+
backgroundManager.readOutput = async () => '{"status":"completed"}';
|
|
146
|
+
assert.equal(
|
|
147
|
+
(await tool.formatForegroundResult('task-2', handle, {})).output,
|
|
148
|
+
'{"status":"completed"}',
|
|
149
|
+
'structured output must remain canonical JSON without a text wrapper',
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
let spawned = false;
|
|
153
|
+
tool.subagentHost = { spawn: async () => { spawned = true; throw new Error('must not spawn'); } };
|
|
154
|
+
const invalid = await tool.execution({
|
|
155
|
+
...structuredArgs,
|
|
156
|
+
response_format: { name: 'answer', schema: { type: 'array' } },
|
|
157
|
+
}, { toolCallId: 'call-1', signal: new AbortController().signal });
|
|
158
|
+
assert.equal(invalid.isError, true);
|
|
159
|
+
assert.match(invalid.output, /Invalid response_format/u);
|
|
160
|
+
assert.equal(spawned, false, 'invalid schema must fail before child creation');
|
|
161
|
+
|
|
162
|
+
const background = await tool.execution({ ...structuredArgs, run_in_background: true }, {
|
|
163
|
+
toolCallId: 'call-2',
|
|
164
|
+
signal: new AbortController().signal,
|
|
165
|
+
});
|
|
166
|
+
assert.equal(background.isError, true);
|
|
167
|
+
assert.match(background.output, /foreground Agent runs only/u);
|
|
168
|
+
assert.equal(spawned, false);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function exerciseBundledRepairFlow(source) {
|
|
172
|
+
const functionSource = extractBetween(
|
|
173
|
+
source,
|
|
174
|
+
'async function completeStructuredSubagentResult(',
|
|
175
|
+
'function shouldSuppressQueuedAttemptFailureEvent(',
|
|
176
|
+
);
|
|
177
|
+
const runtime = require('../bin/structured-subagent-output.cjs');
|
|
178
|
+
let currentText = '';
|
|
179
|
+
let completionCalls = 0;
|
|
180
|
+
const completeStructuredSubagentResult = Function(
|
|
181
|
+
'validateStructuredSubagentOutput',
|
|
182
|
+
'buildStructuredResponseRepairPrompt',
|
|
183
|
+
'SUBAGENT_PROMPT_ORIGIN',
|
|
184
|
+
'completeChildTurnWithMaxTokensHandoff',
|
|
185
|
+
'lastAssistantText',
|
|
186
|
+
'STRUCTURED_SUBAGENT_OUTPUT_ERROR',
|
|
187
|
+
`"use strict"; ${functionSource}; return completeStructuredSubagentResult;`,
|
|
188
|
+
)(
|
|
189
|
+
runtime.validateStructuredSubagentOutput,
|
|
190
|
+
runtime.buildStructuredResponseRepairPrompt,
|
|
191
|
+
{ kind: 'system_trigger', name: 'subagent' },
|
|
192
|
+
async () => { completionCalls += 1; },
|
|
193
|
+
() => currentText,
|
|
194
|
+
runtime.STRUCTURED_SUBAGENT_OUTPUT_ERROR,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const prepared = runtime.prepareStructuredResponseFormat({
|
|
198
|
+
name: 'answer',
|
|
199
|
+
schema: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: { value: { type: 'string' } },
|
|
202
|
+
required: ['value'],
|
|
203
|
+
additionalProperties: false,
|
|
204
|
+
},
|
|
205
|
+
}, () => {
|
|
206
|
+
const validator = (value) => {
|
|
207
|
+
const ok = value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
208
|
+
&& typeof value.value === 'string' && Object.keys(value).length === 1;
|
|
209
|
+
validator.errors = ok ? [] : [{ keyword: 'required', instancePath: '', params: { missingProperty: 'value' } }];
|
|
210
|
+
return ok;
|
|
211
|
+
};
|
|
212
|
+
return validator;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const prompts = [];
|
|
216
|
+
const child = { turn: { prompt: (parts) => {
|
|
217
|
+
prompts.push(parts[0].text);
|
|
218
|
+
currentText = '{"value":"repaired"}';
|
|
219
|
+
return 'turn-2';
|
|
220
|
+
} } };
|
|
221
|
+
currentText = '{"wrong":true}';
|
|
222
|
+
const repaired = await completeStructuredSubagentResult(
|
|
223
|
+
child,
|
|
224
|
+
'agent-2',
|
|
225
|
+
'coder',
|
|
226
|
+
prepared,
|
|
227
|
+
new AbortController().signal,
|
|
228
|
+
);
|
|
229
|
+
assert.deepEqual(repaired, { value: 'repaired' });
|
|
230
|
+
assert.equal(prompts.length, 1);
|
|
231
|
+
assert.equal(completionCalls, 1);
|
|
232
|
+
|
|
233
|
+
currentText = '{"wrong":true}';
|
|
234
|
+
child.turn.prompt = (parts) => {
|
|
235
|
+
prompts.push(parts[0].text);
|
|
236
|
+
currentText = '{"still_wrong":true}';
|
|
237
|
+
return 'turn-3';
|
|
238
|
+
};
|
|
239
|
+
await assert.rejects(
|
|
240
|
+
completeStructuredSubagentResult(child, 'agent-3', 'coder', prepared, new AbortController().signal),
|
|
241
|
+
/Structured subagent output invalid after one repair attempt[\s\S]*Resume with Agent/u,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
controller.abort(new Error('cancelled'));
|
|
246
|
+
currentText = '{"wrong":true}';
|
|
247
|
+
const promptCountBeforeCancel = prompts.length;
|
|
248
|
+
await assert.rejects(
|
|
249
|
+
completeStructuredSubagentResult(child, 'agent-4', 'coder', prepared, controller.signal),
|
|
250
|
+
/cancelled/u,
|
|
251
|
+
);
|
|
252
|
+
assert.equal(prompts.length, promptCountBeforeCancel, 'cancellation must prevent the repair turn');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function exerciseBundledStructuredFailure(source) {
|
|
256
|
+
const functionSource = extractBetween(
|
|
257
|
+
source,
|
|
258
|
+
'function formatForegroundAgentFailure(',
|
|
259
|
+
'function launchErrorMessage(',
|
|
260
|
+
);
|
|
261
|
+
const runtime = require('../bin/structured-subagent-output.cjs');
|
|
262
|
+
const formatForegroundAgentFailure = Function(
|
|
263
|
+
'SUBAGENT_MAX_TOKENS_ERROR',
|
|
264
|
+
'STRUCTURED_SUBAGENT_OUTPUT_ERROR',
|
|
265
|
+
`"use strict"; ${functionSource}; return formatForegroundAgentFailure;`,
|
|
266
|
+
)('SUBAGENT_MAX_TOKENS', runtime.STRUCTURED_SUBAGENT_OUTPUT_ERROR);
|
|
267
|
+
const output = formatForegroundAgentFailure(
|
|
268
|
+
{ agentId: 'agent-failed', profileName: 'coder' },
|
|
269
|
+
`${runtime.STRUCTURED_SUBAGENT_OUTPUT_ERROR}: missing value`,
|
|
270
|
+
false,
|
|
271
|
+
);
|
|
272
|
+
assert.match(output, /agent_id: agent-failed/u);
|
|
273
|
+
assert.match(output, /actual_subagent_type: coder/u);
|
|
274
|
+
assert.match(output, /missing value/u);
|
|
275
|
+
assert.match(output, /resume_hint: Continue with Agent\(resume="agent-failed"/u);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function main() {
|
|
279
|
+
const suite = spawnSync(process.execPath, ['--test', join(__dirname, 'structured-subagent-output.test.cjs')], {
|
|
280
|
+
stdio: 'inherit',
|
|
281
|
+
});
|
|
282
|
+
assert.equal(suite.status, 0, 'structured subagent helper tests must pass');
|
|
283
|
+
const source = readFileSync(bundlePath, 'utf8');
|
|
284
|
+
assertBundleContract(source);
|
|
285
|
+
await exerciseBundledAgentTool(source);
|
|
286
|
+
await exerciseBundledRepairFlow(source);
|
|
287
|
+
exerciseBundledStructuredFailure(source);
|
|
288
|
+
|
|
289
|
+
assert.throws(
|
|
290
|
+
() => assertBundleContract(source.replace(
|
|
291
|
+
'prepareStructuredResponseFormat(args.response_format, compileToolArgsValidator)',
|
|
292
|
+
'prepareStructuredResponseFormat(args.response_format, () => () => true)',
|
|
293
|
+
)),
|
|
294
|
+
/reuse BLUN AJV/u,
|
|
295
|
+
);
|
|
296
|
+
assert.throws(
|
|
297
|
+
() => assertBundleContract(source.replace(
|
|
298
|
+
'const runInBackground = responseFormat === void 0 ? resolvedRunMode.runInBackground : false',
|
|
299
|
+
'const runInBackground = resolvedRunMode.runInBackground',
|
|
300
|
+
)),
|
|
301
|
+
/foreground run/u,
|
|
302
|
+
);
|
|
303
|
+
assert.throws(
|
|
304
|
+
() => {
|
|
305
|
+
const region = extractBetween(
|
|
306
|
+
source,
|
|
307
|
+
'async function completeStructuredSubagentResult(',
|
|
308
|
+
'function shouldSuppressQueuedAttemptFailureEvent(',
|
|
309
|
+
);
|
|
310
|
+
const mutatedRegion = region.replace(
|
|
311
|
+
/\t\tsignal\.throwIfAborted\(\);\r?\n(?=\t\tif \(child\.turn\.prompt)/u,
|
|
312
|
+
'',
|
|
313
|
+
);
|
|
314
|
+
assertBundleContract(source.replace(region, mutatedRegion));
|
|
315
|
+
},
|
|
316
|
+
/cancellation/u,
|
|
317
|
+
);
|
|
318
|
+
assert.throws(
|
|
319
|
+
() => assertBundleContract(source.replace(
|
|
320
|
+
'responseFormat === void 0 ? formatForegroundAgentSuccess(handle, output) : output',
|
|
321
|
+
'formatForegroundAgentSuccess(handle, output)',
|
|
322
|
+
)),
|
|
323
|
+
/free-form text/u,
|
|
324
|
+
);
|
|
325
|
+
process.stdout.write('structured subagent output regression: ok\n');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
main().catch((error) => {
|
|
329
|
+
console.error(error.stack || error);
|
|
330
|
+
process.exitCode = 1;
|
|
331
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
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 packageRoot = path.resolve(process.env.BLUN_PACKAGE_ROOT || path.join(__dirname, '..'));
|
|
8
|
+
const {
|
|
9
|
+
createDirectReplyTurnStop,
|
|
10
|
+
directMessageAllowsWorkContinuation,
|
|
11
|
+
directMessageStartsOrResumesWork,
|
|
12
|
+
directTelegramPayloadText,
|
|
13
|
+
} = require(path.join(packageRoot, 'bin', 'telegram-direct-focus-policy.cjs'));
|
|
14
|
+
|
|
15
|
+
const bundle = fs.readFileSync(path.join(packageRoot, 'blun.mjs'), 'utf8');
|
|
16
|
+
const direct = (payload) => [
|
|
17
|
+
'A private Telegram DM has priority over background, group, and loop work.',
|
|
18
|
+
'<channel priority="direct" source="telegram" chat_id="1605241602">',
|
|
19
|
+
payload,
|
|
20
|
+
'</channel>',
|
|
21
|
+
].join('\n');
|
|
22
|
+
|
|
23
|
+
const status = direct('Wie weit bist du?');
|
|
24
|
+
assert.equal(directTelegramPayloadText(status), 'Wie weit bist du?');
|
|
25
|
+
assert.equal(directMessageAllowsWorkContinuation(status), true);
|
|
26
|
+
assert.equal(directMessageStartsOrResumesWork(status), false);
|
|
27
|
+
const statusStop = createDirectReplyTurnStop(status);
|
|
28
|
+
assert.equal(statusStop.noteToolResult('mcp__plugin-telegram_telegram__reply', false), true);
|
|
29
|
+
assert.equal(statusStop.shouldStop(), true, 'standalone DM chat should stop after its reply');
|
|
30
|
+
|
|
31
|
+
for (const payload of [
|
|
32
|
+
'Auftrag: Lies die SPEC und baue die Datei.',
|
|
33
|
+
'Feuer frei',
|
|
34
|
+
'Bitte weiterarbeiten und danach melden.',
|
|
35
|
+
]) {
|
|
36
|
+
const input = direct(payload);
|
|
37
|
+
assert.equal(directMessageAllowsWorkContinuation(input), true, payload);
|
|
38
|
+
assert.equal(directMessageStartsOrResumesWork(input), true, payload);
|
|
39
|
+
const stop = createDirectReplyTurnStop(input);
|
|
40
|
+
stop.noteToolResult('mcp__plugin-telegram_telegram__reply', false);
|
|
41
|
+
assert.equal(stop.shouldStop(), false, `work DM must continue after reply: ${payload}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const pause = direct('Bitte stop, warte auf mein Zeichen.');
|
|
45
|
+
assert.equal(directMessageAllowsWorkContinuation(pause), false);
|
|
46
|
+
assert.equal(directMessageStartsOrResumesWork(pause), false);
|
|
47
|
+
|
|
48
|
+
assert.match(bundle, /let directSteerContinuationPending = false;/u);
|
|
49
|
+
assert.match(bundle, /pendingSteers\.some\(\(steer\) => directMessageAllowsWorkContinuation/u);
|
|
50
|
+
assert.match(bundle, /name: "telegram_direct_work_resume"/u);
|
|
51
|
+
assert.match(bundle, /perform the next unfinished action now\./u);
|
|
52
|
+
|
|
53
|
+
console.log('telegram-direct-work-resume-regression PASS');
|