@jsonstudio/llms 0.6.2125 → 0.6.2172
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/dist/conversion/compat/actions/deepseek-web-response.js +27 -3
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +9 -3
- package/dist/conversion/hub/process/chat-process.js +15 -18
- package/dist/conversion/responses/responses-openai-bridge.js +13 -12
- package/dist/conversion/shared/bridge-message-utils.js +92 -39
- package/dist/router/virtual-router/classifier.js +29 -5
- package/dist/router/virtual-router/engine/routing-pools/index.js +111 -5
- package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -0
- package/dist/router/virtual-router/engine.d.ts +2 -0
- package/dist/router/virtual-router/engine.js +57 -14
- package/dist/router/virtual-router/features.js +12 -4
- package/dist/router/virtual-router/message-utils.d.ts +8 -0
- package/dist/router/virtual-router/message-utils.js +170 -45
- package/dist/router/virtual-router/token-counter.js +51 -10
- package/dist/router/virtual-router/types.d.ts +3 -0
- package/dist/servertool/clock/session-scope.d.ts +3 -0
- package/dist/servertool/clock/session-scope.js +52 -0
- package/dist/servertool/engine.js +68 -8
- package/dist/servertool/handlers/clock-auto.js +2 -8
- package/dist/servertool/handlers/clock.js +3 -9
- package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
- package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
- package/dist/servertool/handlers/stop-message-auto.d.ts +1 -7
- package/dist/servertool/handlers/stop-message-auto.js +69 -971
- package/dist/servertool/handlers/web-search.js +117 -0
- package/package.json +1 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import * as childProcess from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
const STOP_MESSAGE_BD_CREATE_TIMEOUT_MS = 2_000;
|
|
5
|
+
const STOP_MESSAGE_BLOCKED_TEXT_SCAN_LIMIT = 12;
|
|
6
|
+
const STOP_MESSAGE_BLOCKED_CANDIDATE_MAX_LENGTH = 12_000;
|
|
7
|
+
export function extractBlockedReportFromMessages(messages) {
|
|
8
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const start = Math.max(0, messages.length - STOP_MESSAGE_BLOCKED_TEXT_SCAN_LIMIT);
|
|
12
|
+
for (let idx = messages.length - 1; idx >= start; idx -= 1) {
|
|
13
|
+
const text = extractCapturedMessageText(messages[idx]);
|
|
14
|
+
if (!text) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const blockedReport = extractBlockedReportFromText(text);
|
|
18
|
+
if (blockedReport) {
|
|
19
|
+
return blockedReport;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export function extractBlockedReportFromMessagesForTests(messages) {
|
|
25
|
+
return extractBlockedReportFromMessages(messages);
|
|
26
|
+
}
|
|
27
|
+
export function createBdIssueFromBlockedReport(blockedReport, context, cwdOverride) {
|
|
28
|
+
const cwd = resolveBdWorkingDirectoryForStopMessage(cwdOverride);
|
|
29
|
+
const title = buildBlockedIssueTitle(blockedReport);
|
|
30
|
+
const description = buildBlockedIssueDescription(blockedReport, context);
|
|
31
|
+
const acceptance = buildBlockedIssueAcceptance(blockedReport);
|
|
32
|
+
try {
|
|
33
|
+
const result = childProcess.spawnSync('bd', [
|
|
34
|
+
'--no-db',
|
|
35
|
+
'create',
|
|
36
|
+
'--json',
|
|
37
|
+
'-t',
|
|
38
|
+
'bug',
|
|
39
|
+
'-p',
|
|
40
|
+
'0',
|
|
41
|
+
'--title',
|
|
42
|
+
title,
|
|
43
|
+
'--description',
|
|
44
|
+
description,
|
|
45
|
+
'--acceptance',
|
|
46
|
+
acceptance
|
|
47
|
+
], {
|
|
48
|
+
cwd,
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
timeout: STOP_MESSAGE_BD_CREATE_TIMEOUT_MS,
|
|
51
|
+
maxBuffer: 1024 * 1024
|
|
52
|
+
});
|
|
53
|
+
if (result.error || result.status !== 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return parseCreatedIssueId(result.stdout);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function extractCapturedMessageText(message) {
|
|
63
|
+
if (!message) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
if (typeof message === 'string') {
|
|
67
|
+
return message.trim();
|
|
68
|
+
}
|
|
69
|
+
if (typeof message !== 'object' || Array.isArray(message)) {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
const record = message;
|
|
73
|
+
const contentText = extractTextFromMessageContent(record.content);
|
|
74
|
+
if (contentText) {
|
|
75
|
+
return contentText;
|
|
76
|
+
}
|
|
77
|
+
const inputText = extractTextFromMessageContent(record.input);
|
|
78
|
+
if (inputText) {
|
|
79
|
+
return inputText;
|
|
80
|
+
}
|
|
81
|
+
const outputText = extractTextFromMessageContent(record.output);
|
|
82
|
+
if (outputText) {
|
|
83
|
+
return outputText;
|
|
84
|
+
}
|
|
85
|
+
const argumentsText = toNonEmptyText(record.arguments);
|
|
86
|
+
return argumentsText;
|
|
87
|
+
}
|
|
88
|
+
export function extractTextFromMessageContent(content) {
|
|
89
|
+
if (typeof content === 'string') {
|
|
90
|
+
return content.trim();
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(content)) {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
const chunks = [];
|
|
96
|
+
for (const item of content) {
|
|
97
|
+
if (typeof item === 'string') {
|
|
98
|
+
if (item.trim())
|
|
99
|
+
chunks.push(item.trim());
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const record = item;
|
|
106
|
+
const type = toNonEmptyText(record.type).toLowerCase();
|
|
107
|
+
if (type === 'text' || type === 'output_text' || type === 'input_text' || !type) {
|
|
108
|
+
const text = toNonEmptyText(record.text);
|
|
109
|
+
if (text) {
|
|
110
|
+
chunks.push(text);
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const fallbackText = toNonEmptyText(record.content) || toNonEmptyText(record.value);
|
|
115
|
+
if (fallbackText) {
|
|
116
|
+
chunks.push(fallbackText);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return chunks.join('\n').trim();
|
|
120
|
+
}
|
|
121
|
+
function extractBlockedReportFromText(text) {
|
|
122
|
+
const trimmed = typeof text === 'string' ? text.trim() : '';
|
|
123
|
+
if (!trimmed) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const candidates = [];
|
|
127
|
+
const pushCandidate = (candidate) => {
|
|
128
|
+
const normalized = candidate.trim();
|
|
129
|
+
if (!normalized || normalized.length > STOP_MESSAGE_BLOCKED_CANDIDATE_MAX_LENGTH) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (candidates.includes(normalized)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
candidates.push(normalized);
|
|
136
|
+
};
|
|
137
|
+
pushCandidate(trimmed);
|
|
138
|
+
for (const codeBlock of extractJsonCodeBlocks(trimmed)) {
|
|
139
|
+
pushCandidate(codeBlock);
|
|
140
|
+
}
|
|
141
|
+
for (const objectText of extractBalancedJsonObjectStrings(trimmed)) {
|
|
142
|
+
if (objectText.includes('"type"') && objectText.toLowerCase().includes('"blocked"')) {
|
|
143
|
+
pushCandidate(objectText);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(candidate);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const report = normalizeBlockedReport(parsed);
|
|
155
|
+
if (report) {
|
|
156
|
+
return report;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
function normalizeBlockedReport(value) {
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
for (const entry of value) {
|
|
164
|
+
const report = normalizeBlockedReport(entry);
|
|
165
|
+
if (report) {
|
|
166
|
+
return report;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
if (!value || typeof value !== 'object') {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const record = value;
|
|
175
|
+
const type = toNonEmptyText(record.type).toLowerCase();
|
|
176
|
+
if (type !== 'blocked') {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const summary = toNonEmptyText(record.summary) ||
|
|
180
|
+
toNonEmptyText(record.title) ||
|
|
181
|
+
toNonEmptyText(record.problem);
|
|
182
|
+
const blocker = toNonEmptyText(record.blocker) ||
|
|
183
|
+
toNonEmptyText(record.reason) ||
|
|
184
|
+
toNonEmptyText(record.blocked_by);
|
|
185
|
+
if (!summary || !blocker) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const impact = toNonEmptyText(record.impact) || toNonEmptyText(record.effect);
|
|
189
|
+
const nextAction = toNonEmptyText(record.next_action) ||
|
|
190
|
+
toNonEmptyText(record.nextAction) ||
|
|
191
|
+
toNonEmptyText(record.next_step);
|
|
192
|
+
const evidence = normalizeBlockedEvidence(record.evidence);
|
|
193
|
+
return {
|
|
194
|
+
summary: summary.slice(0, 1_000),
|
|
195
|
+
blocker: blocker.slice(0, 1_000),
|
|
196
|
+
...(impact ? { impact: impact.slice(0, 1_000) } : {}),
|
|
197
|
+
...(nextAction ? { nextAction: nextAction.slice(0, 1_000) } : {}),
|
|
198
|
+
evidence
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function normalizeBlockedEvidence(raw) {
|
|
202
|
+
if (Array.isArray(raw)) {
|
|
203
|
+
const normalized = raw
|
|
204
|
+
.map((entry) => toNonEmptyText(entry))
|
|
205
|
+
.filter((entry) => entry.length > 0)
|
|
206
|
+
.map((entry) => entry.slice(0, 800));
|
|
207
|
+
return normalized.slice(0, 8);
|
|
208
|
+
}
|
|
209
|
+
const single = toNonEmptyText(raw);
|
|
210
|
+
return single ? [single.slice(0, 800)] : [];
|
|
211
|
+
}
|
|
212
|
+
function extractJsonCodeBlocks(text) {
|
|
213
|
+
const candidates = [];
|
|
214
|
+
const regex = /```(?:json)?\s*([\s\S]*?)```/gi;
|
|
215
|
+
let match;
|
|
216
|
+
while ((match = regex.exec(text)) !== null) {
|
|
217
|
+
const body = (match[1] || '').trim();
|
|
218
|
+
if (!body) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
candidates.push(body);
|
|
222
|
+
}
|
|
223
|
+
return candidates;
|
|
224
|
+
}
|
|
225
|
+
function extractBalancedJsonObjectStrings(text) {
|
|
226
|
+
const results = [];
|
|
227
|
+
let start = -1;
|
|
228
|
+
let depth = 0;
|
|
229
|
+
let inString = false;
|
|
230
|
+
let escaped = false;
|
|
231
|
+
for (let idx = 0; idx < text.length; idx += 1) {
|
|
232
|
+
const ch = text[idx];
|
|
233
|
+
if (inString) {
|
|
234
|
+
if (escaped) {
|
|
235
|
+
escaped = false;
|
|
236
|
+
}
|
|
237
|
+
else if (ch === '\\') {
|
|
238
|
+
escaped = true;
|
|
239
|
+
}
|
|
240
|
+
else if (ch === '"') {
|
|
241
|
+
inString = false;
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (ch === '"') {
|
|
246
|
+
inString = true;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (ch === '{') {
|
|
250
|
+
if (depth === 0) {
|
|
251
|
+
start = idx;
|
|
252
|
+
}
|
|
253
|
+
depth += 1;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (ch === '}') {
|
|
257
|
+
if (depth <= 0) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
depth -= 1;
|
|
261
|
+
if (depth === 0 && start >= 0) {
|
|
262
|
+
results.push(text.slice(start, idx + 1));
|
|
263
|
+
start = -1;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return results;
|
|
268
|
+
}
|
|
269
|
+
function resolveBdWorkingDirectoryForStopMessage(cwdOverride) {
|
|
270
|
+
const fromOverride = toNonEmptyText(cwdOverride);
|
|
271
|
+
if (fromOverride) {
|
|
272
|
+
return path.resolve(fromOverride);
|
|
273
|
+
}
|
|
274
|
+
const fromEnv = toNonEmptyText(process.env.ROUTECODEX_STOPMESSAGE_BD_WORKDIR);
|
|
275
|
+
if (fromEnv) {
|
|
276
|
+
return path.resolve(fromEnv);
|
|
277
|
+
}
|
|
278
|
+
const cwd = process.cwd();
|
|
279
|
+
return findBdProjectRootForStopMessage(cwd) || cwd;
|
|
280
|
+
}
|
|
281
|
+
function findBdProjectRootForStopMessage(startDirectory) {
|
|
282
|
+
let current = path.resolve(startDirectory);
|
|
283
|
+
while (true) {
|
|
284
|
+
const beadFile = path.join(current, '.beads', 'issues.jsonl');
|
|
285
|
+
if (fs.existsSync(beadFile)) {
|
|
286
|
+
return current;
|
|
287
|
+
}
|
|
288
|
+
const parent = path.dirname(current);
|
|
289
|
+
if (parent === current) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
current = parent;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function buildBlockedIssueTitle(report) {
|
|
296
|
+
const base = report.summary.trim() || report.blocker.trim() || 'stopMessage blocked';
|
|
297
|
+
const cleaned = base.replace(/\s+/g, ' ').slice(0, 120);
|
|
298
|
+
return `[stopMessage] ${cleaned}`.trim();
|
|
299
|
+
}
|
|
300
|
+
function buildBlockedIssueDescription(report, context) {
|
|
301
|
+
const lines = [
|
|
302
|
+
'自动建单来源:stop_message_auto 结构化 blocked 报告',
|
|
303
|
+
'',
|
|
304
|
+
`Summary: ${report.summary}`,
|
|
305
|
+
`Blocker: ${report.blocker}`,
|
|
306
|
+
`Impact: ${report.impact || 'n/a'}`,
|
|
307
|
+
`Next Action: ${report.nextAction || 'n/a'}`,
|
|
308
|
+
'',
|
|
309
|
+
`RequestId: ${context?.requestId || 'n/a'}`,
|
|
310
|
+
`SessionId: ${context?.sessionId || 'n/a'}`,
|
|
311
|
+
'',
|
|
312
|
+
'Evidence:',
|
|
313
|
+
...(report.evidence.length > 0 ? report.evidence.map((entry) => `- ${entry}`) : ['- n/a']),
|
|
314
|
+
'',
|
|
315
|
+
'Notes:',
|
|
316
|
+
'- 本 issue 由系统在 stopMessage 检测到结构化阻塞后自动创建。',
|
|
317
|
+
'- 请按 blocker/next action 先解除阻塞,再恢复执行。'
|
|
318
|
+
];
|
|
319
|
+
return lines.join('\n');
|
|
320
|
+
}
|
|
321
|
+
function buildBlockedIssueAcceptance(report) {
|
|
322
|
+
const next = report.nextAction || '执行可验证的解阻动作并记录结果';
|
|
323
|
+
return [
|
|
324
|
+
`1. 明确并确认 blocker:${report.blocker}`,
|
|
325
|
+
`2. 完成解阻动作:${next}`,
|
|
326
|
+
'3. 验证 stopMessage followup 可继续推进'
|
|
327
|
+
].join('\n');
|
|
328
|
+
}
|
|
329
|
+
function parseCreatedIssueId(stdout) {
|
|
330
|
+
if (typeof stdout !== 'string') {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const trimmed = stdout.trim();
|
|
334
|
+
if (!trimmed) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
const parsed = JSON.parse(trimmed);
|
|
339
|
+
const id = toNonEmptyText(parsed.id);
|
|
340
|
+
return id || null;
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
const match = trimmed.match(/\b[a-z]+-\d+(?:\.\d+)?\b/i);
|
|
344
|
+
return match ? match[0] : null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function toNonEmptyText(value) {
|
|
348
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
349
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface StopMessageAutoResponseSnapshot {
|
|
2
|
+
providerProtocol?: string;
|
|
3
|
+
finishReason?: string;
|
|
4
|
+
assistantText?: string;
|
|
5
|
+
reasoningText?: string;
|
|
6
|
+
responseExcerpt?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function renderStopMessageAutoFollowupViaIflow(args: {
|
|
9
|
+
baseStopMessageText: string;
|
|
10
|
+
candidateFollowupText: string;
|
|
11
|
+
responseSnapshot: StopMessageAutoResponseSnapshot;
|
|
12
|
+
requestId?: string;
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
providerKey?: string;
|
|
15
|
+
model?: string;
|
|
16
|
+
usedRepeats: number;
|
|
17
|
+
maxRepeats: number;
|
|
18
|
+
}): string | null;
|
|
19
|
+
export declare function extractStopMessageAutoResponseSnapshot(base: unknown, adapterContext: unknown): StopMessageAutoResponseSnapshot;
|
|
20
|
+
export declare function extractResponsesOutputText(base: {
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}): string;
|
|
23
|
+
export declare function hasToolLikeOutput(value: unknown): boolean;
|