@jsonstudio/llms 0.6.1739 → 0.6.1890

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 (107) hide show
  1. package/dist/conversion/compat/actions/deepseek-web-request.d.ts +3 -0
  2. package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
  3. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
  4. package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
  5. package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
  6. package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
  10. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
  11. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
  12. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
  14. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
  15. package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
  16. package/dist/conversion/hub/policy/policy-engine.js +8 -0
  17. package/dist/conversion/hub/process/chat-process.js +466 -16
  18. package/dist/conversion/hub/response/provider-response.js +0 -35
  19. package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
  20. package/dist/conversion/responses/responses-openai-bridge.js +166 -8
  21. package/dist/conversion/shared/anthropic-message-utils.js +10 -1
  22. package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
  23. package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
  24. package/dist/conversion/shared/tool-governor.js +102 -0
  25. package/dist/guidance/index.js +17 -0
  26. package/dist/router/virtual-router/bootstrap.js +46 -1
  27. package/dist/router/virtual-router/classifier.js +59 -4
  28. package/dist/router/virtual-router/engine/health/index.js +6 -6
  29. package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
  30. package/dist/router/virtual-router/engine-logging.js +62 -24
  31. package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
  32. package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
  33. package/dist/router/virtual-router/engine.d.ts +3 -1
  34. package/dist/router/virtual-router/engine.js +359 -39
  35. package/dist/router/virtual-router/features.js +2 -1
  36. package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
  37. package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
  38. package/dist/router/virtual-router/provider-registry.js +3 -1
  39. package/dist/router/virtual-router/routing-instructions.d.ts +15 -1
  40. package/dist/router/virtual-router/routing-instructions.js +110 -151
  41. package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
  42. package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
  43. package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
  44. package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
  45. package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
  46. package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
  47. package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
  48. package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
  49. package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
  50. package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
  51. package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
  52. package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
  53. package/dist/router/virtual-router/sticky-session-store.js +206 -57
  54. package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
  55. package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
  56. package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
  57. package/dist/router/virtual-router/stop-message-state-sync.js +5 -0
  58. package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
  59. package/dist/router/virtual-router/token-file-scanner.js +64 -3
  60. package/dist/router/virtual-router/tool-signals.d.ts +5 -0
  61. package/dist/router/virtual-router/tool-signals.js +42 -3
  62. package/dist/router/virtual-router/types.d.ts +19 -1
  63. package/dist/router/virtual-router/types.js +1 -0
  64. package/dist/servertool/clock/config.d.ts +1 -1
  65. package/dist/servertool/clock/config.js +27 -4
  66. package/dist/servertool/clock/state.js +41 -2
  67. package/dist/servertool/clock/task-store.d.ts +2 -2
  68. package/dist/servertool/clock/task-store.js +1 -1
  69. package/dist/servertool/clock/tasks.d.ts +3 -1
  70. package/dist/servertool/clock/tasks.js +209 -18
  71. package/dist/servertool/clock/types.d.ts +17 -0
  72. package/dist/servertool/continue-execution/log.d.ts +3 -0
  73. package/dist/servertool/continue-execution/log.js +13 -0
  74. package/dist/servertool/engine.js +414 -68
  75. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
  76. package/dist/servertool/handlers/clock-auto.js +54 -71
  77. package/dist/servertool/handlers/clock.js +121 -6
  78. package/dist/servertool/handlers/continue-execution.d.ts +1 -0
  79. package/dist/servertool/handlers/continue-execution.js +91 -0
  80. package/dist/servertool/handlers/followup-request-builder.js +13 -0
  81. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
  82. package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
  83. package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
  84. package/dist/servertool/handlers/stop-message-auto.js +386 -257
  85. package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
  86. package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
  87. package/dist/servertool/handlers/vision.js +1 -1
  88. package/dist/servertool/log/progress-file.d.ts +14 -0
  89. package/dist/servertool/log/progress-file.js +88 -0
  90. package/dist/servertool/pre-command-hooks.d.ts +17 -0
  91. package/dist/servertool/pre-command-hooks.js +491 -0
  92. package/dist/servertool/registry.d.ts +23 -6
  93. package/dist/servertool/registry.js +66 -1
  94. package/dist/servertool/server-side-tools.d.ts +1 -0
  95. package/dist/servertool/server-side-tools.js +216 -14
  96. package/dist/servertool/stop-gateway-context.d.ts +14 -0
  97. package/dist/servertool/stop-gateway-context.js +167 -0
  98. package/dist/servertool/stop-message-compare-context.d.ts +24 -0
  99. package/dist/servertool/stop-message-compare-context.js +133 -0
  100. package/dist/servertool/types.d.ts +12 -0
  101. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
  102. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
  103. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
  104. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
  105. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
  106. package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
  107. package/package.json +1 -1
@@ -0,0 +1,684 @@
1
+ import { createHash } from 'node:crypto';
2
+ import * as childProcess from 'node:child_process';
3
+ import * as fs from 'node:fs';
4
+ import * as path from 'node:path';
5
+ import { loadStopMessageStageTemplate } from '../../router/virtual-router/stop-message-stage-template-files.js';
6
+ const DEFAULT_STATUS_TEMPLATE = [
7
+ '先做状态确认再继续执行:',
8
+ '1) 运行 `bd --no-db ready` 与 `bd --no-db list --status in_progress --json --limit 5`,确认是否有可继续任务;',
9
+ '2) 若存在 blocked,先分析阻塞原因并制定解决方案;若仅有 in_progress/open,则直接推进可验证动作;',
10
+ '3) 若 ready 为空,再检查 `bd --no-db list --status blocked --json --limit 5` 与 `bd --no-db epic status`,判断是否仍有未完成工作;',
11
+ '4) 若仍有未完成工作,优先解 blocked;必要时用 bd update/dep add/create 同步计划后继续执行;',
12
+ '5) 仅当确认确实无任务可推进时,基于当前上下文判断是否需要 `bd --no-db create` 新任务;不需要才停止。',
13
+ '',
14
+ '{{BASE_STOP_MESSAGE}}'
15
+ ].join('\n');
16
+ const DEFAULT_ACTIVE_TEMPLATE = [
17
+ '检测到当前仍有任务,按优先级继续执行,不允许只汇报状态:',
18
+ '1) 先检查是否存在 blocked 任务;若存在,先分析阻塞原因并执行解阻塞动作(补依赖/修配置/拆子任务/补证据);',
19
+ '2) 需要调整执行顺序或新增动作时,必须用 bd 更新计划与状态(update/dep add/create),保持可追踪;',
20
+ '3) 若无 blocked,再继续当前 in_progress/open 任务并执行至少一个可验证动作(改代码/跑测试/更新证据);',
21
+ '4) 未完成上述动作前,不要输出“已完成/已汇报”类结论;完成后只汇报结果与下一步。',
22
+ '',
23
+ '{{BASE_STOP_MESSAGE}}'
24
+ ].join('\n');
25
+ const DEFAULT_LOOP_TEMPLATE = [
26
+ '检测到可能出现重复循环,请先自检并尝试跳出:',
27
+ '1) 对比最近两轮是否重复同一命令/同一结论;',
28
+ '2) 若重复,改用新的有效动作(更具体命令、换验证路径或直接给结论);',
29
+ '3) 执行后再观察状态变化;若仍无变化则停止并报告阻塞点。',
30
+ '',
31
+ '{{BASE_STOP_MESSAGE}}'
32
+ ].join('\n');
33
+ let bdRuntimeCache = null;
34
+ const DEFAULT_BD_TIMEOUT_MS = 1200;
35
+ const DEFAULT_BD_CACHE_TTL_MS = 1500;
36
+ export function resolveStopMessageStageDecision(args) {
37
+ const mode = resolveStageMode(args.state.stopMessageStageMode);
38
+ const observationHash = buildObservationHash(args.capturedMessages);
39
+ const toolSignatureHash = buildToolSignatureHash(args.capturedMessages);
40
+ if (mode === 'off') {
41
+ return {
42
+ action: 'stop',
43
+ observationHash,
44
+ observationStableCount: 0,
45
+ bdWorkState: 'unknown',
46
+ stopReason: 'mode_off',
47
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
48
+ };
49
+ }
50
+ const templates = resolveStageTemplates(mode);
51
+ if (!templates.enabled) {
52
+ return {
53
+ action: 'followup',
54
+ followupText: args.baseText,
55
+ observationHash,
56
+ observationStableCount: 0,
57
+ bdWorkState: 'unknown',
58
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
59
+ };
60
+ }
61
+ const previousHash = normalizeText(args.state.stopMessageObservationHash);
62
+ const previousStableCount = toSafeInt(args.state.stopMessageObservationStableCount, 0);
63
+ const stableCount = previousHash && previousHash === observationHash ? previousStableCount + 1 : 0;
64
+ const bdWorkState = detectBdWorkState(args.capturedMessages, {
65
+ bdCommandRunner: args.bdCommandRunner,
66
+ nowMs: args.nowMs
67
+ });
68
+ const previousStage = normalizeStageName(args.state.stopMessageStage);
69
+ const previousBdWorkState = normalizeBdWorkState(args.state.stopMessageBdWorkState);
70
+ const idleConfirmed = bdWorkState === 'idle' &&
71
+ previousBdWorkState === 'idle' &&
72
+ previousStage === 'status_probe';
73
+ if (bdWorkState === 'idle') {
74
+ if (mode !== 'on' || idleConfirmed) {
75
+ return {
76
+ action: 'stop',
77
+ observationHash,
78
+ observationStableCount: stableCount,
79
+ bdWorkState,
80
+ stopReason: 'bd_idle',
81
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
82
+ };
83
+ }
84
+ }
85
+ if (stableCount >= 2) {
86
+ return {
87
+ action: 'stop',
88
+ observationHash,
89
+ observationStableCount: stableCount,
90
+ bdWorkState,
91
+ stopReason: 'loop_stable',
92
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
93
+ };
94
+ }
95
+ const stage = bdWorkState === 'active'
96
+ ? 'active_continue'
97
+ : stableCount <= 0
98
+ ? 'status_probe'
99
+ : 'loop_self_check';
100
+ const stageTemplate = stage === 'status_probe'
101
+ ? templates.statusProbeTemplate
102
+ : stage === 'active_continue'
103
+ ? templates.activeContinueTemplate
104
+ : templates.loopSelfCheckTemplate;
105
+ const followupText = buildStageMessage(stageTemplate, args.baseText);
106
+ return {
107
+ action: 'followup',
108
+ stage,
109
+ followupText,
110
+ observationHash,
111
+ observationStableCount: stableCount,
112
+ bdWorkState,
113
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
114
+ };
115
+ }
116
+ export function resolveBdWorkStateFromRuntime(options) {
117
+ const mode = resolveBdMode();
118
+ const nowMs = typeof options?.nowMs === 'number' && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
119
+ const cwd = resolveBdWorkingDirectory(options?.cwd);
120
+ const ttlMs = resolveBdCacheTtlMs();
121
+ if (ttlMs > 0 &&
122
+ bdRuntimeCache &&
123
+ bdRuntimeCache.mode === mode &&
124
+ bdRuntimeCache.cwd === cwd &&
125
+ bdRuntimeCache.expiresAt > nowMs) {
126
+ return bdRuntimeCache.state;
127
+ }
128
+ const commandRunner = options?.bdCommandRunner ?? runBdCommand;
129
+ const timeoutMs = resolveBdTimeoutMs();
130
+ const inProgress = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'in_progress', '--json', '--limit', '1'], {
131
+ cwd,
132
+ timeoutMs
133
+ });
134
+ if (inProgress === 'active') {
135
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
136
+ }
137
+ if (inProgress === 'unknown') {
138
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
139
+ }
140
+ const ready = runBdQuery(commandRunner, ['--no-db', 'ready', '--json', '--limit', '1'], {
141
+ cwd,
142
+ timeoutMs
143
+ });
144
+ if (ready === 'active') {
145
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
146
+ }
147
+ if (ready === 'unknown') {
148
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
149
+ }
150
+ const open = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'open', '--json', '--limit', '1'], {
151
+ cwd,
152
+ timeoutMs
153
+ });
154
+ if (open === 'active') {
155
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
156
+ }
157
+ if (open === 'unknown') {
158
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
159
+ }
160
+ const blocked = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'blocked', '--json', '--limit', '1'], {
161
+ cwd,
162
+ timeoutMs
163
+ });
164
+ if (blocked === 'active') {
165
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
166
+ }
167
+ if (blocked === 'unknown') {
168
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
169
+ }
170
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'idle');
171
+ }
172
+ export function resetStopMessageBdRuntimeCacheForTests() {
173
+ bdRuntimeCache = null;
174
+ }
175
+ function buildStageMessage(template, baseText) {
176
+ const safeBaseText = baseText.trim();
177
+ const safeTemplate = typeof template === 'string' ? template.trim() : '';
178
+ if (safeTemplate.includes('{{BASE_STOP_MESSAGE}}')) {
179
+ const replaced = safeTemplate.replaceAll('{{BASE_STOP_MESSAGE}}', safeBaseText).replace(/\n{3,}/g, '\n\n').trim();
180
+ if (replaced) {
181
+ return replaced;
182
+ }
183
+ }
184
+ if (!safeTemplate) {
185
+ return safeBaseText;
186
+ }
187
+ if (!safeBaseText) {
188
+ return safeTemplate;
189
+ }
190
+ return `${safeTemplate}\n\n原始约束:\n${safeBaseText}`.trim();
191
+ }
192
+ function resolveStageTemplates(mode) {
193
+ const statusProbeTemplate = loadStageTemplate('status_probe');
194
+ const activeContinueTemplate = loadStageTemplate('active_continue');
195
+ const loopSelfCheckTemplate = loadStageTemplate('loop_self_check');
196
+ const hasUserTemplate = Boolean(statusProbeTemplate || activeContinueTemplate || loopSelfCheckTemplate);
197
+ const enabled = mode === 'on' || hasUserTemplate;
198
+ if (!enabled) {
199
+ return {
200
+ enabled: false,
201
+ statusProbeTemplate: '',
202
+ activeContinueTemplate: '',
203
+ loopSelfCheckTemplate: ''
204
+ };
205
+ }
206
+ return {
207
+ enabled: true,
208
+ statusProbeTemplate: statusProbeTemplate || DEFAULT_STATUS_TEMPLATE,
209
+ activeContinueTemplate: activeContinueTemplate || DEFAULT_ACTIVE_TEMPLATE,
210
+ loopSelfCheckTemplate: loopSelfCheckTemplate || DEFAULT_LOOP_TEMPLATE
211
+ };
212
+ }
213
+ function resolveStageMode(modeFromState) {
214
+ const stateMode = normalizeStageMode(modeFromState);
215
+ if (stateMode) {
216
+ return stateMode;
217
+ }
218
+ const raw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_STAGE_MODE).toLowerCase();
219
+ if (raw === '1' || raw === 'true' || raw === 'on') {
220
+ return 'on';
221
+ }
222
+ if (raw === '0' || raw === 'false' || raw === 'off') {
223
+ return 'off';
224
+ }
225
+ if (raw === 'auto') {
226
+ return 'auto';
227
+ }
228
+ return 'auto';
229
+ }
230
+ function loadStageTemplate(stage) {
231
+ const loaded = loadStopMessageStageTemplate(stage);
232
+ return loaded.text || null;
233
+ }
234
+ function detectBdWorkState(messages, options) {
235
+ const mode = resolveBdMode();
236
+ if (mode !== 'heuristic') {
237
+ const runtimeState = resolveBdWorkStateFromRuntime({
238
+ bdCommandRunner: options?.bdCommandRunner,
239
+ nowMs: options?.nowMs
240
+ });
241
+ if (runtimeState === 'active' || runtimeState === 'idle') {
242
+ return runtimeState;
243
+ }
244
+ if (mode === 'runtime') {
245
+ return 'unknown';
246
+ }
247
+ }
248
+ return detectBdWorkStateFromHeuristics(messages);
249
+ }
250
+ function detectBdWorkStateFromHeuristics(messages) {
251
+ const normalizedTail = messages
252
+ .slice(-30)
253
+ .map((message) => normalizeMessageForHeuristics(message))
254
+ .filter(Boolean)
255
+ .join('\n')
256
+ .toLowerCase();
257
+ if (!normalizedTail.includes('bd')) {
258
+ return 'unknown';
259
+ }
260
+ const idlePatterns = [
261
+ /\bno\s+(ready|open|in_progress)\b/i,
262
+ /\b0\s+(ready|open|in_progress)\b/i,
263
+ /\bnothing\s+(to\s+do|ready|in\s+progress)\b/i,
264
+ /\ball\s+(tasks?|issues?)\s+(done|closed)\b/i,
265
+ /没有可(做|推进)任务/i,
266
+ /无\s*(ready|open|in_progress)\s*任务/i
267
+ ];
268
+ if (idlePatterns.some((pattern) => pattern.test(normalizedTail))) {
269
+ return 'idle';
270
+ }
271
+ const activePatterns = [
272
+ /\bin_progress\b/i,
273
+ /\bready\b/i,
274
+ /\bblocked\b/i,
275
+ /\bopen\b/i,
276
+ /\bepic status\b/i,
277
+ /\bshow\s+[a-z0-9._-]+\b/i
278
+ ];
279
+ if (activePatterns.some((pattern) => pattern.test(normalizedTail))) {
280
+ return 'active';
281
+ }
282
+ return 'unknown';
283
+ }
284
+ function writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, state) {
285
+ if (ttlMs > 0) {
286
+ bdRuntimeCache = {
287
+ mode,
288
+ cwd,
289
+ expiresAt: nowMs + ttlMs,
290
+ state
291
+ };
292
+ }
293
+ return state;
294
+ }
295
+ function resolveBdMode() {
296
+ const modeRaw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_MODE).toLowerCase();
297
+ if (modeRaw === 'runtime' || modeRaw === 'real') {
298
+ return 'runtime';
299
+ }
300
+ if (modeRaw === 'heuristic' || modeRaw === 'text') {
301
+ return 'heuristic';
302
+ }
303
+ const legacyRuntimeRaw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_RUNTIME).toLowerCase();
304
+ if (legacyRuntimeRaw === '1' || legacyRuntimeRaw === 'true' || legacyRuntimeRaw === 'on') {
305
+ return 'runtime';
306
+ }
307
+ if (legacyRuntimeRaw === '0' || legacyRuntimeRaw === 'false' || legacyRuntimeRaw === 'off') {
308
+ return 'heuristic';
309
+ }
310
+ return 'auto';
311
+ }
312
+ function resolveBdCacheTtlMs() {
313
+ const parsed = parseNonNegativeInteger(process.env.ROUTECODEX_STOPMESSAGE_BD_CACHE_TTL_MS);
314
+ if (typeof parsed === 'number') {
315
+ return parsed;
316
+ }
317
+ return DEFAULT_BD_CACHE_TTL_MS;
318
+ }
319
+ function resolveBdTimeoutMs() {
320
+ const parsed = parsePositiveInteger(process.env.ROUTECODEX_STOPMESSAGE_BD_TIMEOUT_MS);
321
+ if (typeof parsed === 'number') {
322
+ return parsed;
323
+ }
324
+ return DEFAULT_BD_TIMEOUT_MS;
325
+ }
326
+ function parseNonNegativeInteger(value) {
327
+ if (typeof value !== 'string') {
328
+ return undefined;
329
+ }
330
+ const parsed = Number.parseInt(value.trim(), 10);
331
+ if (!Number.isFinite(parsed) || parsed < 0) {
332
+ return undefined;
333
+ }
334
+ return parsed;
335
+ }
336
+ function parsePositiveInteger(value) {
337
+ const parsed = parseNonNegativeInteger(value);
338
+ if (typeof parsed !== 'number' || parsed <= 0) {
339
+ return undefined;
340
+ }
341
+ return parsed;
342
+ }
343
+ function resolveBdWorkingDirectory(cwdOverride) {
344
+ const fromOverride = normalizeText(cwdOverride);
345
+ if (fromOverride) {
346
+ return path.resolve(fromOverride);
347
+ }
348
+ const fromEnv = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_WORKDIR);
349
+ if (fromEnv) {
350
+ return path.resolve(fromEnv);
351
+ }
352
+ const current = process.cwd();
353
+ return findBdProjectRoot(current) || current;
354
+ }
355
+ function findBdProjectRoot(startDirectory) {
356
+ let current = path.resolve(startDirectory);
357
+ while (true) {
358
+ const beadsDir = path.join(current, '.beads');
359
+ const issuesJsonlPath = path.join(beadsDir, 'issues.jsonl');
360
+ if (fs.existsSync(issuesJsonlPath)) {
361
+ return current;
362
+ }
363
+ if (fs.existsSync(beadsDir)) {
364
+ return current;
365
+ }
366
+ const parent = path.dirname(current);
367
+ if (parent === current) {
368
+ return null;
369
+ }
370
+ current = parent;
371
+ }
372
+ }
373
+ function runBdCommand(args, options) {
374
+ try {
375
+ const result = childProcess.spawnSync('bd', args, {
376
+ cwd: options.cwd,
377
+ encoding: 'utf8',
378
+ timeout: options.timeoutMs,
379
+ maxBuffer: 1024 * 1024
380
+ });
381
+ return {
382
+ status: result.status,
383
+ stdout: typeof result.stdout === 'string' ? result.stdout : String(result.stdout ?? ''),
384
+ stderr: typeof result.stderr === 'string' ? result.stderr : String(result.stderr ?? ''),
385
+ error: result.error
386
+ };
387
+ }
388
+ catch (error) {
389
+ return {
390
+ status: null,
391
+ stdout: '',
392
+ stderr: '',
393
+ error
394
+ };
395
+ }
396
+ }
397
+ function runBdQuery(commandRunner, args, options) {
398
+ const result = commandRunner(args, options);
399
+ if (result.error || result.status !== 0) {
400
+ return 'unknown';
401
+ }
402
+ const list = parseJsonArrayOutput(result.stdout);
403
+ if (!list) {
404
+ return 'unknown';
405
+ }
406
+ return list.length > 0 ? 'active' : 'idle';
407
+ }
408
+ function parseJsonArrayOutput(stdout) {
409
+ const trimmed = typeof stdout === 'string' ? stdout.trim() : '';
410
+ if (!trimmed) {
411
+ return [];
412
+ }
413
+ const parsedDirect = parseJsonArray(trimmed);
414
+ if (parsedDirect) {
415
+ return parsedDirect;
416
+ }
417
+ const firstBracket = trimmed.indexOf('[');
418
+ const lastBracket = trimmed.lastIndexOf(']');
419
+ if (firstBracket >= 0 && lastBracket > firstBracket) {
420
+ const arraySlice = trimmed.slice(firstBracket, lastBracket + 1).trim();
421
+ const parsedSlice = parseJsonArray(arraySlice);
422
+ if (parsedSlice) {
423
+ return parsedSlice;
424
+ }
425
+ }
426
+ return null;
427
+ }
428
+ function parseJsonArray(raw) {
429
+ try {
430
+ const value = JSON.parse(raw);
431
+ if (Array.isArray(value)) {
432
+ return value;
433
+ }
434
+ if (value && typeof value === 'object') {
435
+ const record = value;
436
+ if (Array.isArray(record.items)) {
437
+ return record.items;
438
+ }
439
+ if (Array.isArray(record.issues)) {
440
+ return record.issues;
441
+ }
442
+ if (Array.isArray(record.data)) {
443
+ return record.data;
444
+ }
445
+ }
446
+ return null;
447
+ }
448
+ catch {
449
+ return null;
450
+ }
451
+ }
452
+ function buildObservationHash(messages) {
453
+ const tail = messages
454
+ .slice(-20)
455
+ .map((message) => normalizeMessageForHash(message))
456
+ .filter(Boolean)
457
+ .join('\n');
458
+ if (!tail) {
459
+ return 'empty';
460
+ }
461
+ return createHash('sha1').update(tail).digest('hex').slice(0, 16);
462
+ }
463
+ function stableStringifyForObservation(value) {
464
+ if (value === null)
465
+ return 'null';
466
+ const type = typeof value;
467
+ if (type === 'string')
468
+ return JSON.stringify(value);
469
+ if (type === 'number')
470
+ return Number.isFinite(value) ? String(value) : 'null';
471
+ if (type === 'boolean')
472
+ return value ? 'true' : 'false';
473
+ if (type === 'undefined' || type === 'function' || type === 'symbol')
474
+ return 'null';
475
+ if (Array.isArray(value)) {
476
+ return `[${value.map((item) => stableStringifyForObservation(item)).join(',')}]`;
477
+ }
478
+ if (value && typeof value === 'object') {
479
+ const record = value;
480
+ const keys = Object.keys(record).sort();
481
+ const chunks = [];
482
+ for (const key of keys) {
483
+ const child = record[key];
484
+ if (typeof child === 'undefined') {
485
+ continue;
486
+ }
487
+ chunks.push(`${JSON.stringify(key)}:${stableStringifyForObservation(child)}`);
488
+ }
489
+ return `{${chunks.join(',')}}`;
490
+ }
491
+ try {
492
+ return JSON.stringify(value);
493
+ }
494
+ catch {
495
+ return 'null';
496
+ }
497
+ }
498
+ function normalizeToolArgumentsForObservation(value) {
499
+ if (typeof value === 'string') {
500
+ const trimmed = value.trim();
501
+ if (!trimmed) {
502
+ return '{}';
503
+ }
504
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
505
+ try {
506
+ return stableStringifyForObservation(JSON.parse(trimmed));
507
+ }
508
+ catch {
509
+ return normalizeWhitespace(trimmed);
510
+ }
511
+ }
512
+ return normalizeWhitespace(trimmed);
513
+ }
514
+ if (typeof value === 'undefined') {
515
+ return '{}';
516
+ }
517
+ return stableStringifyForObservation(value);
518
+ }
519
+ function extractToolCallSignatureText(record) {
520
+ const signatures = [];
521
+ const appendSignature = (nameValue, argsValue) => {
522
+ const name = normalizeText(nameValue).toLowerCase();
523
+ if (!name) {
524
+ return;
525
+ }
526
+ const args = normalizeToolArgumentsForObservation(argsValue);
527
+ signatures.push(`${name}(${args})`);
528
+ };
529
+ const scan = (node) => {
530
+ if (!node) {
531
+ return;
532
+ }
533
+ if (Array.isArray(node)) {
534
+ for (const item of node) {
535
+ scan(item);
536
+ }
537
+ return;
538
+ }
539
+ if (typeof node !== 'object') {
540
+ return;
541
+ }
542
+ const current = node;
543
+ if (current.function && typeof current.function === 'object' && !Array.isArray(current.function)) {
544
+ const fn = current.function;
545
+ appendSignature(fn.name, fn.arguments);
546
+ }
547
+ if (normalizeText(current.type).toLowerCase() === 'function_call') {
548
+ appendSignature(current.name, current.arguments);
549
+ }
550
+ if (typeof current.name === 'string' && Object.prototype.hasOwnProperty.call(current, 'arguments')) {
551
+ appendSignature(current.name, current.arguments);
552
+ }
553
+ if (current.function_call && typeof current.function_call === 'object' && !Array.isArray(current.function_call)) {
554
+ const fnCall = current.function_call;
555
+ appendSignature(fnCall.name, fnCall.arguments);
556
+ }
557
+ };
558
+ scan(record.tool_calls);
559
+ scan(record.tool_call);
560
+ scan(record.function_call);
561
+ scan(record.output);
562
+ return signatures.join('; ');
563
+ }
564
+ function buildToolSignatureHash(messages) {
565
+ const signatures = messages
566
+ .slice(-20)
567
+ .map((message) => extractToolSignatureForHash(message))
568
+ .filter(Boolean)
569
+ .join('\n');
570
+ if (!signatures) {
571
+ return undefined;
572
+ }
573
+ return createHash('sha1').update(signatures).digest('hex').slice(0, 16);
574
+ }
575
+ function extractToolSignatureForHash(message) {
576
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
577
+ return '';
578
+ }
579
+ const record = message;
580
+ return extractToolCallSignatureText(record);
581
+ }
582
+ function normalizeMessageForHash(message) {
583
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
584
+ return '';
585
+ }
586
+ const record = message;
587
+ const role = normalizeText(record.role).toLowerCase() || 'unknown';
588
+ const text = extractMessageText(record.content || record.output || record.input || '');
589
+ const toolSignature = extractToolCallSignatureText(record);
590
+ const combined = [text, toolSignature].filter((entry) => typeof entry === 'string' && entry.trim()).join(' | ');
591
+ if (!combined) {
592
+ return role;
593
+ }
594
+ return `${role}:${combined.slice(0, 800)}`;
595
+ }
596
+ function normalizeMessageForHeuristics(message) {
597
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
598
+ return '';
599
+ }
600
+ const record = message;
601
+ const role = normalizeText(record.role).toLowerCase();
602
+ const text = extractMessageText(record.content || record.output || record.input || '');
603
+ return `${role}:${text}`.trim();
604
+ }
605
+ function extractMessageText(value) {
606
+ if (typeof value === 'string') {
607
+ return normalizeWhitespace(value);
608
+ }
609
+ if (Array.isArray(value)) {
610
+ const chunks = [];
611
+ for (const entry of value) {
612
+ const text = extractMessageText(entry);
613
+ if (text) {
614
+ chunks.push(text);
615
+ }
616
+ }
617
+ return normalizeWhitespace(chunks.join('\n'));
618
+ }
619
+ if (!value || typeof value !== 'object') {
620
+ return '';
621
+ }
622
+ const record = value;
623
+ const candidates = [
624
+ record.text,
625
+ record.content,
626
+ record.output_text,
627
+ record.input_text,
628
+ record.arguments,
629
+ record.name,
630
+ record.result,
631
+ record.stdout,
632
+ record.stderr
633
+ ];
634
+ const chunks = [];
635
+ for (const candidate of candidates) {
636
+ const text = extractMessageText(candidate);
637
+ if (text) {
638
+ chunks.push(text);
639
+ }
640
+ }
641
+ return normalizeWhitespace(chunks.join('\n'));
642
+ }
643
+ function normalizeWhitespace(text) {
644
+ return text.replace(/\s+/g, ' ').trim();
645
+ }
646
+ function normalizeText(value) {
647
+ return typeof value === 'string' ? value.trim() : '';
648
+ }
649
+ function normalizeStageName(value) {
650
+ if (typeof value !== 'string') {
651
+ return undefined;
652
+ }
653
+ const normalized = value.trim().toLowerCase();
654
+ if (normalized === 'status_probe' || normalized === 'active_continue' || normalized === 'loop_self_check') {
655
+ return normalized;
656
+ }
657
+ return undefined;
658
+ }
659
+ function normalizeBdWorkState(value) {
660
+ if (typeof value !== 'string') {
661
+ return undefined;
662
+ }
663
+ const normalized = value.trim().toLowerCase();
664
+ if (normalized === 'active' || normalized === 'idle' || normalized === 'unknown') {
665
+ return normalized;
666
+ }
667
+ return undefined;
668
+ }
669
+ function normalizeStageMode(value) {
670
+ if (typeof value !== 'string') {
671
+ return undefined;
672
+ }
673
+ const normalized = value.trim().toLowerCase();
674
+ if (normalized === 'on' || normalized === 'off' || normalized === 'auto') {
675
+ return normalized;
676
+ }
677
+ return undefined;
678
+ }
679
+ function toSafeInt(value, fallback) {
680
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
681
+ return fallback;
682
+ }
683
+ return Math.max(0, Math.floor(value));
684
+ }
@@ -66,7 +66,7 @@ const handler = async (ctx) => {
66
66
  }
67
67
  };
68
68
  };
69
- registerServerToolHandler('vision_auto', handler, { trigger: 'auto' });
69
+ registerServerToolHandler('vision_auto', handler, { trigger: 'auto', hook: { phase: 'post', priority: 60 } });
70
70
  export async function executeVisionBackendPlan(args) {
71
71
  const plan = args.plan;
72
72
  const options = args.options;