@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
@@ -6,6 +6,7 @@ import { cancelClockTask, clearClockTasks, listClockTasks, resolveClockConfig, p
6
6
  import { getClockTimeSnapshot } from '../clock/ntp.js';
7
7
  import { nowMs } from '../clock/state.js';
8
8
  import { logClock } from '../clock/log.js';
9
+ import { resolveClockSessionScope } from '../clock/session-scope.js';
9
10
  const FLOW_ID = 'clock_flow';
10
11
  let fallbackClockToolCallSeq = 0;
11
12
  function ensureClockToolCall(toolCall, requestId) {
@@ -86,13 +87,6 @@ function parseToolArguments(toolCall) {
86
87
  return {};
87
88
  }
88
89
  }
89
- function resolveSessionId(adapterContext) {
90
- if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
91
- return null;
92
- }
93
- const sessionId = typeof adapterContext.sessionId === 'string' ? String(adapterContext.sessionId).trim() : '';
94
- return sessionId || null;
95
- }
96
90
  function injectClockToolOutput(base, toolCall, content) {
97
91
  const cloned = cloneJson(base);
98
92
  const existingOutputs = Array.isArray(cloned.tool_outputs)
@@ -236,6 +230,7 @@ function normalizeScheduleItems(parsed) {
236
230
  items.push({
237
231
  dueAtMs,
238
232
  task,
233
+ setBy: 'agent',
239
234
  ...(tool ? { tool } : {}),
240
235
  ...(argsObj ? { arguments: argsObj } : {}),
241
236
  ...(recurrenceParsed.recurrence ? { recurrence: recurrenceParsed.recurrence } : {})
@@ -259,6 +254,8 @@ function mapTaskForTool(t) {
259
254
  return {
260
255
  taskId: t.taskId,
261
256
  dueAt: toIso(t.dueAtMs),
257
+ setBy: t.setBy === 'agent' ? 'agent' : 'user',
258
+ setAt: toIso(t.createdAtMs),
262
259
  task: t.task,
263
260
  ...(t.tool ? { tool: t.tool } : {}),
264
261
  ...(t.arguments ? { arguments: t.arguments } : {}),
@@ -274,7 +271,7 @@ const handler = async (ctx) => {
274
271
  return null;
275
272
  }
276
273
  const rt = readRuntimeMetadata(ctx.adapterContext);
277
- const sessionId = resolveSessionId(ctx.adapterContext);
274
+ const sessionId = resolveClockSessionScope(ctx.adapterContext, rt);
278
275
  const rawConfig = rt?.clock ?? ctx.adapterContext.clock;
279
276
  // Default-enable clock when config is absent, but keep "explicitly disabled" honored.
280
277
  const clockConfig = resolveClockConfig(rawConfig);
@@ -388,7 +385,7 @@ const handler = async (ctx) => {
388
385
  return respond({
389
386
  ok: false,
390
387
  action,
391
- message: 'clock requires sessionId (x-session-id header or metadata.sessionId).'
388
+ message: 'clock requires session scope (sessionId/conversationId or clockDaemonId).'
392
389
  });
393
390
  }
394
391
  if (action === 'list') {
@@ -252,13 +252,34 @@ const handler = async (ctx) => {
252
252
  // Treat followup hops as an interruption: do not count them, and clear any ongoing streak
253
253
  // so that post-followup calls always restart from 0.
254
254
  if (shouldSkipFollowup(ctx.adapterContext)) {
255
- sessionStates.delete(sessionKey);
255
+ const prev = sessionStates.get(sessionKey);
256
+ if (prev) {
257
+ sessionStates.set(sessionKey, {
258
+ ...prev,
259
+ updatedAt: now,
260
+ signature: undefined,
261
+ consecutiveCount: 0
262
+ });
263
+ }
256
264
  return null;
257
265
  }
258
- const existing = sessionStates.get(sessionKey) ?? { updatedAt: now, consecutiveCount: 0 };
266
+ const existing = sessionStates.get(sessionKey) ?? { updatedAt: now, consecutiveCount: 0, triggerCount: 0 };
259
267
  // Any interruption (no tool calls) resets the counter.
260
268
  if (!ctx.toolCalls || !ctx.toolCalls.length) {
261
- if (existing.signature || existing.consecutiveCount) {
269
+ if (existing.signature || existing.consecutiveCount || existing.triggerCount) {
270
+ if (existing.triggerCount > 0) {
271
+ sessionStates.set(sessionKey, {
272
+ ...existing,
273
+ updatedAt: now,
274
+ signature: undefined,
275
+ consecutiveCount: 0
276
+ });
277
+ }
278
+ else {
279
+ sessionStates.delete(sessionKey);
280
+ }
281
+ }
282
+ else {
262
283
  sessionStates.delete(sessionKey);
263
284
  }
264
285
  return null;
@@ -282,8 +303,21 @@ const handler = async (ctx) => {
282
303
  if (state.consecutiveCount < CONSECUTIVE_TRIGGER_COUNT) {
283
304
  continue;
284
305
  }
285
- // Triggered: reset counter immediately (including "trigger info sent").
286
- sessionStates.delete(sessionKey);
306
+ const triggerCount = (state.triggerCount || 0) + 1;
307
+ const escalatedStop = triggerCount >= 2;
308
+ // Triggered: clear streak immediately; escalate on second trigger.
309
+ // After escalated stop, clear state so next execution starts fresh.
310
+ if (escalatedStop) {
311
+ sessionStates.delete(sessionKey);
312
+ }
313
+ else {
314
+ sessionStates.set(sessionKey, {
315
+ updatedAt: now,
316
+ signature: undefined,
317
+ consecutiveCount: 0,
318
+ triggerCount
319
+ });
320
+ }
287
321
  // Must send a followup request to provider (not a direct client warning).
288
322
  if (!ctx.capabilities.reenterPipeline) {
289
323
  return null;
@@ -304,9 +338,19 @@ const handler = async (ctx) => {
304
338
  if (!toolMessages.length) {
305
339
  return null;
306
340
  }
307
- const reminder = `请停下来检查你的任务,你已经反复进行了调用,是否考虑别的方式处理,如果无法处理请停下来,报告问题。` +
308
- `(触发条件:同一工具同一参数连续 ${CONSECUTIVE_TRIGGER_COUNT} 次:${toolCall.name})` +
309
- `请不要在回复中提及“检测器/守卫/servertool”,也不要继续调用该工具,除非用户明确要求且参数发生变化。`;
341
+ const reminder = escalatedStop
342
+ ? `再次检测到同一循环(同一工具同一参数连续 ${CONSECUTIVE_TRIGGER_COUNT} 次:${toolCall.name})。` +
343
+ `现在停止该工具链路并直接报告阻塞点,等待用户处理后再继续。`
344
+ : `检测到循环调用(同一工具同一参数连续 ${CONSECUTIVE_TRIGGER_COUNT} 次:${toolCall.name})。` +
345
+ `请先简短提醒风险并清理当前循环计数,然后继续自动执行;若再次出现同样循环,立即停止并报告阻塞。`;
346
+ const ops = [
347
+ { op: 'inject_system_text', text: reminder },
348
+ { op: 'append_assistant_message' },
349
+ { op: 'append_tool_messages_from_tool_outputs' }
350
+ ];
351
+ if (escalatedStop) {
352
+ ops.push({ op: 'drop_tool_by_name', name: toolCall.name });
353
+ }
310
354
  return {
311
355
  flowId: FLOW_ID,
312
356
  finalize: async () => ({
@@ -317,12 +361,7 @@ const handler = async (ctx) => {
317
361
  requestIdSuffix: ':recursive_detection_guard_followup',
318
362
  entryEndpoint: ctx.entryEndpoint,
319
363
  injection: {
320
- ops: [
321
- { op: 'inject_system_text', text: reminder },
322
- { op: 'append_assistant_message' },
323
- { op: 'append_tool_messages_from_tool_outputs' },
324
- { op: 'drop_tool_by_name', name: toolCall.name }
325
- ]
364
+ ops
326
365
  }
327
366
  }
328
367
  }
@@ -0,0 +1,16 @@
1
+ export interface StopMessageBlockedReport {
2
+ summary: string;
3
+ blocker: string;
4
+ impact?: string;
5
+ nextAction?: string;
6
+ evidence: string[];
7
+ }
8
+ export interface StopMessageBlockedIssueContext {
9
+ requestId?: string;
10
+ sessionId?: string;
11
+ }
12
+ export declare function extractBlockedReportFromMessages(messages: unknown[]): StopMessageBlockedReport | null;
13
+ export declare function extractBlockedReportFromMessagesForTests(messages: unknown[]): StopMessageBlockedReport | null;
14
+ export declare function createBdIssueFromBlockedReport(blockedReport: StopMessageBlockedReport, context?: StopMessageBlockedIssueContext, cwdOverride?: string): string | null;
15
+ export declare function extractCapturedMessageText(message: unknown): string;
16
+ export declare function extractTextFromMessageContent(content: unknown): string;
@@ -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;