@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.
- package/dist/conversion/compat/actions/deepseek-web-request.js +16 -2
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +7 -1
- package/dist/conversion/compat/actions/deepseek-web-response.js +302 -40
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +5 -0
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +7 -4
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +1 -0
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +12 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
- package/dist/conversion/compat/actions/tool-text-request-guidance.d.ts +9 -0
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +177 -0
- package/dist/conversion/compat/antigravity-session-signature.d.ts +6 -0
- package/dist/conversion/compat/antigravity-session-signature.js +15 -0
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +52 -1
- package/dist/conversion/compat/profiles/chat-glm.json +22 -0
- package/dist/conversion/compat/profiles/chat-iflow.json +4 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +13 -27
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +10 -1
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +13 -4
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -53
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +8 -4
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +191 -9
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +118 -15
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +65 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.d.ts +34 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.js +75 -0
- package/dist/conversion/hub/process/chat-process.js +85 -18
- package/dist/conversion/hub/response/provider-response.js +21 -50
- package/dist/conversion/hub/response/response-runtime.js +71 -10
- package/dist/conversion/responses/responses-openai-bridge/response-payload.d.ts +3 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +576 -0
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +42 -0
- package/dist/conversion/responses/responses-openai-bridge/types.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -44
- package/dist/conversion/responses/responses-openai-bridge.js +193 -504
- package/dist/conversion/shared/anthropic-message-utils.js +82 -2
- package/dist/conversion/shared/bridge-message-utils.js +92 -39
- package/dist/conversion/shared/snapshot-hooks.js +8 -13
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +129 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +4 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +637 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +21 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +177 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +385 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +10 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +602 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.js +4 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.js +76 -0
- package/dist/conversion/shared/text-markup-normalizer.d.ts +3 -25
- package/dist/conversion/shared/text-markup-normalizer.js +2 -1386
- package/dist/conversion/shared/tool-governor.js +136 -10
- package/dist/filters/utils/snapshot-writer.js +3 -3
- package/dist/router/virtual-router/bootstrap/auth-utils.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap/auth-utils.js +288 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.d.ts +11 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.js +18 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.js +13 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.js +106 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +7 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +68 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +40 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +212 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.d.ts +15 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.js +65 -0
- package/dist/router/virtual-router/bootstrap/routing-config.d.ts +23 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +293 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +12 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.js +128 -0
- package/dist/router/virtual-router/bootstrap/utils.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/utils.js +41 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.js +131 -0
- package/dist/router/virtual-router/bootstrap.d.ts +0 -4
- package/dist/router/virtual-router/bootstrap.js +31 -1275
- package/dist/router/virtual-router/classifier.js +32 -14
- package/dist/router/virtual-router/engine/antigravity/alias-lease.js +2 -2
- package/dist/router/virtual-router/engine/cooldown-manager.d.ts +34 -0
- package/dist/router/virtual-router/engine/cooldown-manager.js +118 -0
- package/dist/router/virtual-router/engine/route-analytics.d.ts +28 -0
- package/dist/router/virtual-router/engine/route-analytics.js +44 -0
- package/dist/router/virtual-router/engine/routing-pools/index.js +165 -4
- package/dist/router/virtual-router/engine/sticky-session-manager.d.ts +29 -0
- package/dist/router/virtual-router/engine/sticky-session-manager.js +55 -0
- package/dist/router/virtual-router/engine-logging.d.ts +42 -1
- package/dist/router/virtual-router/engine-logging.js +82 -15
- package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +31 -1
- package/dist/router/virtual-router/engine.d.ts +21 -7
- package/dist/router/virtual-router/engine.js +198 -194
- package/dist/router/virtual-router/features.js +12 -4
- package/dist/router/virtual-router/message-utils.d.ts +8 -0
- package/dist/router/virtual-router/message-utils.js +170 -45
- package/dist/router/virtual-router/pre-command-file-resolver.js +40 -2
- package/dist/router/virtual-router/routing-instructions.d.ts +8 -0
- package/dist/router/virtual-router/routing-instructions.js +18 -2
- package/dist/router/virtual-router/routing-stop-message-actions.js +34 -10
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +50 -1
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +3 -0
- package/dist/router/virtual-router/token-counter.js +51 -10
- package/dist/router/virtual-router/tool-signals.js +4 -0
- package/dist/router/virtual-router/types.d.ts +15 -0
- package/dist/servertool/clock/session-scope.d.ts +3 -0
- package/dist/servertool/clock/session-scope.js +52 -0
- package/dist/servertool/clock/state.js +9 -0
- package/dist/servertool/clock/tasks.js +12 -1
- package/dist/servertool/clock/types.d.ts +3 -0
- package/dist/servertool/engine.js +177 -31
- package/dist/servertool/handlers/clock-auto.js +2 -8
- package/dist/servertool/handlers/clock.js +6 -9
- package/dist/servertool/handlers/recursive-detection-guard.js +53 -14
- package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
- package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
- package/dist/servertool/handlers/stop-message-auto.d.ts +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +80 -556
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.d.ts +18 -0
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.js +398 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.js +127 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.d.ts +2 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.js +179 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.d.ts +4 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.js +96 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.js +89 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.d.ts +59 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.js +1 -0
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +3 -43
- package/dist/servertool/handlers/stop-message-stage-policy.js +2 -684
- package/dist/servertool/handlers/web-search.js +117 -0
- package/dist/servertool/server-side-tools.d.ts +0 -1
- package/dist/servertool/server-side-tools.js +4 -3
- package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +110 -37
- package/dist/telemetry/stats-center.d.ts +9 -0
- package/dist/telemetry/stats-center.js +29 -1
- package/dist/tools/apply-patch/structured/coercion.js +3 -11
- package/dist/tools/exec-command/validator.d.ts +1 -0
- package/dist/tools/exec-command/validator.js +132 -0
- package/dist/tools/tool-registry.d.ts +1 -0
- package/dist/tools/tool-registry.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Shared types/constants/helpers for text markup tool-call harvest.
|
|
2
|
+
// Strict allowlist for tool names and their argument keys to avoid picking up
|
|
3
|
+
// stray markup or free-form text as JSON keys (reduces false positives).
|
|
4
|
+
export const KNOWN_TOOLS = new Set([
|
|
5
|
+
'shell',
|
|
6
|
+
'exec_command',
|
|
7
|
+
'write_stdin',
|
|
8
|
+
'apply_patch',
|
|
9
|
+
'update_plan',
|
|
10
|
+
'view_image',
|
|
11
|
+
'list_mcp_resources',
|
|
12
|
+
'read_mcp_resource',
|
|
13
|
+
'list_mcp_resource_templates',
|
|
14
|
+
// 文件/目录类工具(CLI 侧已有约定;此处只做文本→tool_calls 收割)
|
|
15
|
+
'list_directory'
|
|
16
|
+
]);
|
|
17
|
+
const ALLOWED_KEYS = {
|
|
18
|
+
shell: new Set(['command', 'justification', 'timeout_ms', 'with_escalated_permissions', 'workdir']),
|
|
19
|
+
exec_command: new Set([
|
|
20
|
+
'cmd',
|
|
21
|
+
'command',
|
|
22
|
+
'workdir',
|
|
23
|
+
'justification',
|
|
24
|
+
'login',
|
|
25
|
+
'tty',
|
|
26
|
+
'shell',
|
|
27
|
+
'yield_time_ms',
|
|
28
|
+
'max_output_tokens',
|
|
29
|
+
'sandbox_permissions',
|
|
30
|
+
'timeout_ms'
|
|
31
|
+
]),
|
|
32
|
+
write_stdin: new Set(['session_id', 'chars', 'text', 'yield_time_ms', 'max_output_tokens']),
|
|
33
|
+
// apply_patch supports both freeform patch text (patch/input/instructions/text) and structured payloads (file/changes)
|
|
34
|
+
apply_patch: new Set(['patch', 'input', 'instructions', 'text', 'file', 'changes']),
|
|
35
|
+
update_plan: new Set(['explanation', 'plan']),
|
|
36
|
+
view_image: new Set(['path']),
|
|
37
|
+
list_mcp_resources: new Set(['server', 'cursor', 'filter', 'root']),
|
|
38
|
+
read_mcp_resource: new Set(['server', 'uri', 'cursor']),
|
|
39
|
+
list_mcp_resource_templates: new Set(['server', 'cursor']),
|
|
40
|
+
list_directory: new Set(['path', 'recursive'])
|
|
41
|
+
};
|
|
42
|
+
export function genToolCallId() {
|
|
43
|
+
return `call_${Math.random().toString(36).slice(2, 10)}`;
|
|
44
|
+
}
|
|
45
|
+
export function normalizeKey(raw) {
|
|
46
|
+
try {
|
|
47
|
+
const t = String(raw || '').trim();
|
|
48
|
+
if (!t)
|
|
49
|
+
return '';
|
|
50
|
+
// Pick the last alphanumeric/underscore token (e.g., '空的\n<tool_call>command' -> 'command')
|
|
51
|
+
const m = t.match(/([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
52
|
+
return m ? m[1] : t;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return String(raw || '');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function readDefaultWorkdirFromEnv() {
|
|
59
|
+
try {
|
|
60
|
+
const env = String(process?.env?.ROUTECODEX_WORKDIR || '').trim() ||
|
|
61
|
+
String(process?.env?.RCC_WORKDIR || '').trim() ||
|
|
62
|
+
String(process?.env?.CLAUDE_WORKDIR || '').trim();
|
|
63
|
+
return env ? env : undefined;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function filterArgsForTool(name, args) {
|
|
70
|
+
try {
|
|
71
|
+
const nm = String(name || '').toLowerCase();
|
|
72
|
+
const allow = ALLOWED_KEYS[nm];
|
|
73
|
+
if (!allow) {
|
|
74
|
+
// Unknown tool; be conservative: keep only simple safe keys if present
|
|
75
|
+
const safe = new Set([
|
|
76
|
+
'cmd',
|
|
77
|
+
'command',
|
|
78
|
+
'workdir',
|
|
79
|
+
'patch',
|
|
80
|
+
'input',
|
|
81
|
+
'instructions',
|
|
82
|
+
'text',
|
|
83
|
+
'plan',
|
|
84
|
+
'explanation',
|
|
85
|
+
'path',
|
|
86
|
+
'server',
|
|
87
|
+
'uri',
|
|
88
|
+
'cursor',
|
|
89
|
+
'filter',
|
|
90
|
+
'root'
|
|
91
|
+
]);
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const [k, v] of Object.entries(args || {})) {
|
|
94
|
+
const key = normalizeKey(k);
|
|
95
|
+
if (safe.has(key))
|
|
96
|
+
out[key] = v;
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
const out = {};
|
|
101
|
+
for (const [k, v] of Object.entries(args || {})) {
|
|
102
|
+
const key = normalizeKey(k);
|
|
103
|
+
if (allow.has(key))
|
|
104
|
+
out[key] = v;
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return args;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function tryParseJsonValue(text) {
|
|
113
|
+
try {
|
|
114
|
+
const trimmed = String(text || '').trim();
|
|
115
|
+
if (!trimmed)
|
|
116
|
+
return null;
|
|
117
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('[') || trimmed.startsWith('"'))) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return JSON.parse(trimmed);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export function tryParsePrimitiveValue(text) {
|
|
127
|
+
try {
|
|
128
|
+
const trimmed = String(text ?? '').trim();
|
|
129
|
+
if (trimmed.length === 0)
|
|
130
|
+
return '';
|
|
131
|
+
const parsed = tryParseJsonValue(trimmed);
|
|
132
|
+
if (parsed !== null)
|
|
133
|
+
return parsed;
|
|
134
|
+
if (trimmed === 'true' || trimmed === 'false')
|
|
135
|
+
return trimmed === 'true';
|
|
136
|
+
if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
137
|
+
const n = Number(trimmed);
|
|
138
|
+
if (Number.isFinite(n))
|
|
139
|
+
return n;
|
|
140
|
+
}
|
|
141
|
+
return trimmed;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return String(text ?? '');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export function coerceCommandValueToString(value) {
|
|
148
|
+
try {
|
|
149
|
+
if (value === null || value === undefined)
|
|
150
|
+
return '';
|
|
151
|
+
if (typeof value === 'string')
|
|
152
|
+
return value.trim();
|
|
153
|
+
if (Array.isArray(value)) {
|
|
154
|
+
return value.map((v) => String(v)).join(' ').trim();
|
|
155
|
+
}
|
|
156
|
+
if (typeof value === 'object') {
|
|
157
|
+
const rec = value;
|
|
158
|
+
const direct = typeof rec.cmd === 'string'
|
|
159
|
+
? String(rec.cmd)
|
|
160
|
+
: typeof rec.command === 'string'
|
|
161
|
+
? String(rec.command)
|
|
162
|
+
: '';
|
|
163
|
+
if (direct.trim().length)
|
|
164
|
+
return direct.trim();
|
|
165
|
+
try {
|
|
166
|
+
return JSON.stringify(value);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return String(value);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return String(value).trim();
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return '';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ToolCallLite } from './extractors-shared.js';
|
|
2
|
+
export declare function extractBareExecCommandFromText(text: string): ToolCallLite[] | null;
|
|
3
|
+
export declare function extractExecuteBlocksFromText(text: string): ToolCallLite[] | null;
|
|
4
|
+
export declare function extractExploredListDirectoryCallsFromText(text: string): ToolCallLite[] | null;
|
|
5
|
+
export declare function extractQwenToolCallTokensFromText(text: string): ToolCallLite[] | null;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { filterArgsForTool, genToolCallId, readDefaultWorkdirFromEnv } from './extractors-shared.js';
|
|
2
|
+
import { salvageToolArgsFromRawText, tryParseJsonWithModelRepairs } from './extractors-json.js';
|
|
3
|
+
function tryParseJsonArrayOnLine(line) {
|
|
4
|
+
try {
|
|
5
|
+
const trimmed = String(line || '').trim();
|
|
6
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']'))
|
|
7
|
+
return null;
|
|
8
|
+
const parsed = JSON.parse(trimmed);
|
|
9
|
+
if (!Array.isArray(parsed))
|
|
10
|
+
return null;
|
|
11
|
+
const out = [];
|
|
12
|
+
for (const entry of parsed) {
|
|
13
|
+
if (entry == null)
|
|
14
|
+
continue;
|
|
15
|
+
out.push(String(entry));
|
|
16
|
+
}
|
|
17
|
+
return out.length ? out : null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function stripTrailingXmlGarbage(line) {
|
|
24
|
+
try {
|
|
25
|
+
const idx = line.indexOf('<');
|
|
26
|
+
if (idx >= 0) {
|
|
27
|
+
return line.slice(0, idx).trim();
|
|
28
|
+
}
|
|
29
|
+
return line.trim();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return String(line || '').trim();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function normalizePossibleCommandLine(raw) {
|
|
36
|
+
try {
|
|
37
|
+
let line = String(raw || '');
|
|
38
|
+
line = line.replace(/^\s*(?:[•*+-]\s*)?/, '');
|
|
39
|
+
line = line.replace(/^\s*\$\s*/, '');
|
|
40
|
+
return stripTrailingXmlGarbage(line);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function looksLikeShellCommand(line) {
|
|
47
|
+
try {
|
|
48
|
+
const t = String(line || '').trim();
|
|
49
|
+
if (!t)
|
|
50
|
+
return false;
|
|
51
|
+
if (t.startsWith('```') || t.startsWith('>'))
|
|
52
|
+
return false;
|
|
53
|
+
if (t.startsWith('zsh:') || t.startsWith('bash:'))
|
|
54
|
+
return false;
|
|
55
|
+
// Conservative allowlist: commands commonly emitted by Codex/Claude in tool-mode.
|
|
56
|
+
return /^(?:rg|ls|cat|node|npm|pnpm|yarn|git|find|sed|head|tail|wc|mkdir|rm|cp|mv|pwd)\b/.test(t);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function looksLikeBrokenToolMarkup(text) {
|
|
63
|
+
try {
|
|
64
|
+
const t = String(text || '');
|
|
65
|
+
if (!t)
|
|
66
|
+
return false;
|
|
67
|
+
const ranCommandMatch = t.match(/^\s*(?:[•*+-]\s*)?Ran\s+(.+)$/im);
|
|
68
|
+
const hasRanShellCommand = !!ranCommandMatch &&
|
|
69
|
+
(() => {
|
|
70
|
+
const payload = String(ranCommandMatch[1] || '').trim();
|
|
71
|
+
if (!payload)
|
|
72
|
+
return false;
|
|
73
|
+
const arr = tryParseJsonArrayOnLine(payload);
|
|
74
|
+
if (arr && arr.length) {
|
|
75
|
+
return looksLikeShellCommand(arr.join(' '));
|
|
76
|
+
}
|
|
77
|
+
return looksLikeShellCommand(payload);
|
|
78
|
+
})();
|
|
79
|
+
return (t.includes('<exec_command') ||
|
|
80
|
+
t.includes('<invoke') ||
|
|
81
|
+
t.includes('<function_calls') ||
|
|
82
|
+
t.includes('<parameter') ||
|
|
83
|
+
t.includes('<tool_call') ||
|
|
84
|
+
t.includes('</tool_call') ||
|
|
85
|
+
t.includes('<arg_value') ||
|
|
86
|
+
t.includes('</arg_value') ||
|
|
87
|
+
t.includes('</func_call') ||
|
|
88
|
+
/\bRan\b\s+\[/.test(t) ||
|
|
89
|
+
hasRanShellCommand);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function extractBareExecCommandFromText(text) {
|
|
96
|
+
try {
|
|
97
|
+
if (typeof text !== 'string' || !text)
|
|
98
|
+
return null;
|
|
99
|
+
// Only attempt this when we see clear evidence of a broken tool markup (avoid turning examples into tool calls).
|
|
100
|
+
if (!looksLikeBrokenToolMarkup(text))
|
|
101
|
+
return null;
|
|
102
|
+
const candidates = [];
|
|
103
|
+
const lines = text.split(/\r?\n/);
|
|
104
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
105
|
+
const raw = String(lines[i] || '');
|
|
106
|
+
const line = normalizePossibleCommandLine(raw);
|
|
107
|
+
if (!line)
|
|
108
|
+
continue;
|
|
109
|
+
// Pattern A: "Ran [\"ls\", \"-l\", ...]" or "Ran git status"
|
|
110
|
+
const ran = line.match(/^Ran\s+(.+)$/i);
|
|
111
|
+
if (ran && ran[1]) {
|
|
112
|
+
const payload = String(ran[1] || '').trim();
|
|
113
|
+
const arr = tryParseJsonArrayOnLine(payload);
|
|
114
|
+
if (arr && arr.length) {
|
|
115
|
+
const cmd = arr.join(' ').trim();
|
|
116
|
+
if (looksLikeShellCommand(cmd)) {
|
|
117
|
+
candidates.push({ cmd });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (looksLikeShellCommand(payload)) {
|
|
122
|
+
candidates.push({ cmd: payload });
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Pattern B: a single-line shell command (rg/ls/node/...)
|
|
127
|
+
if (looksLikeShellCommand(line)) {
|
|
128
|
+
candidates.push({ cmd: line.trim() });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (!candidates.length)
|
|
133
|
+
return null;
|
|
134
|
+
const picked = candidates[candidates.length - 1];
|
|
135
|
+
const argsObj = { cmd: picked.cmd };
|
|
136
|
+
const envWorkdir = readDefaultWorkdirFromEnv();
|
|
137
|
+
if (envWorkdir) {
|
|
138
|
+
argsObj.workdir = envWorkdir;
|
|
139
|
+
}
|
|
140
|
+
const filtered = filterArgsForTool('exec_command', argsObj);
|
|
141
|
+
let argsStr = '{}';
|
|
142
|
+
try {
|
|
143
|
+
argsStr = JSON.stringify(filtered);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
argsStr = '{}';
|
|
147
|
+
}
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
id: genToolCallId(),
|
|
151
|
+
name: 'exec_command',
|
|
152
|
+
args: argsStr
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function extractExecuteBlocksFromText(text) {
|
|
161
|
+
try {
|
|
162
|
+
if (typeof text !== 'string' || !text)
|
|
163
|
+
return null;
|
|
164
|
+
const re = /<function=execute>\s*<parameter=command>([\s\S]*?)<\/parameter>\s*<\/function>/gi;
|
|
165
|
+
const out = [];
|
|
166
|
+
let m;
|
|
167
|
+
while ((m = re.exec(text)) !== null) {
|
|
168
|
+
const commandRaw = (m[1] || '').trim();
|
|
169
|
+
if (!commandRaw)
|
|
170
|
+
continue;
|
|
171
|
+
let args = '{}';
|
|
172
|
+
try {
|
|
173
|
+
args = JSON.stringify({ command: commandRaw });
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
args = '{"command":""}';
|
|
177
|
+
}
|
|
178
|
+
out.push({ id: genToolCallId(), name: 'shell', args });
|
|
179
|
+
}
|
|
180
|
+
return out.length ? out : null;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function normalizeExploredListPath(raw) {
|
|
187
|
+
try {
|
|
188
|
+
let path = String(raw || '').trim();
|
|
189
|
+
// Drop common tree/marker prefixes from terminal transcript lines.
|
|
190
|
+
path = path.replace(/^[\s│]+/, '');
|
|
191
|
+
path = path.replace(/^[└├─]+\s*/, '');
|
|
192
|
+
// Strip wrapping quotes/backticks.
|
|
193
|
+
path = path.replace(/^([`"'])([\s\S]*?)\1$/, '$2').trim();
|
|
194
|
+
if (!path)
|
|
195
|
+
return '';
|
|
196
|
+
// Keep only first line to avoid swallowing multiline status output.
|
|
197
|
+
const firstLine = path.split(/\r?\n/)[0] || '';
|
|
198
|
+
path = firstLine.trim();
|
|
199
|
+
if (!path)
|
|
200
|
+
return '';
|
|
201
|
+
// Guard obvious non-path prose fragments.
|
|
202
|
+
if (/^(?:and|with|for|to)\b/i.test(path))
|
|
203
|
+
return '';
|
|
204
|
+
if (/[<>|;&]/.test(path))
|
|
205
|
+
return '';
|
|
206
|
+
return path;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return '';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Harvest Codex-style transcript snippets:
|
|
213
|
+
// • Explored
|
|
214
|
+
// └ List xiaohongshu
|
|
215
|
+
// List app
|
|
216
|
+
// into list_directory tool calls.
|
|
217
|
+
export function extractExploredListDirectoryCallsFromText(text) {
|
|
218
|
+
try {
|
|
219
|
+
if (typeof text !== 'string' || !text)
|
|
220
|
+
return null;
|
|
221
|
+
const hasExploredMarker = /(?:^|\n)\s*(?:[•*+-]\s*)?Explored\b/i.test(text);
|
|
222
|
+
const hasTreeListMarker = /(?:^|\n)\s*[│\s]*[└├]\s*List\s+/im.test(text);
|
|
223
|
+
if (!hasExploredMarker && !hasTreeListMarker) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const out = [];
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
const lines = text.split(/\r?\n/);
|
|
229
|
+
for (const rawLine of lines) {
|
|
230
|
+
let line = String(rawLine || '').trim();
|
|
231
|
+
if (!line)
|
|
232
|
+
continue;
|
|
233
|
+
// Remove transcript bullets/tree prefixes.
|
|
234
|
+
line = line.replace(/^[•*+-]\s*/, '');
|
|
235
|
+
line = line.replace(/^[\s│]+/, '');
|
|
236
|
+
line = line.replace(/^[└├─]+\s*/, '');
|
|
237
|
+
const listMatch = /^List\s+([\s\S]+)$/i.exec(line);
|
|
238
|
+
if (!listMatch?.[1]) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const path = normalizeExploredListPath(listMatch[1]);
|
|
242
|
+
if (!path) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const dedupeKey = path.toLowerCase();
|
|
246
|
+
if (seen.has(dedupeKey)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
seen.add(dedupeKey);
|
|
250
|
+
const filtered = filterArgsForTool('list_directory', { path, recursive: false });
|
|
251
|
+
let argsStr = '{}';
|
|
252
|
+
try {
|
|
253
|
+
argsStr = JSON.stringify(filtered);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
argsStr = '{}';
|
|
257
|
+
}
|
|
258
|
+
out.push({
|
|
259
|
+
id: genToolCallId(),
|
|
260
|
+
name: 'list_directory',
|
|
261
|
+
args: argsStr
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return out.length ? out : null;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Extract Qwen-style tool call tokens that appear in plain text.
|
|
271
|
+
export function extractQwenToolCallTokensFromText(text) {
|
|
272
|
+
try {
|
|
273
|
+
if (typeof text !== 'string' || !text)
|
|
274
|
+
return null;
|
|
275
|
+
// Upstreams (notably Qwen-family models behind OpenAI-compatible shells) may insert
|
|
276
|
+
// whitespace/newlines inside token markers, e.g. "<| tool_call_begin|>".
|
|
277
|
+
if (!/<\|\s*tool_call_begin\s*\|>/i.test(text) && !/<\|\s*tool_call_argument_begin\s*\|>/i.test(text)) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const out = [];
|
|
281
|
+
// tolerate whitespace/newlines and missing argument_end token
|
|
282
|
+
const re = /<\|\s*tool_call_begin\s*\|>\s*([A-Za-z0-9_.-]+)(?::(\d+))?\s*(?:<\|\s*tool_call_argument_begin\s*\|>\s*([\s\S]*?)\s*)?<\|\s*tool_call_end\s*\|>/gi;
|
|
283
|
+
let m;
|
|
284
|
+
while ((m = re.exec(text)) !== null) {
|
|
285
|
+
const rawName = String(m[1] || '').trim();
|
|
286
|
+
const numericId = typeof m[2] === 'string' && m[2].trim().length ? m[2].trim() : undefined;
|
|
287
|
+
const rawArgs = typeof m[3] === 'string' ? String(m[3]).trim() : '';
|
|
288
|
+
if (!rawName)
|
|
289
|
+
continue;
|
|
290
|
+
let name = rawName;
|
|
291
|
+
if (name.startsWith('functions.')) {
|
|
292
|
+
name = name.slice('functions.'.length);
|
|
293
|
+
}
|
|
294
|
+
const lname = name.toLowerCase();
|
|
295
|
+
if (!lname)
|
|
296
|
+
continue;
|
|
297
|
+
let parsedArgs = null;
|
|
298
|
+
if (!rawArgs) {
|
|
299
|
+
parsedArgs = {};
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
parsedArgs = tryParseJsonWithModelRepairs(rawArgs);
|
|
303
|
+
if (parsedArgs === null) {
|
|
304
|
+
parsedArgs = salvageToolArgsFromRawText(lname, rawArgs) ?? { _raw: rawArgs };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const argsObj = parsedArgs && typeof parsedArgs === 'object' && !Array.isArray(parsedArgs)
|
|
308
|
+
? parsedArgs
|
|
309
|
+
: { value: parsedArgs };
|
|
310
|
+
// Alias normalization (models often use "command" instead of "cmd", etc.)
|
|
311
|
+
if (lname === 'exec_command') {
|
|
312
|
+
const cmd = typeof argsObj.cmd === 'string' ? String(argsObj.cmd) : '';
|
|
313
|
+
const command = typeof argsObj.command === 'string' ? String(argsObj.command) : '';
|
|
314
|
+
if (!cmd && command) {
|
|
315
|
+
argsObj.cmd = command;
|
|
316
|
+
}
|
|
317
|
+
const cwd = typeof argsObj.cwd === 'string' ? String(argsObj.cwd) : '';
|
|
318
|
+
const workdir = typeof argsObj.workdir === 'string' ? String(argsObj.workdir) : '';
|
|
319
|
+
if (!workdir && cwd) {
|
|
320
|
+
argsObj.workdir = cwd;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (lname === 'write_stdin') {
|
|
324
|
+
const chars = typeof argsObj.chars === 'string' ? String(argsObj.chars) : '';
|
|
325
|
+
const textVal = typeof argsObj.text === 'string' ? String(argsObj.text) : '';
|
|
326
|
+
const input = typeof argsObj.input === 'string' ? String(argsObj.input) : '';
|
|
327
|
+
if (!chars && (textVal || input)) {
|
|
328
|
+
argsObj.chars = textVal || input;
|
|
329
|
+
}
|
|
330
|
+
const sid = argsObj.session_id;
|
|
331
|
+
const sidAlt = argsObj.sessionId;
|
|
332
|
+
if ((sid === undefined || sid === null || String(sid).trim() === '') && sidAlt !== undefined && sidAlt !== null) {
|
|
333
|
+
argsObj.session_id = sidAlt;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (lname === 'apply_patch') {
|
|
337
|
+
const patch = typeof argsObj.patch === 'string' ? String(argsObj.patch) : '';
|
|
338
|
+
const textPatch = typeof argsObj.text === 'string' ? String(argsObj.text) : '';
|
|
339
|
+
const inputPatch = typeof argsObj.input === 'string' ? String(argsObj.input) : '';
|
|
340
|
+
const instructions = typeof argsObj.instructions === 'string' ? String(argsObj.instructions) : '';
|
|
341
|
+
if (!patch && (textPatch || inputPatch || instructions)) {
|
|
342
|
+
argsObj.patch = textPatch || inputPatch || instructions;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const filtered = filterArgsForTool(lname, argsObj);
|
|
346
|
+
// Basic guards for known tools
|
|
347
|
+
if (lname === 'exec_command') {
|
|
348
|
+
const cmd = typeof filtered?.cmd === 'string' ? String(filtered.cmd).trim() : '';
|
|
349
|
+
if (!cmd)
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (lname === 'write_stdin') {
|
|
353
|
+
// Allow empty args: our servertool schema may inject session_id automatically when a session is active.
|
|
354
|
+
}
|
|
355
|
+
if (lname === 'apply_patch') {
|
|
356
|
+
const patchText = typeof filtered?.patch === 'string' ? String(filtered.patch).trim() : '';
|
|
357
|
+
if (!patchText)
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
let argsStr = '{}';
|
|
361
|
+
try {
|
|
362
|
+
argsStr = JSON.stringify(filtered);
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
argsStr = '{}';
|
|
366
|
+
}
|
|
367
|
+
const stableId = (() => {
|
|
368
|
+
if (numericId && /^[0-9]{1,10}$/.test(numericId)) {
|
|
369
|
+
const namePart = lname.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
370
|
+
const id = `call_${namePart}_${numericId}`
|
|
371
|
+
.replace(/_{2,}/g, '_')
|
|
372
|
+
.replace(/^_+/, '')
|
|
373
|
+
.replace(/_+$/, '');
|
|
374
|
+
return id || undefined;
|
|
375
|
+
}
|
|
376
|
+
return undefined;
|
|
377
|
+
})();
|
|
378
|
+
out.push({ id: stableId ?? genToolCallId(), name: lname, args: argsStr });
|
|
379
|
+
}
|
|
380
|
+
return out.length ? out : null;
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ToolCallLite } from './extractors-shared.js';
|
|
2
|
+
export declare function extractToolNamespaceXmlBlocksFromText(text: string): ToolCallLite[] | null;
|
|
3
|
+
export declare function extractParameterXmlToolsFromText(text: string): ToolCallLite[] | null;
|
|
4
|
+
export declare function extractInvokeToolsFromText(text: string): ToolCallLite[] | null;
|
|
5
|
+
export declare function extractXMLToolCallsFromText(text: string): ToolCallLite[] | null;
|
|
6
|
+
/**
|
|
7
|
+
* 提取简单 XML 形式的工具调用块,例如:
|
|
8
|
+
* <list_directory><path>/path</path><recursive>false</recursive></list_directory>
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractSimpleXmlToolsFromText(text: string): ToolCallLite[] | null;
|