@jsonstudio/llms 0.6.1749 → 0.6.1892
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/compat/actions/deepseek-web-request.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
- package/dist/conversion/hub/policy/policy-engine.js +8 -0
- package/dist/conversion/hub/process/chat-process.js +466 -16
- package/dist/conversion/hub/response/provider-response.js +0 -35
- package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
- package/dist/conversion/responses/responses-openai-bridge.js +166 -8
- package/dist/conversion/shared/anthropic-message-utils.js +10 -1
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
- package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
- package/dist/conversion/shared/tool-governor.js +102 -0
- package/dist/guidance/index.js +17 -0
- package/dist/router/virtual-router/bootstrap.js +46 -1
- package/dist/router/virtual-router/classifier.js +59 -4
- package/dist/router/virtual-router/engine/health/index.js +6 -6
- package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
- package/dist/router/virtual-router/engine-logging.js +62 -24
- package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
- package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
- package/dist/router/virtual-router/engine.d.ts +3 -1
- package/dist/router/virtual-router/engine.js +325 -38
- package/dist/router/virtual-router/features.js +2 -1
- package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
- package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
- package/dist/router/virtual-router/provider-registry.js +3 -1
- package/dist/router/virtual-router/routing-instructions.d.ts +11 -1
- package/dist/router/virtual-router/routing-instructions.js +101 -183
- package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
- package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
- package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
- package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
- package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
- package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
- package/dist/router/virtual-router/sticky-session-store.js +206 -57
- package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
- package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +1 -0
- package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
- package/dist/router/virtual-router/token-file-scanner.js +64 -3
- package/dist/router/virtual-router/tool-signals.d.ts +5 -0
- package/dist/router/virtual-router/tool-signals.js +42 -3
- package/dist/router/virtual-router/types.d.ts +15 -1
- package/dist/router/virtual-router/types.js +1 -0
- package/dist/servertool/clock/config.d.ts +1 -1
- package/dist/servertool/clock/config.js +27 -4
- package/dist/servertool/clock/state.js +41 -2
- package/dist/servertool/clock/task-store.d.ts +2 -2
- package/dist/servertool/clock/task-store.js +1 -1
- package/dist/servertool/clock/tasks.d.ts +3 -1
- package/dist/servertool/clock/tasks.js +209 -18
- package/dist/servertool/clock/types.d.ts +17 -0
- package/dist/servertool/continue-execution/log.d.ts +3 -0
- package/dist/servertool/continue-execution/log.js +13 -0
- package/dist/servertool/engine.js +414 -68
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
- package/dist/servertool/handlers/clock-auto.js +54 -71
- package/dist/servertool/handlers/clock.js +121 -6
- package/dist/servertool/handlers/continue-execution.d.ts +1 -0
- package/dist/servertool/handlers/continue-execution.js +91 -0
- package/dist/servertool/handlers/followup-request-builder.js +13 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
- package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
- package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +352 -257
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +22 -1
- package/dist/servertool/handlers/stop-message-stage-policy.js +472 -60
- package/dist/servertool/handlers/vision.js +1 -1
- package/dist/servertool/log/progress-file.d.ts +14 -0
- package/dist/servertool/log/progress-file.js +88 -0
- package/dist/servertool/pre-command-hooks.d.ts +17 -0
- package/dist/servertool/pre-command-hooks.js +491 -0
- package/dist/servertool/registry.d.ts +23 -6
- package/dist/servertool/registry.js +66 -1
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +216 -14
- package/dist/servertool/stop-gateway-context.d.ts +14 -0
- package/dist/servertool/stop-gateway-context.js +167 -0
- package/dist/servertool/stop-message-compare-context.d.ts +24 -0
- package/dist/servertool/stop-message-compare-context.js +133 -0
- package/dist/servertool/types.d.ts +12 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
- package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
- package/package.json +1 -1
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
export type StopMessageStageName = 'status_probe' | 'active_continue' | 'loop_self_check';
|
|
2
2
|
export type StopMessageBdWorkState = 'active' | 'idle' | 'unknown';
|
|
3
|
+
export type StopMessageBdMode = 'auto' | 'runtime' | 'heuristic';
|
|
3
4
|
export interface StopMessageStageStateSnapshot {
|
|
4
5
|
stopMessageStage?: string;
|
|
6
|
+
stopMessageStageMode?: 'on' | 'off' | 'auto' | string;
|
|
5
7
|
stopMessageObservationHash?: string;
|
|
6
8
|
stopMessageObservationStableCount?: number;
|
|
7
9
|
stopMessageBdWorkState?: string;
|
|
8
10
|
}
|
|
11
|
+
export interface StopMessageBdCommandResult {
|
|
12
|
+
status: number | null;
|
|
13
|
+
stdout: string;
|
|
14
|
+
stderr: string;
|
|
15
|
+
error?: unknown;
|
|
16
|
+
}
|
|
17
|
+
export type StopMessageBdCommandRunner = (args: string[], options: {
|
|
18
|
+
cwd: string;
|
|
19
|
+
timeoutMs: number;
|
|
20
|
+
}) => StopMessageBdCommandResult;
|
|
9
21
|
export interface StopMessageStageDecision {
|
|
10
22
|
action: 'followup' | 'stop';
|
|
11
23
|
stage?: StopMessageStageName;
|
|
12
24
|
followupText?: string;
|
|
13
25
|
observationHash: string;
|
|
14
26
|
observationStableCount: number;
|
|
27
|
+
toolSignatureHash?: string;
|
|
15
28
|
bdWorkState: StopMessageBdWorkState;
|
|
16
|
-
stopReason?: 'loop_stable' | 'bd_idle';
|
|
29
|
+
stopReason?: 'loop_stable' | 'bd_idle' | 'mode_off';
|
|
17
30
|
}
|
|
18
31
|
export declare function resolveStopMessageStageDecision(args: {
|
|
19
32
|
baseText: string;
|
|
20
33
|
state: StopMessageStageStateSnapshot;
|
|
21
34
|
capturedMessages: unknown[];
|
|
35
|
+
bdCommandRunner?: StopMessageBdCommandRunner;
|
|
36
|
+
nowMs?: number;
|
|
22
37
|
}): StopMessageStageDecision;
|
|
38
|
+
export declare function resolveBdWorkStateFromRuntime(options?: {
|
|
39
|
+
bdCommandRunner?: StopMessageBdCommandRunner;
|
|
40
|
+
nowMs?: number;
|
|
41
|
+
cwd?: string;
|
|
42
|
+
}): StopMessageBdWorkState;
|
|
43
|
+
export declare function resetStopMessageBdRuntimeCacheForTests(): void;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import
|
|
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';
|
|
3
6
|
const DEFAULT_STATUS_TEMPLATE = [
|
|
4
7
|
'先做状态确认再继续执行:',
|
|
5
|
-
'1) 运行 `bd --no-db ready
|
|
6
|
-
'2)
|
|
7
|
-
'3)
|
|
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` 新任务;不需要才停止。',
|
|
8
13
|
'',
|
|
9
14
|
'{{BASE_STOP_MESSAGE}}'
|
|
10
15
|
].join('\n');
|
|
11
16
|
const DEFAULT_ACTIVE_TEMPLATE = [
|
|
12
|
-
'
|
|
13
|
-
'1)
|
|
14
|
-
'2)
|
|
15
|
-
'3)
|
|
17
|
+
'检测到当前仍有任务,按优先级继续执行,不允许只汇报状态:',
|
|
18
|
+
'1) 先检查是否存在 blocked 任务;若存在,先分析阻塞原因并执行解阻塞动作(补依赖/修配置/拆子任务/补证据);',
|
|
19
|
+
'2) 需要调整执行顺序或新增动作时,必须用 bd 更新计划与状态(update/dep add/create),保持可追踪;',
|
|
20
|
+
'3) 若无 blocked,再继续当前 in_progress/open 任务并执行至少一个可验证动作(改代码/跑测试/更新证据);',
|
|
21
|
+
'4) 未完成上述动作前,不要输出“已完成/已汇报”类结论;完成后只汇报结果与下一步。',
|
|
16
22
|
'',
|
|
17
23
|
'{{BASE_STOP_MESSAGE}}'
|
|
18
24
|
].join('\n');
|
|
@@ -24,35 +30,57 @@ const DEFAULT_LOOP_TEMPLATE = [
|
|
|
24
30
|
'',
|
|
25
31
|
'{{BASE_STOP_MESSAGE}}'
|
|
26
32
|
].join('\n');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
loop_self_check: '<file://stopMessage/stage-loop-self-check.md>'
|
|
31
|
-
};
|
|
33
|
+
let bdRuntimeCache = null;
|
|
34
|
+
const DEFAULT_BD_TIMEOUT_MS = 1200;
|
|
35
|
+
const DEFAULT_BD_CACHE_TTL_MS = 1500;
|
|
32
36
|
export function resolveStopMessageStageDecision(args) {
|
|
33
|
-
const
|
|
37
|
+
const mode = resolveStageMode(args.state.stopMessageStageMode);
|
|
34
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);
|
|
35
51
|
if (!templates.enabled) {
|
|
36
52
|
return {
|
|
37
53
|
action: 'followup',
|
|
38
54
|
followupText: args.baseText,
|
|
39
55
|
observationHash,
|
|
40
56
|
observationStableCount: 0,
|
|
41
|
-
bdWorkState: 'unknown'
|
|
57
|
+
bdWorkState: 'unknown',
|
|
58
|
+
...(toolSignatureHash ? { toolSignatureHash } : {})
|
|
42
59
|
};
|
|
43
60
|
}
|
|
44
61
|
const previousHash = normalizeText(args.state.stopMessageObservationHash);
|
|
45
62
|
const previousStableCount = toSafeInt(args.state.stopMessageObservationStableCount, 0);
|
|
46
63
|
const stableCount = previousHash && previousHash === observationHash ? previousStableCount + 1 : 0;
|
|
47
|
-
const bdWorkState = detectBdWorkState(args.capturedMessages
|
|
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';
|
|
48
73
|
if (bdWorkState === 'idle') {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
}
|
|
56
84
|
}
|
|
57
85
|
if (stableCount >= 2) {
|
|
58
86
|
return {
|
|
@@ -60,7 +88,8 @@ export function resolveStopMessageStageDecision(args) {
|
|
|
60
88
|
observationHash,
|
|
61
89
|
observationStableCount: stableCount,
|
|
62
90
|
bdWorkState,
|
|
63
|
-
stopReason: 'loop_stable'
|
|
91
|
+
stopReason: 'loop_stable',
|
|
92
|
+
...(toolSignatureHash ? { toolSignatureHash } : {})
|
|
64
93
|
};
|
|
65
94
|
}
|
|
66
95
|
const stage = bdWorkState === 'active'
|
|
@@ -80,29 +109,87 @@ export function resolveStopMessageStageDecision(args) {
|
|
|
80
109
|
followupText,
|
|
81
110
|
observationHash,
|
|
82
111
|
observationStableCount: stableCount,
|
|
83
|
-
bdWorkState
|
|
112
|
+
bdWorkState,
|
|
113
|
+
...(toolSignatureHash ? { toolSignatureHash } : {})
|
|
84
114
|
};
|
|
85
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
|
+
}
|
|
86
175
|
function buildStageMessage(template, baseText) {
|
|
87
176
|
const safeBaseText = baseText.trim();
|
|
88
|
-
|
|
89
|
-
|
|
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
|
+
}
|
|
90
183
|
}
|
|
91
|
-
if (
|
|
92
|
-
return
|
|
184
|
+
if (!safeTemplate) {
|
|
185
|
+
return safeBaseText;
|
|
93
186
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
function resolveStageTemplates() {
|
|
97
|
-
const mode = resolveStageMode();
|
|
98
|
-
if (mode === 'off') {
|
|
99
|
-
return {
|
|
100
|
-
enabled: false,
|
|
101
|
-
statusProbeTemplate: '',
|
|
102
|
-
activeContinueTemplate: '',
|
|
103
|
-
loopSelfCheckTemplate: ''
|
|
104
|
-
};
|
|
187
|
+
if (!safeBaseText) {
|
|
188
|
+
return safeTemplate;
|
|
105
189
|
}
|
|
190
|
+
return `${safeTemplate}\n\n原始约束:\n${safeBaseText}`.trim();
|
|
191
|
+
}
|
|
192
|
+
function resolveStageTemplates(mode) {
|
|
106
193
|
const statusProbeTemplate = loadStageTemplate('status_probe');
|
|
107
194
|
const activeContinueTemplate = loadStageTemplate('active_continue');
|
|
108
195
|
const loopSelfCheckTemplate = loadStageTemplate('loop_self_check');
|
|
@@ -123,7 +210,11 @@ function resolveStageTemplates() {
|
|
|
123
210
|
loopSelfCheckTemplate: loopSelfCheckTemplate || DEFAULT_LOOP_TEMPLATE
|
|
124
211
|
};
|
|
125
212
|
}
|
|
126
|
-
function resolveStageMode() {
|
|
213
|
+
function resolveStageMode(modeFromState) {
|
|
214
|
+
const stateMode = normalizeStageMode(modeFromState);
|
|
215
|
+
if (stateMode) {
|
|
216
|
+
return stateMode;
|
|
217
|
+
}
|
|
127
218
|
const raw = normalizeText(process.env.ROUTECODEX_STOPMESSAGE_STAGE_MODE).toLowerCase();
|
|
128
219
|
if (raw === '1' || raw === 'true' || raw === 'on') {
|
|
129
220
|
return 'on';
|
|
@@ -134,27 +225,29 @@ function resolveStageMode() {
|
|
|
134
225
|
if (raw === 'auto') {
|
|
135
226
|
return 'auto';
|
|
136
227
|
}
|
|
137
|
-
return '
|
|
228
|
+
return 'auto';
|
|
138
229
|
}
|
|
139
230
|
function loadStageTemplate(stage) {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
+
}
|
|
155
247
|
}
|
|
248
|
+
return detectBdWorkStateFromHeuristics(messages);
|
|
156
249
|
}
|
|
157
|
-
function
|
|
250
|
+
function detectBdWorkStateFromHeuristics(messages) {
|
|
158
251
|
const normalizedTail = messages
|
|
159
252
|
.slice(-30)
|
|
160
253
|
.map((message) => normalizeMessageForHeuristics(message))
|
|
@@ -188,6 +281,174 @@ function detectBdWorkState(messages) {
|
|
|
188
281
|
}
|
|
189
282
|
return 'unknown';
|
|
190
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
|
+
}
|
|
191
452
|
function buildObservationHash(messages) {
|
|
192
453
|
const tail = messages
|
|
193
454
|
.slice(-20)
|
|
@@ -199,6 +460,125 @@ function buildObservationHash(messages) {
|
|
|
199
460
|
}
|
|
200
461
|
return createHash('sha1').update(tail).digest('hex').slice(0, 16);
|
|
201
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
|
+
}
|
|
202
582
|
function normalizeMessageForHash(message) {
|
|
203
583
|
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
204
584
|
return '';
|
|
@@ -206,10 +586,12 @@ function normalizeMessageForHash(message) {
|
|
|
206
586
|
const record = message;
|
|
207
587
|
const role = normalizeText(record.role).toLowerCase() || 'unknown';
|
|
208
588
|
const text = extractMessageText(record.content || record.output || record.input || '');
|
|
209
|
-
|
|
589
|
+
const toolSignature = extractToolCallSignatureText(record);
|
|
590
|
+
const combined = [text, toolSignature].filter((entry) => typeof entry === 'string' && entry.trim()).join(' | ');
|
|
591
|
+
if (!combined) {
|
|
210
592
|
return role;
|
|
211
593
|
}
|
|
212
|
-
return `${role}:${
|
|
594
|
+
return `${role}:${combined.slice(0, 800)}`;
|
|
213
595
|
}
|
|
214
596
|
function normalizeMessageForHeuristics(message) {
|
|
215
597
|
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
@@ -264,6 +646,36 @@ function normalizeWhitespace(text) {
|
|
|
264
646
|
function normalizeText(value) {
|
|
265
647
|
return typeof value === 'string' ? value.trim() : '';
|
|
266
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
|
+
}
|
|
267
679
|
function toSafeInt(value, fallback) {
|
|
268
680
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
269
681
|
return fallback;
|
|
@@ -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;
|