@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
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
const SERVER_TOOL_HANDLERS = Object.create(null);
|
|
2
2
|
const AUTO_SERVER_TOOL_HANDLERS = [];
|
|
3
|
+
const DEFAULT_AUTO_HOOK_PRIORITY = 100;
|
|
4
|
+
let autoHookRegistrationOrder = 0;
|
|
5
|
+
function normalizeAutoHookPhase(value) {
|
|
6
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
7
|
+
if (normalized === 'pre' || normalized === 'before') {
|
|
8
|
+
return 'pre';
|
|
9
|
+
}
|
|
10
|
+
if (normalized === 'post' || normalized === 'after') {
|
|
11
|
+
return 'post';
|
|
12
|
+
}
|
|
13
|
+
return 'default';
|
|
14
|
+
}
|
|
15
|
+
function normalizeAutoHookPriority(value) {
|
|
16
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
17
|
+
return Math.floor(value);
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === 'string' && value.trim()) {
|
|
20
|
+
const parsed = Number.parseInt(value.trim(), 10);
|
|
21
|
+
if (Number.isFinite(parsed)) {
|
|
22
|
+
return Math.floor(parsed);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return DEFAULT_AUTO_HOOK_PRIORITY;
|
|
26
|
+
}
|
|
27
|
+
function resolveAutoHookPhaseRank(phase) {
|
|
28
|
+
if (phase === 'pre')
|
|
29
|
+
return 0;
|
|
30
|
+
if (phase === 'post')
|
|
31
|
+
return 2;
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
3
34
|
export function registerServerToolHandler(name, handler, options) {
|
|
4
35
|
if (!name || typeof name !== 'string' || typeof handler !== 'function')
|
|
5
36
|
return;
|
|
@@ -9,6 +40,14 @@ export function registerServerToolHandler(name, handler, options) {
|
|
|
9
40
|
const trigger = options?.trigger ?? 'tool_call';
|
|
10
41
|
const entry = { name: key, trigger, handler };
|
|
11
42
|
if (trigger === 'auto') {
|
|
43
|
+
const priority = normalizeAutoHookPriority(options?.hook?.priority ?? options?.priority);
|
|
44
|
+
const phase = normalizeAutoHookPhase(options?.hook?.phase ?? options?.phase);
|
|
45
|
+
entry.autoHook = {
|
|
46
|
+
id: key,
|
|
47
|
+
phase,
|
|
48
|
+
priority,
|
|
49
|
+
order: autoHookRegistrationOrder++
|
|
50
|
+
};
|
|
12
51
|
AUTO_SERVER_TOOL_HANDLERS.push(entry);
|
|
13
52
|
return;
|
|
14
53
|
}
|
|
@@ -23,5 +62,31 @@ export function getServerToolHandler(name) {
|
|
|
23
62
|
return SERVER_TOOL_HANDLERS[key];
|
|
24
63
|
}
|
|
25
64
|
export function listAutoServerToolHandlers() {
|
|
26
|
-
return [...AUTO_SERVER_TOOL_HANDLERS]
|
|
65
|
+
return [...AUTO_SERVER_TOOL_HANDLERS].sort((left, right) => {
|
|
66
|
+
const leftHook = left.autoHook;
|
|
67
|
+
const rightHook = right.autoHook;
|
|
68
|
+
const phaseRankDiff = resolveAutoHookPhaseRank(leftHook?.phase ?? 'default') -
|
|
69
|
+
resolveAutoHookPhaseRank(rightHook?.phase ?? 'default');
|
|
70
|
+
if (phaseRankDiff !== 0) {
|
|
71
|
+
return phaseRankDiff;
|
|
72
|
+
}
|
|
73
|
+
const priorityDiff = (leftHook?.priority ?? DEFAULT_AUTO_HOOK_PRIORITY) - (rightHook?.priority ?? DEFAULT_AUTO_HOOK_PRIORITY);
|
|
74
|
+
if (priorityDiff !== 0) {
|
|
75
|
+
return priorityDiff;
|
|
76
|
+
}
|
|
77
|
+
const orderDiff = (leftHook?.order ?? 0) - (rightHook?.order ?? 0);
|
|
78
|
+
if (orderDiff !== 0) {
|
|
79
|
+
return orderDiff;
|
|
80
|
+
}
|
|
81
|
+
return left.name.localeCompare(right.name);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export function listAutoServerToolHooks() {
|
|
85
|
+
return listAutoServerToolHandlers().map((entry) => ({
|
|
86
|
+
id: entry.name,
|
|
87
|
+
phase: entry.autoHook?.phase ?? 'default',
|
|
88
|
+
priority: entry.autoHook?.priority ?? DEFAULT_AUTO_HOOK_PRIORITY,
|
|
89
|
+
order: entry.autoHook?.order ?? 0,
|
|
90
|
+
handler: entry.handler
|
|
91
|
+
}));
|
|
27
92
|
}
|
|
@@ -9,6 +9,7 @@ import './handlers/clock-auto.js';
|
|
|
9
9
|
import './handlers/exec-command-guard.js';
|
|
10
10
|
import './handlers/apply-patch-guard.js';
|
|
11
11
|
import './handlers/recursive-detection-guard.js';
|
|
12
|
+
import './handlers/continue-execution.js';
|
|
12
13
|
export declare function runServerSideToolEngine(options: ServerSideToolEngineOptions): Promise<ServerSideToolEngineResult>;
|
|
13
14
|
export declare function extractToolCalls(chatResponse: JsonObject): ToolCall[];
|
|
14
15
|
export declare function cloneJson<T>(value: T): T;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getServerToolHandler,
|
|
1
|
+
import { getServerToolHandler, listAutoServerToolHooks } from './registry.js';
|
|
2
2
|
import { ProviderProtocolError } from '../conversion/shared/errors.js';
|
|
3
3
|
import { executeWebSearchBackendPlan } from './handlers/web-search.js';
|
|
4
4
|
import { executeVisionBackendPlan } from './handlers/vision.js';
|
|
@@ -11,6 +11,77 @@ import './handlers/clock-auto.js';
|
|
|
11
11
|
import './handlers/exec-command-guard.js';
|
|
12
12
|
import './handlers/apply-patch-guard.js';
|
|
13
13
|
import './handlers/recursive-detection-guard.js';
|
|
14
|
+
import './handlers/continue-execution.js';
|
|
15
|
+
import { runPreCommandHooks } from './pre-command-hooks.js';
|
|
16
|
+
import { readRuntimeMetadata } from '../conversion/shared/runtime-metadata.js';
|
|
17
|
+
function traceAutoHook(options, event) {
|
|
18
|
+
try {
|
|
19
|
+
options.onAutoHookTrace?.(event);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// best-effort trace callback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const OPTIONAL_PRIMARY_HOOK_ORDER = ['empty_reply_continue', 'stop_message_auto', 'clock_auto'];
|
|
26
|
+
const MANDATORY_HOOK_ORDER = [];
|
|
27
|
+
let fallbackToolCallIdSeq = 0;
|
|
28
|
+
function ensureToolCallId(record) {
|
|
29
|
+
const existing = typeof record.id === 'string' ? String(record.id).trim() : '';
|
|
30
|
+
if (existing) {
|
|
31
|
+
return existing;
|
|
32
|
+
}
|
|
33
|
+
fallbackToolCallIdSeq += 1;
|
|
34
|
+
const generated = `call_servertool_fallback_${Date.now()}_${fallbackToolCallIdSeq}`;
|
|
35
|
+
record.id = generated;
|
|
36
|
+
return generated;
|
|
37
|
+
}
|
|
38
|
+
function buildAutoHookQueues(hooks) {
|
|
39
|
+
const hookById = new Map();
|
|
40
|
+
for (const hook of hooks) {
|
|
41
|
+
if (!hook || typeof hook.id !== 'string') {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
hookById.set(hook.id, hook);
|
|
45
|
+
}
|
|
46
|
+
const consumed = new Set();
|
|
47
|
+
const optionalQueue = [];
|
|
48
|
+
for (const hook of hooks) {
|
|
49
|
+
if (hook.phase !== 'pre') {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (consumed.has(hook.id)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
optionalQueue.push(hook);
|
|
56
|
+
consumed.add(hook.id);
|
|
57
|
+
}
|
|
58
|
+
for (const id of OPTIONAL_PRIMARY_HOOK_ORDER) {
|
|
59
|
+
const hook = hookById.get(id);
|
|
60
|
+
if (!hook || consumed.has(hook.id)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
optionalQueue.push(hook);
|
|
64
|
+
consumed.add(hook.id);
|
|
65
|
+
}
|
|
66
|
+
for (const hook of hooks) {
|
|
67
|
+
if (consumed.has(hook.id)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
optionalQueue.push(hook);
|
|
71
|
+
consumed.add(hook.id);
|
|
72
|
+
}
|
|
73
|
+
const mandatoryQueue = [];
|
|
74
|
+
const mandatorySeen = new Set();
|
|
75
|
+
for (const id of MANDATORY_HOOK_ORDER) {
|
|
76
|
+
const hook = hookById.get(id);
|
|
77
|
+
if (!hook || mandatorySeen.has(hook.id)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
mandatoryQueue.push(hook);
|
|
81
|
+
mandatorySeen.add(hook.id);
|
|
82
|
+
}
|
|
83
|
+
return { optionalQueue, mandatoryQueue };
|
|
84
|
+
}
|
|
14
85
|
function extractToolCallsFromMessage(message) {
|
|
15
86
|
const toolCalls = getArray(message.tool_calls);
|
|
16
87
|
const out = [];
|
|
@@ -18,7 +89,7 @@ function extractToolCallsFromMessage(message) {
|
|
|
18
89
|
const tc = asObject(raw);
|
|
19
90
|
if (!tc)
|
|
20
91
|
continue;
|
|
21
|
-
const id =
|
|
92
|
+
const id = ensureToolCallId(tc);
|
|
22
93
|
const fn = asObject(tc.function) ??
|
|
23
94
|
asObject(tc.functionCall) ??
|
|
24
95
|
asObject(tc.function_call);
|
|
@@ -44,7 +115,7 @@ function extractToolCallsFromMessage(message) {
|
|
|
44
115
|
else if (rawArgs !== undefined && rawArgs !== null) {
|
|
45
116
|
args = String(rawArgs);
|
|
46
117
|
}
|
|
47
|
-
if (!
|
|
118
|
+
if (!name)
|
|
48
119
|
continue;
|
|
49
120
|
out.push({ raw: tc, parsed: { id, name, arguments: args } });
|
|
50
121
|
}
|
|
@@ -104,6 +175,45 @@ function replaceJsonObjectInPlace(target, next) {
|
|
|
104
175
|
// ignore
|
|
105
176
|
}
|
|
106
177
|
}
|
|
178
|
+
function patchToolCallArgumentsById(chatResponse, toolCallId, argumentsText) {
|
|
179
|
+
if (!toolCallId || typeof argumentsText !== 'string') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const choices = getArray(chatResponse.choices);
|
|
183
|
+
for (const choice of choices) {
|
|
184
|
+
const choiceObj = asObject(choice);
|
|
185
|
+
if (!choiceObj)
|
|
186
|
+
continue;
|
|
187
|
+
const message = asObject(choiceObj.message);
|
|
188
|
+
if (!message)
|
|
189
|
+
continue;
|
|
190
|
+
const toolCalls = getArray(message.tool_calls);
|
|
191
|
+
for (const toolCall of toolCalls) {
|
|
192
|
+
const record = asObject(toolCall);
|
|
193
|
+
if (!record)
|
|
194
|
+
continue;
|
|
195
|
+
const id = typeof record.id === 'string' ? String(record.id).trim() : '';
|
|
196
|
+
if (!id || id !== toolCallId) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const fn = asObject(record.function);
|
|
200
|
+
if (fn) {
|
|
201
|
+
fn.arguments = argumentsText;
|
|
202
|
+
}
|
|
203
|
+
const fnCamel = asObject(record.functionCall);
|
|
204
|
+
if (fnCamel) {
|
|
205
|
+
fnCamel.arguments = argumentsText;
|
|
206
|
+
}
|
|
207
|
+
const fnSnake = asObject(record.function_call);
|
|
208
|
+
if (fnSnake) {
|
|
209
|
+
fnSnake.arguments = argumentsText;
|
|
210
|
+
}
|
|
211
|
+
if (Object.prototype.hasOwnProperty.call(record, 'arguments')) {
|
|
212
|
+
record.arguments = argumentsText;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
107
217
|
function filterOutExecutedToolCalls(chatResponse, executedIds) {
|
|
108
218
|
const choices = getArray(chatResponse.choices);
|
|
109
219
|
for (const choice of choices) {
|
|
@@ -157,6 +267,10 @@ export async function runServerSideToolEngine(options) {
|
|
|
157
267
|
const executedFlowIds = [];
|
|
158
268
|
let lastExecution;
|
|
159
269
|
const attemptedToolCallsByMessage = [];
|
|
270
|
+
const runtimeMetadata = readRuntimeMetadata(options.adapterContext);
|
|
271
|
+
const runtimePreCommandState = runtimeMetadata && typeof runtimeMetadata === 'object'
|
|
272
|
+
? runtimeMetadata.preCommandState
|
|
273
|
+
: undefined;
|
|
160
274
|
const choices = getArray(base.choices);
|
|
161
275
|
for (const choice of choices) {
|
|
162
276
|
const choiceObj = asObject(choice);
|
|
@@ -168,6 +282,23 @@ export async function runServerSideToolEngine(options) {
|
|
|
168
282
|
attemptedToolCallsByMessage.push(...extractToolCallsFromMessage(message));
|
|
169
283
|
}
|
|
170
284
|
for (const { parsed: toolCall } of attemptedToolCallsByMessage) {
|
|
285
|
+
const preHookResult = runPreCommandHooks({
|
|
286
|
+
requestId: options.requestId,
|
|
287
|
+
entryEndpoint: options.entryEndpoint,
|
|
288
|
+
providerProtocol: options.providerProtocol,
|
|
289
|
+
toolName: toolCall.name,
|
|
290
|
+
toolCallId: toolCall.id,
|
|
291
|
+
toolArguments: toolCall.arguments,
|
|
292
|
+
preCommandState: runtimePreCommandState
|
|
293
|
+
});
|
|
294
|
+
for (const trace of preHookResult.traces) {
|
|
295
|
+
traceAutoHook(options, trace);
|
|
296
|
+
}
|
|
297
|
+
if (preHookResult.changed && preHookResult.toolArguments !== toolCall.arguments) {
|
|
298
|
+
toolCall.arguments = preHookResult.toolArguments;
|
|
299
|
+
patchToolCallArgumentsById(base, toolCall.id, preHookResult.toolArguments);
|
|
300
|
+
patchToolCallArgumentsById(baseForExecution, toolCall.id, preHookResult.toolArguments);
|
|
301
|
+
}
|
|
171
302
|
const entry = getServerToolHandler(toolCall.name);
|
|
172
303
|
if (!entry || entry.trigger !== 'tool_call') {
|
|
173
304
|
continue;
|
|
@@ -276,18 +407,89 @@ export async function runServerSideToolEngine(options) {
|
|
|
276
407
|
}
|
|
277
408
|
};
|
|
278
409
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
410
|
+
const autoHookExecutionList = listAutoServerToolHooks();
|
|
411
|
+
const { optionalQueue, mandatoryQueue } = buildAutoHookQueues(autoHookExecutionList);
|
|
412
|
+
const optionalResult = await runAutoHookQueue({
|
|
413
|
+
queueName: 'A_optional',
|
|
414
|
+
hooks: optionalQueue,
|
|
415
|
+
options,
|
|
416
|
+
contextBase: contextBase
|
|
417
|
+
});
|
|
418
|
+
if (optionalResult) {
|
|
419
|
+
return {
|
|
420
|
+
mode: 'tool_flow',
|
|
421
|
+
finalChatResponse: optionalResult.chatResponse,
|
|
422
|
+
execution: optionalResult.execution
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const mandatoryResult = await runAutoHookQueue({
|
|
426
|
+
queueName: 'B_mandatory',
|
|
427
|
+
hooks: mandatoryQueue,
|
|
428
|
+
options,
|
|
429
|
+
contextBase: contextBase
|
|
430
|
+
});
|
|
431
|
+
if (mandatoryResult) {
|
|
432
|
+
return {
|
|
433
|
+
mode: 'tool_flow',
|
|
434
|
+
finalChatResponse: mandatoryResult.chatResponse,
|
|
435
|
+
execution: mandatoryResult.execution
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return { mode: 'passthrough', finalChatResponse: base };
|
|
439
|
+
}
|
|
440
|
+
async function runAutoHookQueue(options) {
|
|
441
|
+
const queueTotal = options.hooks.length;
|
|
442
|
+
for (let idx = 0; idx < options.hooks.length; idx += 1) {
|
|
443
|
+
const hook = options.hooks[idx];
|
|
444
|
+
const traceBase = {
|
|
445
|
+
hookId: hook.id,
|
|
446
|
+
phase: hook.phase,
|
|
447
|
+
priority: hook.priority,
|
|
448
|
+
queue: options.queueName,
|
|
449
|
+
queueIndex: idx + 1,
|
|
450
|
+
queueTotal
|
|
451
|
+
};
|
|
452
|
+
let planned = null;
|
|
453
|
+
try {
|
|
454
|
+
planned = await runHandler(hook.handler, options.contextBase);
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
const message = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
458
|
+
traceAutoHook(options.options, {
|
|
459
|
+
...traceBase,
|
|
460
|
+
result: 'error',
|
|
461
|
+
reason: message
|
|
462
|
+
});
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
if (!planned) {
|
|
466
|
+
traceAutoHook(options.options, {
|
|
467
|
+
...traceBase,
|
|
468
|
+
result: 'miss',
|
|
469
|
+
reason: 'predicate_false'
|
|
470
|
+
});
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
const result = await materializePlannedResult(planned, options.options);
|
|
282
474
|
if (result) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
475
|
+
const flowId = result.execution && typeof result.execution.flowId === 'string' && result.execution.flowId.trim()
|
|
476
|
+
? result.execution.flowId.trim()
|
|
477
|
+
: undefined;
|
|
478
|
+
traceAutoHook(options.options, {
|
|
479
|
+
...traceBase,
|
|
480
|
+
result: 'match',
|
|
481
|
+
reason: flowId ? 'matched' : 'matched_without_flow',
|
|
482
|
+
...(flowId ? { flowId } : {})
|
|
483
|
+
});
|
|
484
|
+
return result;
|
|
288
485
|
}
|
|
486
|
+
traceAutoHook(options.options, {
|
|
487
|
+
...traceBase,
|
|
488
|
+
result: 'miss',
|
|
489
|
+
reason: 'empty_materialized_result'
|
|
490
|
+
});
|
|
289
491
|
}
|
|
290
|
-
return
|
|
492
|
+
return null;
|
|
291
493
|
}
|
|
292
494
|
async function runHandler(handler, ctx) {
|
|
293
495
|
try {
|
|
@@ -350,7 +552,7 @@ export function extractToolCalls(chatResponse) {
|
|
|
350
552
|
const tc = asObject(raw);
|
|
351
553
|
if (!tc)
|
|
352
554
|
continue;
|
|
353
|
-
const id =
|
|
555
|
+
const id = ensureToolCallId(tc);
|
|
354
556
|
const fn = asObject(tc.function) ??
|
|
355
557
|
asObject(tc.functionCall) ??
|
|
356
558
|
asObject(tc.function_call);
|
|
@@ -376,7 +578,7 @@ export function extractToolCalls(chatResponse) {
|
|
|
376
578
|
else if (rawArgs !== undefined && rawArgs !== null) {
|
|
377
579
|
args = String(rawArgs);
|
|
378
580
|
}
|
|
379
|
-
if (!
|
|
581
|
+
if (!name)
|
|
380
582
|
continue;
|
|
381
583
|
calls.push({ id, name, arguments: args });
|
|
382
584
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AdapterContext } from '../conversion/hub/types/chat-envelope.js';
|
|
2
|
+
export interface StopGatewayContext {
|
|
3
|
+
observed: boolean;
|
|
4
|
+
eligible: boolean;
|
|
5
|
+
source: 'chat' | 'responses' | 'none';
|
|
6
|
+
reason: string;
|
|
7
|
+
choiceIndex?: number;
|
|
8
|
+
hasToolCalls?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function inspectStopGatewaySignal(base: unknown): StopGatewayContext;
|
|
11
|
+
export declare function attachStopGatewayContext(adapterContext: AdapterContext, context: StopGatewayContext): void;
|
|
12
|
+
export declare function readStopGatewayContext(adapterContext: unknown): StopGatewayContext | undefined;
|
|
13
|
+
export declare function resolveStopGatewayContext(base: unknown, adapterContext?: unknown): StopGatewayContext;
|
|
14
|
+
export declare function isStopEligibleForServerTool(base: unknown, adapterContext?: unknown): boolean;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ensureRuntimeMetadata, readRuntimeMetadata } from '../conversion/shared/runtime-metadata.js';
|
|
2
|
+
export function inspectStopGatewaySignal(base) {
|
|
3
|
+
if (!base || typeof base !== 'object' || Array.isArray(base)) {
|
|
4
|
+
return {
|
|
5
|
+
observed: false,
|
|
6
|
+
eligible: false,
|
|
7
|
+
source: 'none',
|
|
8
|
+
reason: 'invalid_payload'
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const payload = base;
|
|
12
|
+
const choicesRaw = payload.choices;
|
|
13
|
+
if (Array.isArray(choicesRaw) && choicesRaw.length) {
|
|
14
|
+
for (let idx = 0; idx < choicesRaw.length; idx += 1) {
|
|
15
|
+
const choice = choicesRaw[idx];
|
|
16
|
+
if (!choice || typeof choice !== 'object' || Array.isArray(choice)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const finishReasonRaw = choice.finish_reason;
|
|
20
|
+
const finishReason = typeof finishReasonRaw === 'string' && finishReasonRaw.trim()
|
|
21
|
+
? finishReasonRaw.trim().toLowerCase()
|
|
22
|
+
: '';
|
|
23
|
+
if (!finishReason || finishReason === 'tool_calls') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (finishReason !== 'stop' && finishReason !== 'length') {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const message = choice.message &&
|
|
30
|
+
typeof choice.message === 'object' &&
|
|
31
|
+
!Array.isArray(choice.message)
|
|
32
|
+
? choice.message
|
|
33
|
+
: null;
|
|
34
|
+
const toolCalls = message && Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
35
|
+
const hasToolCalls = toolCalls.length > 0;
|
|
36
|
+
return {
|
|
37
|
+
observed: true,
|
|
38
|
+
eligible: !hasToolCalls,
|
|
39
|
+
source: 'chat',
|
|
40
|
+
reason: `finish_reason_${finishReason}`,
|
|
41
|
+
choiceIndex: idx,
|
|
42
|
+
hasToolCalls
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
observed: false,
|
|
47
|
+
eligible: false,
|
|
48
|
+
source: 'chat',
|
|
49
|
+
reason: 'no_stop_finish_reason'
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const statusRaw = typeof payload.status === 'string' ? payload.status.trim().toLowerCase() : '';
|
|
53
|
+
if (statusRaw && statusRaw !== 'completed') {
|
|
54
|
+
return {
|
|
55
|
+
observed: false,
|
|
56
|
+
eligible: false,
|
|
57
|
+
source: 'responses',
|
|
58
|
+
reason: `status_${statusRaw}`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const hasRequiredAction = Boolean(payload.required_action && typeof payload.required_action === 'object');
|
|
62
|
+
const outputRaw = Array.isArray(payload.output) ? payload.output : [];
|
|
63
|
+
if (!statusRaw && outputRaw.length === 0) {
|
|
64
|
+
return {
|
|
65
|
+
observed: false,
|
|
66
|
+
eligible: false,
|
|
67
|
+
source: 'responses',
|
|
68
|
+
reason: 'no_status_or_output'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (outputRaw.some((item) => hasToolLikeOutput(item))) {
|
|
72
|
+
return {
|
|
73
|
+
observed: true,
|
|
74
|
+
eligible: false,
|
|
75
|
+
source: 'responses',
|
|
76
|
+
reason: 'responses_tool_like_output'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (hasRequiredAction) {
|
|
80
|
+
return {
|
|
81
|
+
observed: true,
|
|
82
|
+
eligible: false,
|
|
83
|
+
source: 'responses',
|
|
84
|
+
reason: 'responses_required_action'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
observed: true,
|
|
89
|
+
eligible: true,
|
|
90
|
+
source: 'responses',
|
|
91
|
+
reason: statusRaw ? `status_${statusRaw}` : 'responses_output_completed'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export function attachStopGatewayContext(adapterContext, context) {
|
|
95
|
+
try {
|
|
96
|
+
const rt = ensureRuntimeMetadata(adapterContext);
|
|
97
|
+
rt.stopGatewayContext = {
|
|
98
|
+
observed: context.observed,
|
|
99
|
+
eligible: context.eligible,
|
|
100
|
+
source: context.source,
|
|
101
|
+
reason: context.reason,
|
|
102
|
+
...(typeof context.choiceIndex === 'number' ? { choiceIndex: context.choiceIndex } : {}),
|
|
103
|
+
...(typeof context.hasToolCalls === 'boolean' ? { hasToolCalls: context.hasToolCalls } : {})
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// ignore metadata write failures
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function readStopGatewayContext(adapterContext) {
|
|
111
|
+
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
const rt = readRuntimeMetadata(adapterContext);
|
|
115
|
+
const raw = rt && typeof rt === 'object' ? rt.stopGatewayContext : undefined;
|
|
116
|
+
return normalizeStopGatewayContext(raw);
|
|
117
|
+
}
|
|
118
|
+
export function resolveStopGatewayContext(base, adapterContext) {
|
|
119
|
+
const fromMetadata = readStopGatewayContext(adapterContext);
|
|
120
|
+
if (fromMetadata) {
|
|
121
|
+
return fromMetadata;
|
|
122
|
+
}
|
|
123
|
+
return inspectStopGatewaySignal(base);
|
|
124
|
+
}
|
|
125
|
+
export function isStopEligibleForServerTool(base, adapterContext) {
|
|
126
|
+
return resolveStopGatewayContext(base, adapterContext).eligible;
|
|
127
|
+
}
|
|
128
|
+
function normalizeStopGatewayContext(raw) {
|
|
129
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const record = raw;
|
|
133
|
+
if (typeof record.observed !== 'boolean' || typeof record.eligible !== 'boolean') {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const sourceRaw = typeof record.source === 'string' ? record.source.trim().toLowerCase() : '';
|
|
137
|
+
const source = sourceRaw === 'chat' || sourceRaw === 'responses' || sourceRaw === 'none'
|
|
138
|
+
? sourceRaw
|
|
139
|
+
: 'none';
|
|
140
|
+
const reason = typeof record.reason === 'string' && record.reason.trim() ? record.reason.trim() : 'unknown';
|
|
141
|
+
const choiceIndex = typeof record.choiceIndex === 'number' && Number.isFinite(record.choiceIndex)
|
|
142
|
+
? Math.floor(record.choiceIndex)
|
|
143
|
+
: undefined;
|
|
144
|
+
const hasToolCalls = typeof record.hasToolCalls === 'boolean' ? record.hasToolCalls : undefined;
|
|
145
|
+
return {
|
|
146
|
+
observed: record.observed,
|
|
147
|
+
eligible: record.eligible,
|
|
148
|
+
source,
|
|
149
|
+
reason,
|
|
150
|
+
...(typeof choiceIndex === 'number' ? { choiceIndex } : {}),
|
|
151
|
+
...(typeof hasToolCalls === 'boolean' ? { hasToolCalls } : {})
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function hasToolLikeOutput(value) {
|
|
155
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const typeRaw = value.type;
|
|
159
|
+
const type = typeof typeRaw === 'string' ? typeRaw.trim().toLowerCase() : '';
|
|
160
|
+
if (!type) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return (type === 'tool_call' ||
|
|
164
|
+
type === 'tool_use' ||
|
|
165
|
+
type === 'function_call' ||
|
|
166
|
+
type.includes('tool'));
|
|
167
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface StopMessageCompareContext {
|
|
2
|
+
armed: boolean;
|
|
3
|
+
mode: 'off' | 'on' | 'auto';
|
|
4
|
+
allowModeOnly: boolean;
|
|
5
|
+
textLength: number;
|
|
6
|
+
maxRepeats: number;
|
|
7
|
+
used: number;
|
|
8
|
+
remaining: number;
|
|
9
|
+
active: boolean;
|
|
10
|
+
stopEligible: boolean;
|
|
11
|
+
hasCapturedRequest: boolean;
|
|
12
|
+
compactionRequest: boolean;
|
|
13
|
+
hasSeed: boolean;
|
|
14
|
+
decision: 'trigger' | 'skip';
|
|
15
|
+
reason: string;
|
|
16
|
+
stage?: string;
|
|
17
|
+
bdWorkState?: string;
|
|
18
|
+
observationHash?: string;
|
|
19
|
+
observationStableCount?: number;
|
|
20
|
+
toolSignatureHash?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function attachStopMessageCompareContext(adapterContext: unknown, context: StopMessageCompareContext): void;
|
|
23
|
+
export declare function readStopMessageCompareContext(adapterContext: unknown): StopMessageCompareContext | undefined;
|
|
24
|
+
export declare function formatStopMessageCompareContext(context: StopMessageCompareContext | undefined): string;
|