@jsonstudio/llms 0.6.1739 → 0.6.1890
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +359 -39
- 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 +15 -1
- package/dist/router/virtual-router/routing-instructions.js +110 -151
- 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 +5 -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 +19 -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 +386 -257
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
- package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
- 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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
function resolveRoutecodexUserDir() {
|
|
5
|
+
const override = process.env.ROUTECODEX_USER_DIR;
|
|
6
|
+
if (override && override.trim()) {
|
|
7
|
+
return override.trim();
|
|
8
|
+
}
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
if (!home) {
|
|
11
|
+
throw new Error('precommand: cannot resolve homedir');
|
|
12
|
+
}
|
|
13
|
+
return path.join(home, '.routecodex');
|
|
14
|
+
}
|
|
15
|
+
function resolvePreCommandBaseDir() {
|
|
16
|
+
return path.resolve(resolveRoutecodexUserDir(), 'precommand');
|
|
17
|
+
}
|
|
18
|
+
function normalizeRelativePath(raw) {
|
|
19
|
+
const normalized = path.posix.normalize(raw.replace(/\\/g, '/'));
|
|
20
|
+
if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../')) {
|
|
21
|
+
throw new Error('precommand: invalid relative path');
|
|
22
|
+
}
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
function normalizePreCommandRelativePath(raw, fromFileScheme) {
|
|
26
|
+
const normalized = normalizeRelativePath(raw);
|
|
27
|
+
if (normalized === 'precommand') {
|
|
28
|
+
throw new Error('precommand: expected script file under ~/.routecodex/precommand');
|
|
29
|
+
}
|
|
30
|
+
if (normalized.startsWith('precommand/')) {
|
|
31
|
+
return normalized.slice('precommand/'.length);
|
|
32
|
+
}
|
|
33
|
+
if (fromFileScheme) {
|
|
34
|
+
throw new Error('precommand file://: path must be under file://precommand/...');
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
export function resolvePreCommandScriptPath(raw) {
|
|
39
|
+
let text = typeof raw === 'string' ? raw.trim() : '';
|
|
40
|
+
if (!text) {
|
|
41
|
+
throw new Error('precommand: missing script path');
|
|
42
|
+
}
|
|
43
|
+
if (text.startsWith('<') && text.endsWith('>') && text.length >= 3) {
|
|
44
|
+
text = text.slice(1, -1).trim();
|
|
45
|
+
}
|
|
46
|
+
const fromFileScheme = /^file:\/\//i.test(text);
|
|
47
|
+
const relRaw = fromFileScheme ? text.slice('file://'.length).trim() : text;
|
|
48
|
+
if (!relRaw) {
|
|
49
|
+
throw new Error('precommand file://: missing relative path');
|
|
50
|
+
}
|
|
51
|
+
if (relRaw.startsWith('/') || relRaw.startsWith('\\') || /^[a-zA-Z]:[\\/]/.test(relRaw)) {
|
|
52
|
+
throw new Error('precommand: only supports paths relative to ~/.routecodex/precommand');
|
|
53
|
+
}
|
|
54
|
+
const relToPreCommand = normalizePreCommandRelativePath(relRaw, fromFileScheme);
|
|
55
|
+
const base = resolvePreCommandBaseDir();
|
|
56
|
+
const abs = path.resolve(base, relToPreCommand);
|
|
57
|
+
if (abs !== base && !abs.startsWith(`${base}${path.sep}`)) {
|
|
58
|
+
throw new Error('precommand: path escapes ~/.routecodex/precommand');
|
|
59
|
+
}
|
|
60
|
+
let stat;
|
|
61
|
+
try {
|
|
62
|
+
stat = fs.statSync(abs);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const message = err && typeof err.message === 'string' ? err.message : String(err || 'unknown error');
|
|
66
|
+
throw new Error(`precommand: cannot stat ${abs}: ${message}`);
|
|
67
|
+
}
|
|
68
|
+
if (!stat.isFile()) {
|
|
69
|
+
throw new Error(`precommand: not a file: ${abs}`);
|
|
70
|
+
}
|
|
71
|
+
return abs;
|
|
72
|
+
}
|
|
73
|
+
export function isPreCommandScriptPathAllowed(rawPath) {
|
|
74
|
+
const scriptPath = typeof rawPath === 'string' ? rawPath.trim() : '';
|
|
75
|
+
if (!scriptPath) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const base = resolvePreCommandBaseDir();
|
|
79
|
+
const abs = path.resolve(scriptPath);
|
|
80
|
+
if (abs !== base && !abs.startsWith(`${base}${path.sep}`)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const stat = fs.statSync(abs);
|
|
85
|
+
return stat.isFile();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -84,7 +84,8 @@ export class ProviderRegistry {
|
|
|
84
84
|
processMode: profile.processMode || 'chat',
|
|
85
85
|
responsesConfig: profile.responsesConfig,
|
|
86
86
|
streaming: profile.streaming,
|
|
87
|
-
maxContextTokens: profile.maxContextTokens
|
|
87
|
+
maxContextTokens: profile.maxContextTokens,
|
|
88
|
+
...(profile.deepseek ? { deepseek: profile.deepseek } : {})
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
static normalizeProfile(key, profile) {
|
|
@@ -103,6 +104,7 @@ export class ProviderRegistry {
|
|
|
103
104
|
responsesConfig: profile.responsesConfig,
|
|
104
105
|
streaming: profile.streaming,
|
|
105
106
|
maxContextTokens: profile.maxContextTokens,
|
|
107
|
+
...(profile.deepseek ? { deepseek: profile.deepseek } : {}),
|
|
106
108
|
...(profile.serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
107
109
|
};
|
|
108
110
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { StandardizedMessage } from '../../conversion/hub/types/standardized.js';
|
|
2
2
|
export interface RoutingInstruction {
|
|
3
|
-
type: 'force' | 'sticky' | 'prefer' | 'disable' | 'enable' | 'clear' | 'allow' | 'stopMessageSet' | 'stopMessageClear';
|
|
3
|
+
type: 'force' | 'sticky' | 'prefer' | 'disable' | 'enable' | 'clear' | 'allow' | 'stopMessageSet' | 'stopMessageMode' | 'stopMessageClear' | 'preCommandSet' | 'preCommandClear';
|
|
4
4
|
provider?: string;
|
|
5
5
|
keyAlias?: string;
|
|
6
6
|
keyIndex?: number;
|
|
7
7
|
model?: string;
|
|
8
8
|
pathLength?: number;
|
|
9
|
+
processMode?: 'chat' | 'passthrough';
|
|
9
10
|
stopMessageText?: string;
|
|
10
11
|
stopMessageMaxRepeats?: number;
|
|
12
|
+
stopMessageStageMode?: 'on' | 'off' | 'auto';
|
|
13
|
+
preCommandScriptPath?: string;
|
|
11
14
|
}
|
|
12
15
|
export interface RoutingInstructionState {
|
|
13
16
|
forcedTarget?: {
|
|
@@ -16,6 +19,7 @@ export interface RoutingInstructionState {
|
|
|
16
19
|
keyIndex?: number;
|
|
17
20
|
model?: string;
|
|
18
21
|
pathLength?: number;
|
|
22
|
+
processMode?: 'chat' | 'passthrough';
|
|
19
23
|
};
|
|
20
24
|
stickyTarget?: {
|
|
21
25
|
provider?: string;
|
|
@@ -23,6 +27,7 @@ export interface RoutingInstructionState {
|
|
|
23
27
|
keyIndex?: number;
|
|
24
28
|
model?: string;
|
|
25
29
|
pathLength?: number;
|
|
30
|
+
processMode?: 'chat' | 'passthrough';
|
|
26
31
|
};
|
|
27
32
|
preferTarget?: {
|
|
28
33
|
provider?: string;
|
|
@@ -30,6 +35,7 @@ export interface RoutingInstructionState {
|
|
|
30
35
|
keyIndex?: number;
|
|
31
36
|
model?: string;
|
|
32
37
|
pathLength?: number;
|
|
38
|
+
processMode?: 'chat' | 'passthrough';
|
|
33
39
|
};
|
|
34
40
|
allowedProviders: Set<string>;
|
|
35
41
|
disabledProviders: Set<string>;
|
|
@@ -46,6 +52,14 @@ export interface RoutingInstructionState {
|
|
|
46
52
|
stopMessageUsed?: number;
|
|
47
53
|
stopMessageUpdatedAt?: number;
|
|
48
54
|
stopMessageLastUsedAt?: number;
|
|
55
|
+
stopMessageStage?: string;
|
|
56
|
+
stopMessageStageMode?: 'on' | 'off' | 'auto';
|
|
57
|
+
stopMessageObservationHash?: string;
|
|
58
|
+
stopMessageObservationStableCount?: number;
|
|
59
|
+
stopMessageBdWorkState?: string;
|
|
60
|
+
preCommandSource?: string;
|
|
61
|
+
preCommandScriptPath?: string;
|
|
62
|
+
preCommandUpdatedAt?: number;
|
|
49
63
|
}
|
|
50
64
|
export declare function parseRoutingInstructions(messages: StandardizedMessage[]): RoutingInstruction[];
|
|
51
65
|
/**
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { extractMessageText } from './message-utils.js';
|
|
2
|
-
import {
|
|
2
|
+
import { parseStopMessageInstruction } from './routing-stop-message-parser.js';
|
|
3
|
+
import { applyStopMessageInstructionToState } from './routing-stop-message-actions.js';
|
|
4
|
+
import { parsePreCommandInstruction } from './routing-pre-command-parser.js';
|
|
5
|
+
import { applyPreCommandInstructionToState, clearPreCommandState } from './routing-pre-command-actions.js';
|
|
6
|
+
import { deserializeStopMessageState, serializeStopMessageState } from './routing-stop-message-state-codec.js';
|
|
7
|
+
import { deserializePreCommandState, serializePreCommandState } from './routing-pre-command-state-codec.js';
|
|
3
8
|
export function parseRoutingInstructions(messages) {
|
|
4
9
|
const instructions = [];
|
|
5
10
|
// 从最新一条携带路由指令标记(<** ... **>)的 user 消息中解析指令,
|
|
@@ -45,10 +50,8 @@ export function parseRoutingInstructions(messages) {
|
|
|
45
50
|
for (const segment of segments) {
|
|
46
51
|
const parsed = parseSingleInstruction(segment);
|
|
47
52
|
if (parsed) {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
// would keep re-applying stopMessage after it has been consumed/cleared.
|
|
51
|
-
if ((parsed.type === 'stopMessageSet' || parsed.type === 'stopMessageClear') &&
|
|
53
|
+
// preCommand 是强副作用命令(执行本地脚本),仅允许从 latest user message 生效。
|
|
54
|
+
if ((parsed.type === 'preCommandSet' || parsed.type === 'preCommandClear') &&
|
|
52
55
|
lastUserIndex >= 0 &&
|
|
53
56
|
sanitizedIndex >= 0 &&
|
|
54
57
|
sanitizedIndex !== lastUserIndex) {
|
|
@@ -105,7 +108,7 @@ function expandInstructionSegments(instruction) {
|
|
|
105
108
|
}
|
|
106
109
|
// stopMessage 指令需要整体解析,不能按逗号拆分,否则类似
|
|
107
110
|
// "<**stopMessage:\"继续\",3**>" 会被错误拆成 ["stopMessage:\"继续\"", "3"]。
|
|
108
|
-
if (/^stopMessage\s*:/i.test(trimmed)) {
|
|
111
|
+
if (/^stopMessage\s*:/i.test(trimmed) || /^precommand(?:\s*:|$)/i.test(trimmed)) {
|
|
109
112
|
return [trimmed];
|
|
110
113
|
}
|
|
111
114
|
const prefix = trimmed[0];
|
|
@@ -124,80 +127,75 @@ function splitInstructionTargets(content) {
|
|
|
124
127
|
.map((segment) => segment.trim())
|
|
125
128
|
.filter((segment) => segment.length > 0);
|
|
126
129
|
}
|
|
130
|
+
function splitTargetAndProcessMode(rawTarget) {
|
|
131
|
+
const trimmed = typeof rawTarget === 'string' ? rawTarget.trim() : '';
|
|
132
|
+
if (!trimmed) {
|
|
133
|
+
return { target: '' };
|
|
134
|
+
}
|
|
135
|
+
const separatorIndex = trimmed.lastIndexOf(':');
|
|
136
|
+
if (separatorIndex <= 0 || separatorIndex === trimmed.length - 1) {
|
|
137
|
+
return { target: trimmed };
|
|
138
|
+
}
|
|
139
|
+
const target = trimmed.slice(0, separatorIndex).trim();
|
|
140
|
+
const modeToken = trimmed.slice(separatorIndex + 1).trim().toLowerCase();
|
|
141
|
+
if (!target) {
|
|
142
|
+
return { target: trimmed };
|
|
143
|
+
}
|
|
144
|
+
if (modeToken === 'passthrough') {
|
|
145
|
+
return { target, processMode: 'passthrough' };
|
|
146
|
+
}
|
|
147
|
+
if (modeToken === 'chat') {
|
|
148
|
+
return { target, processMode: 'chat' };
|
|
149
|
+
}
|
|
150
|
+
return { target };
|
|
151
|
+
}
|
|
152
|
+
function parseNamedTargetInstruction(instruction, prefix) {
|
|
153
|
+
const re = new RegExp('^' + prefix + '\\s*:', 'i');
|
|
154
|
+
if (!re.test(instruction)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const body = instruction.slice(instruction.indexOf(':') + 1).trim();
|
|
158
|
+
if (!body) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const { target, processMode } = splitTargetAndProcessMode(body);
|
|
162
|
+
if (!target) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const parsed = parseTarget(target);
|
|
166
|
+
if (!parsed) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const normalized = normalizeStickyOrForceTarget(parsed);
|
|
170
|
+
return { type: prefix, ...normalized, ...(processMode ? { processMode } : {}) };
|
|
171
|
+
}
|
|
127
172
|
function parseSingleInstruction(instruction) {
|
|
128
173
|
if (instruction === 'clear') {
|
|
129
174
|
return { type: 'clear' };
|
|
130
175
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
if (ch === '\\') {
|
|
152
|
-
escaped = true;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (ch === '"') {
|
|
156
|
-
endIndex = i;
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (endIndex <= 0) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
const rawText = cursor.slice(1, endIndex);
|
|
164
|
-
text = rawText.replace(/\\"/g, '"');
|
|
165
|
-
cursor = cursor.slice(endIndex + 1).trim();
|
|
166
|
-
if (cursor.startsWith(',')) {
|
|
167
|
-
const countRaw = cursor.slice(1).trim();
|
|
168
|
-
if (countRaw) {
|
|
169
|
-
const parsed = Number.parseInt(countRaw, 10);
|
|
170
|
-
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
171
|
-
maxRepeats = parsed;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
// 支持无引号的简单形式:stopMessage:继续,3
|
|
178
|
-
const parts = cursor.split(',').map((part) => part.trim()).filter(Boolean);
|
|
179
|
-
if (!parts.length) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
text = parts[0];
|
|
183
|
-
if (parts.length > 1) {
|
|
184
|
-
const parsed = Number.parseInt(parts[1], 10);
|
|
185
|
-
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
186
|
-
maxRepeats = parsed;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (!text) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
type: 'stopMessageSet',
|
|
195
|
-
stopMessageText: resolveStopMessageText(text),
|
|
196
|
-
stopMessageMaxRepeats: maxRepeats
|
|
197
|
-
};
|
|
176
|
+
const preCommandInstruction = parsePreCommandInstruction(instruction);
|
|
177
|
+
if (preCommandInstruction) {
|
|
178
|
+
return preCommandInstruction;
|
|
179
|
+
}
|
|
180
|
+
const stopMessageInstruction = parseStopMessageInstruction(instruction);
|
|
181
|
+
if (stopMessageInstruction) {
|
|
182
|
+
return stopMessageInstruction;
|
|
183
|
+
}
|
|
184
|
+
const stickyInstruction = parseNamedTargetInstruction(instruction, 'sticky');
|
|
185
|
+
if (stickyInstruction) {
|
|
186
|
+
return stickyInstruction;
|
|
187
|
+
}
|
|
188
|
+
const forceInstruction = parseNamedTargetInstruction(instruction, 'force');
|
|
189
|
+
if (forceInstruction) {
|
|
190
|
+
return forceInstruction;
|
|
191
|
+
}
|
|
192
|
+
const preferInstruction = parseNamedTargetInstruction(instruction, 'prefer');
|
|
193
|
+
if (preferInstruction) {
|
|
194
|
+
return preferInstruction;
|
|
198
195
|
}
|
|
199
196
|
if (instruction.startsWith('!')) {
|
|
200
|
-
const
|
|
197
|
+
const rawTarget = instruction.substring(1).trim();
|
|
198
|
+
const { target, processMode } = splitTargetAndProcessMode(rawTarget);
|
|
201
199
|
if (!target) {
|
|
202
200
|
return null;
|
|
203
201
|
}
|
|
@@ -218,7 +216,7 @@ function parseSingleInstruction(instruction) {
|
|
|
218
216
|
return null;
|
|
219
217
|
}
|
|
220
218
|
const normalized = normalizeStickyOrForceTarget(parsed);
|
|
221
|
-
return { type: 'prefer', ...normalized };
|
|
219
|
+
return { type: 'prefer', ...normalized, ...(processMode ? { processMode } : {}) };
|
|
222
220
|
}
|
|
223
221
|
else if (instruction.startsWith('#')) {
|
|
224
222
|
const target = instruction.substring(1).trim();
|
|
@@ -362,11 +360,20 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
362
360
|
disabledProviders: new Set(currentState.disabledProviders),
|
|
363
361
|
disabledKeys: new Map(Array.from(currentState.disabledKeys.entries()).map(([k, v]) => [k, new Set(v)])),
|
|
364
362
|
disabledModels: new Map(Array.from(currentState.disabledModels.entries()).map(([k, v]) => [k, new Set(v)])),
|
|
363
|
+
stopMessageSource: currentState.stopMessageSource,
|
|
365
364
|
stopMessageText: currentState.stopMessageText,
|
|
366
365
|
stopMessageMaxRepeats: currentState.stopMessageMaxRepeats,
|
|
367
366
|
stopMessageUsed: currentState.stopMessageUsed,
|
|
368
367
|
stopMessageUpdatedAt: currentState.stopMessageUpdatedAt,
|
|
369
|
-
stopMessageLastUsedAt: currentState.stopMessageLastUsedAt
|
|
368
|
+
stopMessageLastUsedAt: currentState.stopMessageLastUsedAt,
|
|
369
|
+
stopMessageStage: currentState.stopMessageStage,
|
|
370
|
+
stopMessageStageMode: currentState.stopMessageStageMode,
|
|
371
|
+
stopMessageObservationHash: currentState.stopMessageObservationHash,
|
|
372
|
+
stopMessageObservationStableCount: currentState.stopMessageObservationStableCount,
|
|
373
|
+
stopMessageBdWorkState: currentState.stopMessageBdWorkState,
|
|
374
|
+
preCommandSource: currentState.preCommandSource,
|
|
375
|
+
preCommandScriptPath: currentState.preCommandScriptPath,
|
|
376
|
+
preCommandUpdatedAt: currentState.preCommandUpdatedAt
|
|
370
377
|
};
|
|
371
378
|
let allowReset = false;
|
|
372
379
|
let disableReset = false;
|
|
@@ -378,7 +385,8 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
378
385
|
keyAlias: instruction.keyAlias,
|
|
379
386
|
keyIndex: instruction.keyIndex,
|
|
380
387
|
model: instruction.model,
|
|
381
|
-
pathLength: instruction.pathLength
|
|
388
|
+
pathLength: instruction.pathLength,
|
|
389
|
+
processMode: instruction.processMode
|
|
382
390
|
};
|
|
383
391
|
// 保留 stickyTarget,允许单次 force 覆盖但不清除持久 sticky
|
|
384
392
|
// newState.stickyTarget = undefined;
|
|
@@ -389,7 +397,8 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
389
397
|
keyAlias: instruction.keyAlias,
|
|
390
398
|
keyIndex: instruction.keyIndex,
|
|
391
399
|
model: instruction.model,
|
|
392
|
-
pathLength: instruction.pathLength
|
|
400
|
+
pathLength: instruction.pathLength,
|
|
401
|
+
processMode: instruction.processMode
|
|
393
402
|
};
|
|
394
403
|
newState.forcedTarget = undefined;
|
|
395
404
|
break;
|
|
@@ -399,7 +408,8 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
399
408
|
keyAlias: instruction.keyAlias,
|
|
400
409
|
keyIndex: instruction.keyIndex,
|
|
401
410
|
model: instruction.model,
|
|
402
|
-
pathLength: instruction.pathLength
|
|
411
|
+
pathLength: instruction.pathLength,
|
|
412
|
+
processMode: instruction.processMode
|
|
403
413
|
};
|
|
404
414
|
newState.forcedTarget = undefined;
|
|
405
415
|
newState.stickyTarget = undefined;
|
|
@@ -490,43 +500,16 @@ export function applyRoutingInstructions(instructions, currentState) {
|
|
|
490
500
|
newState.disabledProviders.clear();
|
|
491
501
|
newState.disabledKeys.clear();
|
|
492
502
|
newState.disabledModels.clear();
|
|
503
|
+
clearPreCommandState(newState);
|
|
493
504
|
break;
|
|
494
|
-
case 'stopMessageSet':
|
|
495
|
-
|
|
496
|
-
? instruction.stopMessageText.trim()
|
|
497
|
-
: '';
|
|
498
|
-
const maxRepeats = typeof instruction.stopMessageMaxRepeats === 'number' && Number.isFinite(instruction.stopMessageMaxRepeats)
|
|
499
|
-
? Math.floor(instruction.stopMessageMaxRepeats)
|
|
500
|
-
: 0;
|
|
501
|
-
if (text && maxRepeats > 0) {
|
|
502
|
-
const sameText = typeof newState.stopMessageText === 'string' &&
|
|
503
|
-
newState.stopMessageText.trim() === text;
|
|
504
|
-
const sameMax = typeof newState.stopMessageMaxRepeats === 'number' &&
|
|
505
|
-
Math.floor(newState.stopMessageMaxRepeats) === maxRepeats;
|
|
506
|
-
const isSameInstruction = sameText && sameMax;
|
|
507
|
-
const used = typeof newState.stopMessageUsed === 'number' && Number.isFinite(newState.stopMessageUsed)
|
|
508
|
-
? Math.max(0, Math.floor(newState.stopMessageUsed))
|
|
509
|
-
: 0;
|
|
510
|
-
const hasLastUsedAt = typeof newState.stopMessageLastUsedAt === 'number' && Number.isFinite(newState.stopMessageLastUsedAt);
|
|
511
|
-
const shouldRearm = !isSameInstruction || used > 0 || hasLastUsedAt;
|
|
512
|
-
newState.stopMessageText = text;
|
|
513
|
-
newState.stopMessageMaxRepeats = maxRepeats;
|
|
514
|
-
newState.stopMessageSource = 'explicit';
|
|
515
|
-
if (shouldRearm) {
|
|
516
|
-
newState.stopMessageUsed = 0;
|
|
517
|
-
newState.stopMessageUpdatedAt = Date.now();
|
|
518
|
-
newState.stopMessageLastUsedAt = undefined;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
break;
|
|
522
|
-
}
|
|
505
|
+
case 'stopMessageSet':
|
|
506
|
+
case 'stopMessageMode':
|
|
523
507
|
case 'stopMessageClear':
|
|
524
|
-
newState
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
newState
|
|
529
|
-
newState.stopMessageLastUsedAt = undefined;
|
|
508
|
+
applyStopMessageInstructionToState(instruction, newState);
|
|
509
|
+
break;
|
|
510
|
+
case 'preCommandSet':
|
|
511
|
+
case 'preCommandClear':
|
|
512
|
+
applyPreCommandInstructionToState(instruction, newState);
|
|
530
513
|
break;
|
|
531
514
|
}
|
|
532
515
|
}
|
|
@@ -569,24 +552,8 @@ export function serializeRoutingInstructionState(state) {
|
|
|
569
552
|
provider,
|
|
570
553
|
models: Array.from(models)
|
|
571
554
|
})),
|
|
572
|
-
...(
|
|
573
|
-
|
|
574
|
-
: {}),
|
|
575
|
-
...(typeof state.stopMessageText === 'string' && state.stopMessageText.trim()
|
|
576
|
-
? { stopMessageText: state.stopMessageText }
|
|
577
|
-
: {}),
|
|
578
|
-
...(typeof state.stopMessageMaxRepeats === 'number' && Number.isFinite(state.stopMessageMaxRepeats)
|
|
579
|
-
? { stopMessageMaxRepeats: state.stopMessageMaxRepeats }
|
|
580
|
-
: {}),
|
|
581
|
-
...(typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)
|
|
582
|
-
? { stopMessageUsed: state.stopMessageUsed }
|
|
583
|
-
: {}),
|
|
584
|
-
...(typeof state.stopMessageUpdatedAt === 'number' && Number.isFinite(state.stopMessageUpdatedAt)
|
|
585
|
-
? { stopMessageUpdatedAt: state.stopMessageUpdatedAt }
|
|
586
|
-
: {}),
|
|
587
|
-
...(typeof state.stopMessageLastUsedAt === 'number' && Number.isFinite(state.stopMessageLastUsedAt)
|
|
588
|
-
? { stopMessageLastUsedAt: state.stopMessageLastUsedAt }
|
|
589
|
-
: {})
|
|
555
|
+
...serializeStopMessageState(state),
|
|
556
|
+
...serializePreCommandState(state)
|
|
590
557
|
};
|
|
591
558
|
}
|
|
592
559
|
export function deserializeRoutingInstructionState(data) {
|
|
@@ -599,8 +566,16 @@ export function deserializeRoutingInstructionState(data) {
|
|
|
599
566
|
disabledKeys: new Map(),
|
|
600
567
|
disabledModels: new Map(),
|
|
601
568
|
stopMessageText: undefined,
|
|
569
|
+
stopMessageSource: undefined,
|
|
602
570
|
stopMessageMaxRepeats: undefined,
|
|
603
|
-
stopMessageUsed: undefined
|
|
571
|
+
stopMessageUsed: undefined,
|
|
572
|
+
stopMessageStage: undefined,
|
|
573
|
+
stopMessageStageMode: undefined,
|
|
574
|
+
stopMessageObservationHash: undefined,
|
|
575
|
+
stopMessageBdWorkState: undefined,
|
|
576
|
+
preCommandSource: undefined,
|
|
577
|
+
preCommandScriptPath: undefined,
|
|
578
|
+
preCommandUpdatedAt: undefined
|
|
604
579
|
};
|
|
605
580
|
if (data.forcedTarget && typeof data.forcedTarget === 'object') {
|
|
606
581
|
state.forcedTarget = data.forcedTarget;
|
|
@@ -631,23 +606,7 @@ export function deserializeRoutingInstructionState(data) {
|
|
|
631
606
|
}
|
|
632
607
|
}
|
|
633
608
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
if (typeof data.stopMessageText === 'string' && data.stopMessageText.trim()) {
|
|
638
|
-
state.stopMessageText = data.stopMessageText;
|
|
639
|
-
}
|
|
640
|
-
if (typeof data.stopMessageMaxRepeats === 'number' && Number.isFinite(data.stopMessageMaxRepeats)) {
|
|
641
|
-
state.stopMessageMaxRepeats = Math.floor(data.stopMessageMaxRepeats);
|
|
642
|
-
}
|
|
643
|
-
if (typeof data.stopMessageUsed === 'number' && Number.isFinite(data.stopMessageUsed)) {
|
|
644
|
-
state.stopMessageUsed = Math.max(0, Math.floor(data.stopMessageUsed));
|
|
645
|
-
}
|
|
646
|
-
if (typeof data.stopMessageUpdatedAt === 'number' && Number.isFinite(data.stopMessageUpdatedAt)) {
|
|
647
|
-
state.stopMessageUpdatedAt = data.stopMessageUpdatedAt;
|
|
648
|
-
}
|
|
649
|
-
if (typeof data.stopMessageLastUsedAt === 'number' && Number.isFinite(data.stopMessageLastUsedAt)) {
|
|
650
|
-
state.stopMessageLastUsedAt = data.stopMessageLastUsedAt;
|
|
651
|
-
}
|
|
609
|
+
deserializeStopMessageState(data, state);
|
|
610
|
+
deserializePreCommandState(data, state);
|
|
652
611
|
return state;
|
|
653
612
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RoutingInstruction, RoutingInstructionState } from './routing-instructions.js';
|
|
2
|
+
export declare function applyPreCommandInstructionToState(instruction: RoutingInstruction, state: RoutingInstructionState): boolean;
|
|
3
|
+
export declare function clearPreCommandState(state: RoutingInstructionState): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function applyPreCommandInstructionToState(instruction, state) {
|
|
2
|
+
switch (instruction.type) {
|
|
3
|
+
case 'preCommandSet': {
|
|
4
|
+
const scriptPath = typeof instruction.preCommandScriptPath === 'string' && instruction.preCommandScriptPath.trim()
|
|
5
|
+
? instruction.preCommandScriptPath.trim()
|
|
6
|
+
: '';
|
|
7
|
+
if (!scriptPath) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
state.preCommandScriptPath = scriptPath;
|
|
11
|
+
state.preCommandSource = 'explicit';
|
|
12
|
+
state.preCommandUpdatedAt = Date.now();
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
case 'preCommandClear':
|
|
16
|
+
clearPreCommandState(state);
|
|
17
|
+
return true;
|
|
18
|
+
default:
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function clearPreCommandState(state) {
|
|
23
|
+
state.preCommandScriptPath = undefined;
|
|
24
|
+
state.preCommandSource = undefined;
|
|
25
|
+
state.preCommandUpdatedAt = undefined;
|
|
26
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { resolvePreCommandScriptPath } from './pre-command-file-resolver.js';
|
|
2
|
+
const DEFAULT_PRECOMMAND_SCRIPT = 'default.sh';
|
|
3
|
+
export function parsePreCommandInstruction(instruction) {
|
|
4
|
+
const trimmed = typeof instruction === 'string' ? instruction.trim() : '';
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
if (/^precommand$/i.test(trimmed)) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'preCommandSet',
|
|
11
|
+
preCommandScriptPath: resolvePreCommandScriptPath(resolveDefaultScriptRef())
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (!/^precommand\s*:/i.test(trimmed)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const body = trimmed.slice('precommand'.length + 1).trim();
|
|
18
|
+
if (!body) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const parsedValue = readPreCommandToken(body);
|
|
22
|
+
if (!parsedValue) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const normalized = parsedValue.trim();
|
|
26
|
+
if (!normalized) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (/^(?:clear|off|none)$/i.test(normalized)) {
|
|
30
|
+
return { type: 'preCommandClear' };
|
|
31
|
+
}
|
|
32
|
+
if (/^on$/i.test(normalized)) {
|
|
33
|
+
return {
|
|
34
|
+
type: 'preCommandSet',
|
|
35
|
+
preCommandScriptPath: resolvePreCommandScriptPath(resolveDefaultScriptRef())
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
type: 'preCommandSet',
|
|
40
|
+
preCommandScriptPath: resolvePreCommandScriptPath(normalized)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function resolveDefaultScriptRef() {
|
|
44
|
+
const configured = process.env.ROUTECODEX_PRECOMMAND_DEFAULT_SCRIPT;
|
|
45
|
+
if (typeof configured === 'string' && configured.trim()) {
|
|
46
|
+
return configured.trim();
|
|
47
|
+
}
|
|
48
|
+
return DEFAULT_PRECOMMAND_SCRIPT;
|
|
49
|
+
}
|
|
50
|
+
function readPreCommandToken(body) {
|
|
51
|
+
if (!body) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const first = body[0];
|
|
55
|
+
if (first === '"' || first === "'") {
|
|
56
|
+
const end = findClosingQuote(body, first);
|
|
57
|
+
if (end <= 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return body.slice(1, end).replace(/\\"/g, '"').replace(/\\'/g, "'");
|
|
61
|
+
}
|
|
62
|
+
const comma = body.indexOf(',');
|
|
63
|
+
if (comma >= 0) {
|
|
64
|
+
return body.slice(0, comma).trim();
|
|
65
|
+
}
|
|
66
|
+
return body.trim();
|
|
67
|
+
}
|
|
68
|
+
function findClosingQuote(text, quote) {
|
|
69
|
+
let escaped = false;
|
|
70
|
+
for (let idx = 1; idx < text.length; idx += 1) {
|
|
71
|
+
const ch = text[idx];
|
|
72
|
+
if (escaped) {
|
|
73
|
+
escaped = false;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (ch === '\\') {
|
|
77
|
+
escaped = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (ch === quote) {
|
|
81
|
+
return idx;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return -1;
|
|
85
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RoutingInstructionState } from './routing-instructions.js';
|
|
2
|
+
export declare function serializePreCommandState(state: RoutingInstructionState): Record<string, unknown>;
|
|
3
|
+
export declare function deserializePreCommandState(data: Record<string, unknown>, state: RoutingInstructionState): void;
|