@jsonstudio/llms 0.6.1892 → 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.
Files changed (159) hide show
  1. package/dist/conversion/compat/actions/deepseek-web-request.js +16 -2
  2. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +7 -1
  3. package/dist/conversion/compat/actions/deepseek-web-response.js +302 -40
  4. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +5 -0
  5. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +7 -4
  6. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +1 -0
  7. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +12 -0
  8. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
  9. package/dist/conversion/compat/actions/tool-text-request-guidance.d.ts +9 -0
  10. package/dist/conversion/compat/actions/tool-text-request-guidance.js +177 -0
  11. package/dist/conversion/compat/antigravity-session-signature.d.ts +6 -0
  12. package/dist/conversion/compat/antigravity-session-signature.js +15 -0
  13. package/dist/conversion/compat/profiles/chat-deepseek-web.json +52 -1
  14. package/dist/conversion/compat/profiles/chat-glm.json +22 -0
  15. package/dist/conversion/compat/profiles/chat-iflow.json +4 -0
  16. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +13 -27
  17. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +10 -1
  18. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +13 -4
  19. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -53
  20. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline.js +8 -4
  22. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +191 -9
  23. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +118 -15
  24. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +65 -2
  25. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.d.ts +34 -0
  26. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.js +75 -0
  27. package/dist/conversion/hub/process/chat-process.js +85 -18
  28. package/dist/conversion/hub/response/provider-response.js +21 -50
  29. package/dist/conversion/hub/response/response-runtime.js +71 -10
  30. package/dist/conversion/responses/responses-openai-bridge/response-payload.d.ts +3 -0
  31. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +576 -0
  32. package/dist/conversion/responses/responses-openai-bridge/types.d.ts +42 -0
  33. package/dist/conversion/responses/responses-openai-bridge/types.js +1 -0
  34. package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -44
  35. package/dist/conversion/responses/responses-openai-bridge.js +193 -504
  36. package/dist/conversion/shared/anthropic-message-utils.js +82 -2
  37. package/dist/conversion/shared/bridge-message-utils.js +92 -39
  38. package/dist/conversion/shared/snapshot-hooks.js +8 -13
  39. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +2 -0
  40. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +129 -0
  41. package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +4 -0
  42. package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +637 -0
  43. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +21 -0
  44. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +177 -0
  45. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +5 -0
  46. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +385 -0
  47. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +10 -0
  48. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +602 -0
  49. package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +5 -0
  50. package/dist/conversion/shared/text-markup-normalizer/extractors.js +4 -0
  51. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +2 -0
  52. package/dist/conversion/shared/text-markup-normalizer/normalize.js +76 -0
  53. package/dist/conversion/shared/text-markup-normalizer.d.ts +3 -25
  54. package/dist/conversion/shared/text-markup-normalizer.js +2 -1386
  55. package/dist/conversion/shared/tool-governor.js +136 -10
  56. package/dist/filters/utils/snapshot-writer.js +3 -3
  57. package/dist/router/virtual-router/bootstrap/auth-utils.d.ts +6 -0
  58. package/dist/router/virtual-router/bootstrap/auth-utils.js +288 -0
  59. package/dist/router/virtual-router/bootstrap/claude-code-helpers.d.ts +11 -0
  60. package/dist/router/virtual-router/bootstrap/claude-code-helpers.js +18 -0
  61. package/dist/router/virtual-router/bootstrap/config-defaults.d.ts +5 -0
  62. package/dist/router/virtual-router/bootstrap/config-defaults.js +13 -0
  63. package/dist/router/virtual-router/bootstrap/config-normalizers.d.ts +4 -0
  64. package/dist/router/virtual-router/bootstrap/config-normalizers.js +106 -0
  65. package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +7 -0
  66. package/dist/router/virtual-router/bootstrap/profile-builder.js +68 -0
  67. package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +40 -0
  68. package/dist/router/virtual-router/bootstrap/provider-normalization.js +212 -0
  69. package/dist/router/virtual-router/bootstrap/responses-helpers.d.ts +15 -0
  70. package/dist/router/virtual-router/bootstrap/responses-helpers.js +65 -0
  71. package/dist/router/virtual-router/bootstrap/routing-config.d.ts +23 -0
  72. package/dist/router/virtual-router/bootstrap/routing-config.js +293 -0
  73. package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +12 -0
  74. package/dist/router/virtual-router/bootstrap/streaming-helpers.js +128 -0
  75. package/dist/router/virtual-router/bootstrap/utils.d.ts +5 -0
  76. package/dist/router/virtual-router/bootstrap/utils.js +41 -0
  77. package/dist/router/virtual-router/bootstrap/web-search-config.d.ts +4 -0
  78. package/dist/router/virtual-router/bootstrap/web-search-config.js +131 -0
  79. package/dist/router/virtual-router/bootstrap.d.ts +0 -4
  80. package/dist/router/virtual-router/bootstrap.js +31 -1275
  81. package/dist/router/virtual-router/classifier.js +32 -14
  82. package/dist/router/virtual-router/engine/antigravity/alias-lease.js +2 -2
  83. package/dist/router/virtual-router/engine/cooldown-manager.d.ts +34 -0
  84. package/dist/router/virtual-router/engine/cooldown-manager.js +118 -0
  85. package/dist/router/virtual-router/engine/route-analytics.d.ts +28 -0
  86. package/dist/router/virtual-router/engine/route-analytics.js +44 -0
  87. package/dist/router/virtual-router/engine/routing-pools/index.js +165 -4
  88. package/dist/router/virtual-router/engine/sticky-session-manager.d.ts +29 -0
  89. package/dist/router/virtual-router/engine/sticky-session-manager.js +55 -0
  90. package/dist/router/virtual-router/engine-logging.d.ts +42 -1
  91. package/dist/router/virtual-router/engine-logging.js +82 -15
  92. package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
  93. package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
  94. package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
  95. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
  96. package/dist/router/virtual-router/engine-selection/tier-selection.js +31 -1
  97. package/dist/router/virtual-router/engine.d.ts +21 -7
  98. package/dist/router/virtual-router/engine.js +198 -194
  99. package/dist/router/virtual-router/features.js +12 -4
  100. package/dist/router/virtual-router/message-utils.d.ts +8 -0
  101. package/dist/router/virtual-router/message-utils.js +170 -45
  102. package/dist/router/virtual-router/pre-command-file-resolver.js +40 -2
  103. package/dist/router/virtual-router/routing-instructions.d.ts +8 -0
  104. package/dist/router/virtual-router/routing-instructions.js +18 -2
  105. package/dist/router/virtual-router/routing-stop-message-actions.js +34 -10
  106. package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +2 -0
  107. package/dist/router/virtual-router/routing-stop-message-state-codec.js +50 -1
  108. package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
  109. package/dist/router/virtual-router/stop-message-state-sync.js +3 -0
  110. package/dist/router/virtual-router/token-counter.js +51 -10
  111. package/dist/router/virtual-router/tool-signals.js +4 -0
  112. package/dist/router/virtual-router/types.d.ts +15 -0
  113. package/dist/servertool/clock/session-scope.d.ts +3 -0
  114. package/dist/servertool/clock/session-scope.js +52 -0
  115. package/dist/servertool/clock/state.js +9 -0
  116. package/dist/servertool/clock/tasks.js +12 -1
  117. package/dist/servertool/clock/types.d.ts +3 -0
  118. package/dist/servertool/engine.js +177 -31
  119. package/dist/servertool/handlers/clock-auto.js +2 -8
  120. package/dist/servertool/handlers/clock.js +6 -9
  121. package/dist/servertool/handlers/recursive-detection-guard.js +53 -14
  122. package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
  123. package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
  124. package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
  125. package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
  126. package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
  127. package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
  128. package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
  129. package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
  130. package/dist/servertool/handlers/stop-message-auto.d.ts +1 -1
  131. package/dist/servertool/handlers/stop-message-auto.js +80 -556
  132. package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.d.ts +18 -0
  133. package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.js +398 -0
  134. package/dist/servertool/handlers/stop-message-stage-policy/decision.d.ts +9 -0
  135. package/dist/servertool/handlers/stop-message-stage-policy/decision.js +127 -0
  136. package/dist/servertool/handlers/stop-message-stage-policy/observation.d.ts +2 -0
  137. package/dist/servertool/handlers/stop-message-stage-policy/observation.js +179 -0
  138. package/dist/servertool/handlers/stop-message-stage-policy/templates.d.ts +4 -0
  139. package/dist/servertool/handlers/stop-message-stage-policy/templates.js +96 -0
  140. package/dist/servertool/handlers/stop-message-stage-policy/text-utils.d.ts +9 -0
  141. package/dist/servertool/handlers/stop-message-stage-policy/text-utils.js +89 -0
  142. package/dist/servertool/handlers/stop-message-stage-policy/types.d.ts +59 -0
  143. package/dist/servertool/handlers/stop-message-stage-policy/types.js +1 -0
  144. package/dist/servertool/handlers/stop-message-stage-policy.d.ts +3 -43
  145. package/dist/servertool/handlers/stop-message-stage-policy.js +2 -684
  146. package/dist/servertool/handlers/web-search.js +117 -0
  147. package/dist/servertool/server-side-tools.d.ts +0 -1
  148. package/dist/servertool/server-side-tools.js +4 -3
  149. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  150. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +1 -0
  151. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +110 -37
  152. package/dist/telemetry/stats-center.d.ts +9 -0
  153. package/dist/telemetry/stats-center.js +29 -1
  154. package/dist/tools/apply-patch/structured/coercion.js +3 -11
  155. package/dist/tools/exec-command/validator.d.ts +1 -0
  156. package/dist/tools/exec-command/validator.js +132 -0
  157. package/dist/tools/tool-registry.d.ts +1 -0
  158. package/dist/tools/tool-registry.js +1 -1
  159. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ import type { StopMessageBdCommandRunner, StopMessageBdTaskProbeResult, StopMessageBdWorkState } from './types.js';
2
+ export declare function resolveBdTaskSelectionFromRuntime(options?: {
3
+ bdCommandRunner?: StopMessageBdCommandRunner;
4
+ nowMs?: number;
5
+ cwd?: string;
6
+ limit?: number;
7
+ }): StopMessageBdTaskProbeResult;
8
+ export declare function resolveBdWorkStateFromRuntime(options?: {
9
+ bdCommandRunner?: StopMessageBdCommandRunner;
10
+ nowMs?: number;
11
+ cwd?: string;
12
+ }): StopMessageBdWorkState;
13
+ export declare function detectBdWorkState(messages: unknown[], options?: {
14
+ bdCommandRunner?: StopMessageBdCommandRunner;
15
+ nowMs?: number;
16
+ cwd?: string;
17
+ }): StopMessageBdWorkState;
18
+ export declare function resetStopMessageBdRuntimeCacheForTests(): void;
@@ -0,0 +1,398 @@
1
+ import * as childProcess from 'node:child_process';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { normalizeMessageForHeuristics, normalizeText } from './text-utils.js';
5
+ let bdRuntimeCache = null;
6
+ const DEFAULT_BD_TIMEOUT_MS = 1200;
7
+ const DEFAULT_BD_CACHE_TTL_MS = 1500;
8
+ export function resolveBdTaskSelectionFromRuntime(options) {
9
+ const commandRunner = options?.bdCommandRunner ?? runBdCommand;
10
+ const timeoutMs = resolveBdTimeoutMs();
11
+ const cwd = resolveBdWorkingDirectory(options?.cwd);
12
+ const limit = typeof options?.limit === 'number' && Number.isFinite(options.limit) && options.limit > 0
13
+ ? Math.max(1, Math.floor(options.limit))
14
+ : 20;
15
+ const inProgress = runBdIssueQuery(commandRunner, ['--no-db', 'list', '--status', 'in_progress', '--json', '--limit', String(limit)], { cwd, timeoutMs, source: 'in_progress' });
16
+ if (inProgress.status === 'unknown')
17
+ return { status: 'unknown' };
18
+ if (inProgress.issues.length > 0)
19
+ return { status: 'ok', task: inProgress.issues[0] };
20
+ const ready = runBdIssueQuery(commandRunner, ['--no-db', 'ready', '--json', '--limit', String(limit)], {
21
+ cwd,
22
+ timeoutMs,
23
+ source: 'ready'
24
+ });
25
+ if (ready.status === 'unknown')
26
+ return { status: 'unknown' };
27
+ if (ready.issues.length > 0)
28
+ return { status: 'ok', task: ready.issues[0] };
29
+ const open = runBdIssueQuery(commandRunner, ['--no-db', 'list', '--status', 'open', '--json', '--limit', String(limit)], {
30
+ cwd,
31
+ timeoutMs,
32
+ source: 'open'
33
+ });
34
+ if (open.status === 'unknown')
35
+ return { status: 'unknown' };
36
+ if (open.issues.length > 0)
37
+ return { status: 'ok', task: open.issues[0] };
38
+ return { status: 'ok' };
39
+ }
40
+ export function resolveBdWorkStateFromRuntime(options) {
41
+ const mode = resolveBdMode();
42
+ const nowMs = typeof options?.nowMs === 'number' && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
43
+ const cwd = resolveBdWorkingDirectory(options?.cwd);
44
+ const ttlMs = resolveBdCacheTtlMs();
45
+ if (ttlMs > 0 &&
46
+ bdRuntimeCache &&
47
+ bdRuntimeCache.mode === mode &&
48
+ bdRuntimeCache.cwd === cwd &&
49
+ bdRuntimeCache.expiresAt > nowMs) {
50
+ return bdRuntimeCache.state;
51
+ }
52
+ const commandRunner = options?.bdCommandRunner ?? runBdCommand;
53
+ const timeoutMs = resolveBdTimeoutMs();
54
+ const inProgress = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'in_progress', '--json', '--limit', '1'], {
55
+ cwd,
56
+ timeoutMs
57
+ });
58
+ if (inProgress === 'active') {
59
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
60
+ }
61
+ if (inProgress === 'unknown') {
62
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
63
+ }
64
+ const ready = runBdQuery(commandRunner, ['--no-db', 'ready', '--json', '--limit', '1'], {
65
+ cwd,
66
+ timeoutMs
67
+ });
68
+ if (ready === 'active') {
69
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
70
+ }
71
+ if (ready === 'unknown') {
72
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
73
+ }
74
+ const open = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'open', '--json', '--limit', '1'], {
75
+ cwd,
76
+ timeoutMs
77
+ });
78
+ if (open === 'active') {
79
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
80
+ }
81
+ if (open === 'unknown') {
82
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
83
+ }
84
+ const blocked = runBdQuery(commandRunner, ['--no-db', 'list', '--status', 'blocked', '--json', '--limit', '1'], {
85
+ cwd,
86
+ timeoutMs
87
+ });
88
+ if (blocked === 'active') {
89
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'active');
90
+ }
91
+ if (blocked === 'unknown') {
92
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'unknown');
93
+ }
94
+ return writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, 'idle');
95
+ }
96
+ export function detectBdWorkState(messages, options) {
97
+ const mode = resolveBdMode();
98
+ if (mode !== 'heuristic') {
99
+ const runtimeState = resolveBdWorkStateFromRuntime({
100
+ bdCommandRunner: options?.bdCommandRunner,
101
+ nowMs: options?.nowMs,
102
+ cwd: options?.cwd
103
+ });
104
+ if (runtimeState === 'active' || runtimeState === 'idle') {
105
+ return runtimeState;
106
+ }
107
+ if (mode === 'runtime') {
108
+ return 'unknown';
109
+ }
110
+ }
111
+ return detectBdWorkStateFromHeuristics(messages);
112
+ }
113
+ export function resetStopMessageBdRuntimeCacheForTests() {
114
+ bdRuntimeCache = null;
115
+ }
116
+ function detectBdWorkStateFromHeuristics(messages) {
117
+ const normalizedTail = messages
118
+ .slice(-30)
119
+ .map((message) => normalizeMessageForHeuristics(message))
120
+ .filter(Boolean)
121
+ .join('\n')
122
+ .toLowerCase();
123
+ if (!normalizedTail.includes('bd')) {
124
+ return 'unknown';
125
+ }
126
+ const idlePatterns = [
127
+ /\bno\s+(ready|open|in_progress)\b/i,
128
+ /\b0\s+(ready|open|in_progress)\b/i,
129
+ /\bnothing\s+(to\s+do|ready|in\s+progress)\b/i,
130
+ /\ball\s+(tasks?|issues?)\s+(done|closed)\b/i,
131
+ /没有可(做|推进)任务/i,
132
+ /无\s*(ready|open|in_progress)\s*任务/i
133
+ ];
134
+ if (idlePatterns.some((pattern) => pattern.test(normalizedTail))) {
135
+ return 'idle';
136
+ }
137
+ const activePatterns = [/\bin_progress\b/i, /\bready\b/i, /\bblocked\b/i, /\bopen\b/i, /\bepic status\b/i, /\bshow\s+[a-z0-9._-]+\b/i];
138
+ if (activePatterns.some((pattern) => pattern.test(normalizedTail))) {
139
+ return 'active';
140
+ }
141
+ return 'unknown';
142
+ }
143
+ function writeBdRuntimeCacheAndReturn(mode, cwd, nowMs, ttlMs, state) {
144
+ if (ttlMs > 0) {
145
+ bdRuntimeCache = {
146
+ mode,
147
+ cwd,
148
+ expiresAt: nowMs + ttlMs,
149
+ state
150
+ };
151
+ }
152
+ return state;
153
+ }
154
+ function resolveBdMode() {
155
+ const modeRaw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_MODE).toLowerCase();
156
+ if (modeRaw === 'runtime' || modeRaw === 'real') {
157
+ return 'runtime';
158
+ }
159
+ if (modeRaw === 'heuristic' || modeRaw === 'text') {
160
+ return 'heuristic';
161
+ }
162
+ const legacyRuntimeRaw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_RUNTIME).toLowerCase();
163
+ if (legacyRuntimeRaw === '1' || legacyRuntimeRaw === 'true' || legacyRuntimeRaw === 'on') {
164
+ return 'runtime';
165
+ }
166
+ if (legacyRuntimeRaw === '0' || legacyRuntimeRaw === 'false' || legacyRuntimeRaw === 'off') {
167
+ return 'heuristic';
168
+ }
169
+ return 'auto';
170
+ }
171
+ function resolveBdCacheTtlMs() {
172
+ const parsed = parseNonNegativeInteger(process.env.ROUTECODEX_STOPMESSAGE_BD_CACHE_TTL_MS);
173
+ if (typeof parsed === 'number') {
174
+ return parsed;
175
+ }
176
+ return DEFAULT_BD_CACHE_TTL_MS;
177
+ }
178
+ function resolveBdTimeoutMs() {
179
+ const parsed = parsePositiveInteger(process.env.ROUTECODEX_STOPMESSAGE_BD_TIMEOUT_MS);
180
+ if (typeof parsed === 'number') {
181
+ return parsed;
182
+ }
183
+ return DEFAULT_BD_TIMEOUT_MS;
184
+ }
185
+ function parseNonNegativeInteger(value) {
186
+ if (typeof value !== 'string') {
187
+ return undefined;
188
+ }
189
+ const parsed = Number.parseInt(value.trim(), 10);
190
+ if (!Number.isFinite(parsed) || parsed < 0) {
191
+ return undefined;
192
+ }
193
+ return parsed;
194
+ }
195
+ function parsePositiveInteger(value) {
196
+ const parsed = parseNonNegativeInteger(value);
197
+ if (typeof parsed !== 'number' || parsed <= 0) {
198
+ return undefined;
199
+ }
200
+ return parsed;
201
+ }
202
+ function resolveBdWorkingDirectory(cwdOverride) {
203
+ const fromOverride = normalizeText(cwdOverride);
204
+ if (fromOverride) {
205
+ return path.resolve(fromOverride);
206
+ }
207
+ const fromEnv = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_BD_WORKDIR);
208
+ if (fromEnv) {
209
+ return path.resolve(fromEnv);
210
+ }
211
+ const current = process.cwd();
212
+ return findBdProjectRoot(current) || current;
213
+ }
214
+ function findBdProjectRoot(startDirectory) {
215
+ let current = path.resolve(startDirectory);
216
+ while (true) {
217
+ const beadsDir = path.join(current, '.beads');
218
+ const issuesJsonlPath = path.join(beadsDir, 'issues.jsonl');
219
+ if (fs.existsSync(issuesJsonlPath)) {
220
+ return current;
221
+ }
222
+ if (fs.existsSync(beadsDir)) {
223
+ return current;
224
+ }
225
+ const parent = path.dirname(current);
226
+ if (parent === current) {
227
+ return null;
228
+ }
229
+ current = parent;
230
+ }
231
+ }
232
+ function runBdCommand(args, options) {
233
+ try {
234
+ const result = childProcess.spawnSync('bd', args, {
235
+ cwd: options.cwd,
236
+ encoding: 'utf8',
237
+ timeout: options.timeoutMs,
238
+ maxBuffer: 1024 * 1024
239
+ });
240
+ return {
241
+ status: result.status,
242
+ stdout: typeof result.stdout === 'string' ? result.stdout : String(result.stdout ?? ''),
243
+ stderr: typeof result.stderr === 'string' ? result.stderr : String(result.stderr ?? ''),
244
+ error: result.error
245
+ };
246
+ }
247
+ catch (error) {
248
+ return {
249
+ status: null,
250
+ stdout: '',
251
+ stderr: '',
252
+ error
253
+ };
254
+ }
255
+ }
256
+ function runBdQuery(commandRunner, args, options) {
257
+ const result = commandRunner(args, options);
258
+ if (result.error || result.status !== 0) {
259
+ return 'unknown';
260
+ }
261
+ const list = parseJsonArrayOutput(result.stdout);
262
+ if (!list) {
263
+ return 'unknown';
264
+ }
265
+ return list.length > 0 ? 'active' : 'idle';
266
+ }
267
+ function runBdIssueQuery(commandRunner, args, options) {
268
+ const result = commandRunner(args, { cwd: options.cwd, timeoutMs: options.timeoutMs });
269
+ if (result.error || result.status !== 0) {
270
+ return { status: 'unknown', issues: [] };
271
+ }
272
+ const list = parseJsonArrayOutput(result.stdout);
273
+ if (!list) {
274
+ return { status: 'unknown', issues: [] };
275
+ }
276
+ if (list.length === 0) {
277
+ return { status: 'ok', issues: [] };
278
+ }
279
+ const issues = list
280
+ .map((item) => normalizeIssueSelection(item, options.source))
281
+ .filter((item) => Boolean(item))
282
+ .sort(compareIssueSelection);
283
+ if (issues.length === 0) {
284
+ return { status: 'unknown', issues: [] };
285
+ }
286
+ return { status: 'ok', issues };
287
+ }
288
+ function parseJsonArrayOutput(stdout) {
289
+ const trimmed = typeof stdout === 'string' ? stdout.trim() : '';
290
+ if (!trimmed) {
291
+ return [];
292
+ }
293
+ const parsedDirect = parseJsonArray(trimmed);
294
+ if (parsedDirect) {
295
+ return parsedDirect;
296
+ }
297
+ const firstBracket = trimmed.indexOf('[');
298
+ const lastBracket = trimmed.lastIndexOf(']');
299
+ if (firstBracket >= 0 && lastBracket > firstBracket) {
300
+ const arraySlice = trimmed.slice(firstBracket, lastBracket + 1).trim();
301
+ const parsedSlice = parseJsonArray(arraySlice);
302
+ if (parsedSlice) {
303
+ return parsedSlice;
304
+ }
305
+ }
306
+ return null;
307
+ }
308
+ function parseJsonArray(raw) {
309
+ try {
310
+ const value = JSON.parse(raw);
311
+ if (Array.isArray(value)) {
312
+ return value;
313
+ }
314
+ if (value && typeof value === 'object') {
315
+ const record = value;
316
+ if (Array.isArray(record.items)) {
317
+ return record.items;
318
+ }
319
+ if (Array.isArray(record.issues)) {
320
+ return record.issues;
321
+ }
322
+ if (Array.isArray(record.data)) {
323
+ return record.data;
324
+ }
325
+ }
326
+ return null;
327
+ }
328
+ catch {
329
+ return null;
330
+ }
331
+ }
332
+ function normalizeIssueSelection(item, source) {
333
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
334
+ return null;
335
+ }
336
+ const record = item;
337
+ const issueId = normalizeText(record.id);
338
+ if (!issueId) {
339
+ return null;
340
+ }
341
+ const issueTitle = normalizeText(record.title) ||
342
+ normalizeText(record.summary) ||
343
+ normalizeText(record.name) ||
344
+ `Issue ${issueId}`;
345
+ const priority = parseIssuePriority(record.priority);
346
+ return {
347
+ issueId,
348
+ issueTitle: issueTitle.slice(0, 240),
349
+ priority,
350
+ source
351
+ };
352
+ }
353
+ function parseIssuePriority(value) {
354
+ if (typeof value === 'number' && Number.isFinite(value)) {
355
+ return Math.max(0, Math.floor(value));
356
+ }
357
+ if (typeof value === 'string') {
358
+ const normalized = value.trim().toLowerCase();
359
+ const pMatch = normalized.match(/^p?(\d+)$/);
360
+ if (pMatch) {
361
+ const parsed = Number.parseInt(pMatch[1], 10);
362
+ if (Number.isFinite(parsed)) {
363
+ return Math.max(0, parsed);
364
+ }
365
+ }
366
+ }
367
+ return 99;
368
+ }
369
+ function compareIssueSelection(a, b) {
370
+ if (a.priority !== b.priority) {
371
+ return a.priority - b.priority;
372
+ }
373
+ const idCompare = compareIssueId(a.issueId, b.issueId);
374
+ if (idCompare !== 0) {
375
+ return idCompare;
376
+ }
377
+ return a.issueTitle.localeCompare(b.issueTitle);
378
+ }
379
+ function compareIssueId(left, right) {
380
+ const leftParts = left.match(/\d+|\D+/g) || [left];
381
+ const rightParts = right.match(/\d+|\D+/g) || [right];
382
+ const maxLen = Math.max(leftParts.length, rightParts.length);
383
+ for (let idx = 0; idx < maxLen; idx += 1) {
384
+ const l = leftParts[idx] || '';
385
+ const r = rightParts[idx] || '';
386
+ const lNum = /^\d+$/.test(l) ? Number.parseInt(l, 10) : NaN;
387
+ const rNum = /^\d+$/.test(r) ? Number.parseInt(r, 10) : NaN;
388
+ if (Number.isFinite(lNum) && Number.isFinite(rNum)) {
389
+ if (lNum !== rNum)
390
+ return lNum - rNum;
391
+ continue;
392
+ }
393
+ const compare = l.localeCompare(r);
394
+ if (compare !== 0)
395
+ return compare;
396
+ }
397
+ return 0;
398
+ }
@@ -0,0 +1,9 @@
1
+ import type { StopMessageBdCommandRunner, StopMessageStageDecision, StopMessageStageStateSnapshot } from './types.js';
2
+ export declare function resolveStopMessageStageDecision(args: {
3
+ baseText: string;
4
+ state: StopMessageStageStateSnapshot;
5
+ capturedMessages: unknown[];
6
+ bdCommandRunner?: StopMessageBdCommandRunner;
7
+ bdWorkingDirectory?: string;
8
+ nowMs?: number;
9
+ }): StopMessageStageDecision;
@@ -0,0 +1,127 @@
1
+ import { detectBdWorkState, resolveBdTaskSelectionFromRuntime } from './bd-runtime.js';
2
+ import { buildObservationHash, buildToolSignatureHash } from './observation.js';
3
+ import { buildStageMessage, resolveStageMode, resolveStageTemplates } from './templates.js';
4
+ import { normalizeBdWorkState, normalizeStageName, normalizeText, toSafeInt } from './text-utils.js';
5
+ function buildAssignedTaskBaseText(baseText, assignedTask) {
6
+ const assignmentLine = `系统已分配任务:${assignedTask.issueId} - ${assignedTask.issueTitle}(来源:${assignedTask.source})`;
7
+ const behaviorLine = '请直接执行该任务,优先推进实现、修复与验证。';
8
+ const suffix = normalizeText(baseText);
9
+ if (!suffix) {
10
+ return `${assignmentLine}\n${behaviorLine}`.trim();
11
+ }
12
+ return `${assignmentLine}\n${behaviorLine}\n\n${suffix}`.trim();
13
+ }
14
+ export function resolveStopMessageStageDecision(args) {
15
+ const mode = resolveStageMode(args.state.stopMessageStageMode);
16
+ const observationHash = buildObservationHash(args.capturedMessages);
17
+ const toolSignatureHash = buildToolSignatureHash(args.capturedMessages);
18
+ if (mode === 'off') {
19
+ return {
20
+ action: 'stop',
21
+ observationHash,
22
+ observationStableCount: 0,
23
+ bdWorkState: 'unknown',
24
+ stopReason: 'mode_off',
25
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
26
+ };
27
+ }
28
+ const templates = resolveStageTemplates(mode);
29
+ if (!templates.enabled) {
30
+ return {
31
+ action: 'followup',
32
+ followupText: args.baseText,
33
+ observationHash,
34
+ observationStableCount: 0,
35
+ bdWorkState: 'unknown',
36
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
37
+ };
38
+ }
39
+ const previousHash = normalizeText(args.state.stopMessageObservationHash);
40
+ const previousStableCount = toSafeInt(args.state.stopMessageObservationStableCount, 0);
41
+ const stableCount = previousHash && previousHash === observationHash ? previousStableCount + 1 : 0;
42
+ const taskProbe = resolveBdTaskSelectionFromRuntime({
43
+ bdCommandRunner: args.bdCommandRunner,
44
+ cwd: args.bdWorkingDirectory,
45
+ nowMs: args.nowMs
46
+ });
47
+ const assignedTask = taskProbe.status === 'ok' ? taskProbe.task : undefined;
48
+ const bdWorkState = detectBdWorkState(args.capturedMessages, {
49
+ bdCommandRunner: args.bdCommandRunner,
50
+ cwd: args.bdWorkingDirectory,
51
+ nowMs: args.nowMs
52
+ });
53
+ if (taskProbe.status === 'ok' && !assignedTask) {
54
+ if (Boolean(args.state.stopMessageNoTaskSummaryUsed)) {
55
+ return {
56
+ action: 'stop',
57
+ observationHash,
58
+ observationStableCount: stableCount,
59
+ bdWorkState,
60
+ noTaskSummaryUsed: true,
61
+ stopReason: 'no_task_after_summary',
62
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
63
+ };
64
+ }
65
+ return {
66
+ action: 'followup',
67
+ stage: 'status_probe',
68
+ followupText: buildStageMessage(templates.noTaskSummaryTemplate, args.baseText),
69
+ observationHash,
70
+ observationStableCount: stableCount,
71
+ bdWorkState,
72
+ noTaskSummaryUsed: true,
73
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
74
+ };
75
+ }
76
+ if (mode !== 'on') {
77
+ const previousStage = normalizeStageName(args.state.stopMessageStage);
78
+ const previousBdWorkState = normalizeBdWorkState(args.state.stopMessageBdWorkState);
79
+ const idleConfirmed = bdWorkState === 'idle' && previousBdWorkState === 'idle' && previousStage === 'status_probe';
80
+ if (bdWorkState === 'idle') {
81
+ if (idleConfirmed) {
82
+ return {
83
+ action: 'stop',
84
+ observationHash,
85
+ observationStableCount: stableCount,
86
+ bdWorkState,
87
+ stopReason: 'bd_idle',
88
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
89
+ };
90
+ }
91
+ }
92
+ if (stableCount >= 2) {
93
+ return {
94
+ action: 'stop',
95
+ observationHash,
96
+ observationStableCount: stableCount,
97
+ bdWorkState,
98
+ stopReason: 'loop_stable',
99
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
100
+ };
101
+ }
102
+ }
103
+ const stage = stableCount > 0 ? 'loop_self_check' : bdWorkState === 'active' ? 'active_continue' : 'status_probe';
104
+ const stageTemplate = stage === 'status_probe'
105
+ ? templates.statusProbeTemplate
106
+ : stage === 'active_continue'
107
+ ? templates.activeContinueTemplate
108
+ : templates.loopSelfCheckTemplate;
109
+ const followupText = buildStageMessage(stageTemplate, assignedTask ? buildAssignedTaskBaseText(args.baseText, assignedTask) : args.baseText);
110
+ return {
111
+ action: 'followup',
112
+ stage,
113
+ followupText,
114
+ observationHash,
115
+ observationStableCount: stableCount,
116
+ bdWorkState,
117
+ ...(assignedTask
118
+ ? {
119
+ assignedIssueId: assignedTask.issueId,
120
+ assignedIssueTitle: assignedTask.issueTitle,
121
+ assignedIssueSource: assignedTask.source
122
+ }
123
+ : {}),
124
+ noTaskSummaryUsed: false,
125
+ ...(toolSignatureHash ? { toolSignatureHash } : {})
126
+ };
127
+ }
@@ -0,0 +1,2 @@
1
+ export declare function buildObservationHash(messages: unknown[]): string;
2
+ export declare function buildToolSignatureHash(messages: unknown[]): string | undefined;