@jsonstudio/llms 0.6.2125 → 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-response.js +27 -3
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +9 -3
- package/dist/conversion/hub/process/chat-process.js +15 -18
- package/dist/conversion/responses/responses-openai-bridge.js +13 -12
- package/dist/conversion/shared/bridge-message-utils.js +92 -39
- package/dist/router/virtual-router/classifier.js +29 -5
- package/dist/router/virtual-router/engine/routing-pools/index.js +111 -5
- 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 +2 -0
- package/dist/router/virtual-router/engine.d.ts +2 -0
- package/dist/router/virtual-router/engine.js +57 -14
- 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/token-counter.js +51 -10
- package/dist/router/virtual-router/types.d.ts +3 -0
- package/dist/servertool/clock/session-scope.d.ts +3 -0
- package/dist/servertool/clock/session-scope.js +52 -0
- package/dist/servertool/engine.js +68 -8
- package/dist/servertool/handlers/clock-auto.js +2 -8
- package/dist/servertool/handlers/clock.js +3 -9
- 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 -7
- package/dist/servertool/handlers/stop-message-auto.js +69 -971
- package/dist/servertool/handlers/web-search.js +117 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
1
2
|
export function getLatestUserMessage(messages) {
|
|
2
3
|
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
3
4
|
if (messages[idx]?.role === 'user') {
|
|
@@ -56,55 +57,179 @@ export function detectExtendedThinkingKeyword(text) {
|
|
|
56
57
|
const keywords = ['仔细分析', '思考', '超级思考', '深度思考', 'careful analysis', 'deep thinking', 'deliberate'];
|
|
57
58
|
return keywords.some((keyword) => text.includes(keyword));
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
const LOCAL_URL_SCHEMES = ['data:', 'file:', 'blob:'];
|
|
61
|
+
function extractMediaUrlCandidate(record) {
|
|
62
|
+
if (typeof record.image_url === 'string') {
|
|
63
|
+
return record.image_url ?? '';
|
|
64
|
+
}
|
|
65
|
+
if (typeof record.video_url === 'string') {
|
|
66
|
+
return record.video_url ?? '';
|
|
67
|
+
}
|
|
68
|
+
if (record.image_url &&
|
|
69
|
+
typeof record.image_url?.url === 'string') {
|
|
70
|
+
return record.image_url?.url ?? '';
|
|
71
|
+
}
|
|
72
|
+
if (record.video_url &&
|
|
73
|
+
typeof record.video_url?.url === 'string') {
|
|
74
|
+
return record.video_url?.url ?? '';
|
|
75
|
+
}
|
|
76
|
+
if (typeof record.url === 'string') {
|
|
77
|
+
return record.url ?? '';
|
|
78
|
+
}
|
|
79
|
+
if (typeof record.uri === 'string') {
|
|
80
|
+
return record.uri ?? '';
|
|
81
|
+
}
|
|
82
|
+
if (typeof record.data === 'string') {
|
|
83
|
+
return record.data ?? '';
|
|
84
|
+
}
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
function resolveMediaKind(typeValue, record) {
|
|
88
|
+
if (typeValue.includes('video')) {
|
|
89
|
+
return 'video';
|
|
90
|
+
}
|
|
91
|
+
if (typeValue.includes('image')) {
|
|
92
|
+
return 'image';
|
|
93
|
+
}
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(record, 'video_url')) {
|
|
95
|
+
return 'video';
|
|
96
|
+
}
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(record, 'image_url')) {
|
|
98
|
+
return 'image';
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function isPrivateHost(host) {
|
|
103
|
+
const normalized = host.trim().toLowerCase();
|
|
104
|
+
if (!normalized) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (normalized === 'localhost' || normalized.endsWith('.local')) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const ipType = isIP(normalized);
|
|
111
|
+
if (ipType === 4) {
|
|
112
|
+
const octets = normalized.split('.').map((part) => Number.parseInt(part, 10));
|
|
113
|
+
if (octets.length !== 4 || octets.some((value) => !Number.isFinite(value))) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (octets[0] === 10)
|
|
117
|
+
return true;
|
|
118
|
+
if (octets[0] === 127)
|
|
119
|
+
return true;
|
|
120
|
+
if (octets[0] === 0)
|
|
121
|
+
return true;
|
|
122
|
+
if (octets[0] === 169 && octets[1] === 254)
|
|
123
|
+
return true;
|
|
124
|
+
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)
|
|
125
|
+
return true;
|
|
126
|
+
if (octets[0] === 192 && octets[1] === 168)
|
|
127
|
+
return true;
|
|
61
128
|
return false;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
129
|
+
}
|
|
130
|
+
if (ipType === 6) {
|
|
131
|
+
if (normalized === '::1')
|
|
132
|
+
return true;
|
|
133
|
+
if (normalized.startsWith('fc') || normalized.startsWith('fd'))
|
|
134
|
+
return true;
|
|
135
|
+
if (normalized.startsWith('fe80:'))
|
|
136
|
+
return true;
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
function isRemotePublicHttpUrl(raw) {
|
|
142
|
+
const value = raw.trim();
|
|
143
|
+
if (!value) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const lowered = value.toLowerCase();
|
|
147
|
+
if (LOCAL_URL_SCHEMES.some((prefix) => lowered.startsWith(prefix))) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
let parsed;
|
|
151
|
+
try {
|
|
152
|
+
parsed = new URL(value);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return !isPrivateHost(parsed.hostname);
|
|
161
|
+
}
|
|
162
|
+
export function analyzeMediaAttachments(message) {
|
|
163
|
+
const result = {
|
|
164
|
+
hasAnyMedia: false,
|
|
165
|
+
hasImage: false,
|
|
166
|
+
hasVideo: false,
|
|
167
|
+
hasRemoteVideo: false,
|
|
168
|
+
hasLocalVideo: false
|
|
169
|
+
};
|
|
170
|
+
if (!message) {
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
if (typeof message.content === 'string' && message.content.trim()) {
|
|
174
|
+
const raw = message.content;
|
|
175
|
+
const hasImageBlock = /"type"\s*:\s*"(?:input_)?image(?:_url)?"/iu.test(raw);
|
|
176
|
+
const hasVideoBlock = /"type"\s*:\s*"(?:input_)?video(?:_url)?"/iu.test(raw);
|
|
177
|
+
const hasDataVideo = /data:video\//iu.test(raw);
|
|
178
|
+
const hasRemoteVideo = /https?:\/\/[^\s"'\\]+/iu.test(raw);
|
|
179
|
+
if (hasImageBlock || hasVideoBlock) {
|
|
180
|
+
result.hasAnyMedia = true;
|
|
181
|
+
}
|
|
182
|
+
if (hasImageBlock) {
|
|
183
|
+
result.hasImage = true;
|
|
184
|
+
}
|
|
185
|
+
if (hasVideoBlock) {
|
|
186
|
+
result.hasVideo = true;
|
|
187
|
+
if (hasDataVideo) {
|
|
188
|
+
result.hasLocalVideo = true;
|
|
100
189
|
}
|
|
101
|
-
|
|
102
|
-
|
|
190
|
+
if (hasRemoteVideo) {
|
|
191
|
+
result.hasRemoteVideo = true;
|
|
103
192
|
}
|
|
104
|
-
if (
|
|
105
|
-
|
|
193
|
+
if (!hasDataVideo && !hasRemoteVideo) {
|
|
194
|
+
result.hasLocalVideo = true;
|
|
106
195
|
}
|
|
107
196
|
}
|
|
197
|
+
if (result.hasAnyMedia) {
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
108
200
|
}
|
|
109
|
-
|
|
201
|
+
if (!Array.isArray(message.content)) {
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
for (const part of message.content) {
|
|
205
|
+
if (!part || typeof part !== 'object') {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const record = part;
|
|
209
|
+
const typeValue = typeof record.type === 'string' ? record.type.toLowerCase() : '';
|
|
210
|
+
const mediaKind = resolveMediaKind(typeValue, record);
|
|
211
|
+
if (!mediaKind) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const mediaUrl = extractMediaUrlCandidate(record).trim();
|
|
215
|
+
if (!mediaUrl) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
result.hasAnyMedia = true;
|
|
219
|
+
if (mediaKind === 'image') {
|
|
220
|
+
result.hasImage = true;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
result.hasVideo = true;
|
|
224
|
+
if (isRemotePublicHttpUrl(mediaUrl)) {
|
|
225
|
+
result.hasRemoteVideo = true;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
result.hasLocalVideo = true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
export function detectImageAttachment(message) {
|
|
234
|
+
return analyzeMediaAttachments(message).hasAnyMedia;
|
|
110
235
|
}
|
|
@@ -64,11 +64,54 @@ function countMessageTokens(message, encoder) {
|
|
|
64
64
|
}
|
|
65
65
|
return total;
|
|
66
66
|
}
|
|
67
|
+
function detectMediaKind(record) {
|
|
68
|
+
const typeValue = typeof record.type === 'string' ? record.type.trim().toLowerCase() : '';
|
|
69
|
+
if (typeValue.includes('video')) {
|
|
70
|
+
return 'video';
|
|
71
|
+
}
|
|
72
|
+
if (typeValue.includes('image')) {
|
|
73
|
+
return 'image';
|
|
74
|
+
}
|
|
75
|
+
if (Object.prototype.hasOwnProperty.call(record, 'video_url')) {
|
|
76
|
+
return 'video';
|
|
77
|
+
}
|
|
78
|
+
if (Object.prototype.hasOwnProperty.call(record, 'image_url')) {
|
|
79
|
+
return 'image';
|
|
80
|
+
}
|
|
81
|
+
const dataField = typeof record.data === 'string' ? record.data.trim().toLowerCase() : '';
|
|
82
|
+
if (dataField.startsWith('data:video/')) {
|
|
83
|
+
return 'video';
|
|
84
|
+
}
|
|
85
|
+
if (dataField.startsWith('data:image/')) {
|
|
86
|
+
return 'image';
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function parseStructuredContentString(raw) {
|
|
91
|
+
const trimmed = raw.trim();
|
|
92
|
+
if (!trimmed) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const likelyJson = (trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'));
|
|
96
|
+
if (!likelyJson) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(trimmed);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
67
106
|
function encodeContent(content, encoder) {
|
|
68
107
|
if (content === null || content === undefined) {
|
|
69
108
|
return 0;
|
|
70
109
|
}
|
|
71
110
|
if (typeof content === 'string') {
|
|
111
|
+
const structured = parseStructuredContentString(content);
|
|
112
|
+
if (structured !== undefined) {
|
|
113
|
+
return encodeContent(structured, encoder);
|
|
114
|
+
}
|
|
72
115
|
return encodeText(content, encoder);
|
|
73
116
|
}
|
|
74
117
|
if (Array.isArray(content)) {
|
|
@@ -79,17 +122,11 @@ function encodeContent(content, encoder) {
|
|
|
79
122
|
}
|
|
80
123
|
else if (part && typeof part === 'object') {
|
|
81
124
|
const record = part;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// small textual placeholder instead of the full JSON/body.
|
|
86
|
-
if (typeValue.startsWith('image')) {
|
|
87
|
-
const caption = typeof record.caption === 'string' && record.caption.trim().length
|
|
88
|
-
? record.caption
|
|
89
|
-
: '[image]';
|
|
90
|
-
total += encodeText(caption, encoder);
|
|
125
|
+
// Ignore image/video payload blocks in token estimation.
|
|
126
|
+
if (detectMediaKind(record)) {
|
|
127
|
+
continue;
|
|
91
128
|
}
|
|
92
|
-
|
|
129
|
+
if (typeof record.text === 'string') {
|
|
93
130
|
total += encodeText(record.text, encoder);
|
|
94
131
|
}
|
|
95
132
|
else {
|
|
@@ -100,6 +137,10 @@ function encodeContent(content, encoder) {
|
|
|
100
137
|
return total;
|
|
101
138
|
}
|
|
102
139
|
if (typeof content === 'object') {
|
|
140
|
+
const record = content;
|
|
141
|
+
if (detectMediaKind(record)) {
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
103
144
|
return encodeText(JSON.stringify(content), encoder);
|
|
104
145
|
}
|
|
105
146
|
return encodeText(String(content), encoder);
|
|
@@ -386,6 +386,9 @@ export interface RoutingFeatures {
|
|
|
386
386
|
hasToolCallResponses: boolean;
|
|
387
387
|
hasVisionTool: boolean;
|
|
388
388
|
hasImageAttachment: boolean;
|
|
389
|
+
hasVideoAttachment?: boolean;
|
|
390
|
+
hasRemoteVideoAttachment?: boolean;
|
|
391
|
+
hasLocalVideoAttachment?: boolean;
|
|
389
392
|
hasWebTool: boolean;
|
|
390
393
|
hasWebSearchToolDeclared?: boolean;
|
|
391
394
|
hasCodingTool: boolean;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function buildClockSessionScopeFromDaemonId(daemonId: string): string;
|
|
2
|
+
export declare function extractClockDaemonIdFromSessionScope(sessionScope: string): string | null;
|
|
3
|
+
export declare function resolveClockSessionScope(primary?: Record<string, unknown> | null, fallback?: Record<string, unknown> | null): string | null;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const CLOCK_DAEMON_SESSION_PREFIX = 'clockd.';
|
|
2
|
+
function readToken(value) {
|
|
3
|
+
return typeof value === 'string' && value.trim().length ? value.trim() : '';
|
|
4
|
+
}
|
|
5
|
+
function readRecordToken(record, keys) {
|
|
6
|
+
if (!record) {
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
for (const key of keys) {
|
|
10
|
+
const value = readToken(record[key]);
|
|
11
|
+
if (value) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
export function buildClockSessionScopeFromDaemonId(daemonId) {
|
|
18
|
+
const normalized = readToken(daemonId);
|
|
19
|
+
if (!normalized) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
return `${CLOCK_DAEMON_SESSION_PREFIX}${normalized}`;
|
|
23
|
+
}
|
|
24
|
+
export function extractClockDaemonIdFromSessionScope(sessionScope) {
|
|
25
|
+
const normalized = readToken(sessionScope);
|
|
26
|
+
if (!normalized.startsWith(CLOCK_DAEMON_SESSION_PREFIX)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const daemonId = normalized.slice(CLOCK_DAEMON_SESSION_PREFIX.length).trim();
|
|
30
|
+
return daemonId || null;
|
|
31
|
+
}
|
|
32
|
+
export function resolveClockSessionScope(primary, fallback) {
|
|
33
|
+
const daemonId = readRecordToken(primary, ['clockDaemonId', 'clockClientDaemonId', 'clock_daemon_id', 'clock_client_daemon_id']) ||
|
|
34
|
+
readRecordToken(fallback, ['clockDaemonId', 'clockClientDaemonId', 'clock_daemon_id', 'clock_client_daemon_id']);
|
|
35
|
+
if (daemonId) {
|
|
36
|
+
const daemonScope = buildClockSessionScopeFromDaemonId(daemonId);
|
|
37
|
+
if (daemonScope) {
|
|
38
|
+
return daemonScope;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const sessionId = readRecordToken(primary, ['sessionId', 'session_id']) ||
|
|
42
|
+
readRecordToken(fallback, ['sessionId', 'session_id']);
|
|
43
|
+
if (sessionId) {
|
|
44
|
+
return sessionId;
|
|
45
|
+
}
|
|
46
|
+
const conversationId = readRecordToken(primary, ['conversationId', 'conversation_id']) ||
|
|
47
|
+
readRecordToken(fallback, ['conversationId', 'conversation_id']);
|
|
48
|
+
if (conversationId) {
|
|
49
|
+
return conversationId;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
@@ -7,6 +7,7 @@ import { deserializeRoutingInstructionState, serializeRoutingInstructionState }
|
|
|
7
7
|
import { applyHubFollowupPolicyShadow } from './followup-shadow.js';
|
|
8
8
|
import { buildServerToolFollowupChatPayloadFromInjection, extractCapturedChatSeed } from './handlers/followup-request-builder.js';
|
|
9
9
|
import { findNextUndeliveredDueAtMs, listClockTasks, resolveClockConfig } from './clock/task-store.js';
|
|
10
|
+
import { resolveClockSessionScope } from './clock/session-scope.js';
|
|
10
11
|
import { savePendingServerToolInjection } from './pending-session.js';
|
|
11
12
|
import { appendServerToolProgressFileEvent } from './log/progress-file.js';
|
|
12
13
|
import { attachStopGatewayContext, inspectStopGatewaySignal } from './stop-gateway-context.js';
|
|
@@ -270,7 +271,7 @@ async function shouldDisableServerToolTimeoutForClockHold(args) {
|
|
|
270
271
|
}
|
|
271
272
|
const record = args.adapterContext;
|
|
272
273
|
const rt = readRuntimeMetadata(record);
|
|
273
|
-
const sessionId =
|
|
274
|
+
const sessionId = resolveClockSessionScope(record, rt);
|
|
274
275
|
if (!sessionId) {
|
|
275
276
|
return false;
|
|
276
277
|
}
|
|
@@ -985,19 +986,78 @@ function resolveStickyKeyFromAdapterContext(adapterContext) {
|
|
|
985
986
|
if (!adapterContext || typeof adapterContext !== 'object') {
|
|
986
987
|
return undefined;
|
|
987
988
|
}
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
989
|
+
const record = adapterContext;
|
|
990
|
+
const runtime = readRuntimeMetadata(record);
|
|
991
|
+
const providerProtocol = readTextFromAny(record.providerProtocol) ||
|
|
992
|
+
readTextFromAny(runtime?.providerProtocol) ||
|
|
993
|
+
readTextFromAny(record.provider_protocol) ||
|
|
994
|
+
readTextFromAny(runtime?.provider_protocol);
|
|
995
|
+
if (providerProtocol.trim().toLowerCase() === 'openai-responses') {
|
|
996
|
+
const previousRequestId = resolveResponsesResumePreviousRequestId(record, runtime);
|
|
997
|
+
if (previousRequestId) {
|
|
998
|
+
return previousRequestId;
|
|
999
|
+
}
|
|
1000
|
+
const requestId = resolveStickyRequestId(record, runtime);
|
|
1001
|
+
if (requestId) {
|
|
1002
|
+
return requestId;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const sessionId = readTextFromAny(record.sessionId) ||
|
|
1006
|
+
readTextFromAny(record.session_id) ||
|
|
1007
|
+
readTextFromAny(runtime?.sessionId) ||
|
|
1008
|
+
readTextFromAny(runtime?.session_id);
|
|
1009
|
+
const conversationId = readTextFromAny(record.conversationId) ||
|
|
1010
|
+
readTextFromAny(record.conversation_id) ||
|
|
1011
|
+
readTextFromAny(runtime?.conversationId) ||
|
|
1012
|
+
readTextFromAny(runtime?.conversation_id);
|
|
994
1013
|
if (sessionId) {
|
|
995
1014
|
return `session:${sessionId}`;
|
|
996
1015
|
}
|
|
997
1016
|
if (conversationId) {
|
|
998
1017
|
return `conversation:${conversationId}`;
|
|
999
1018
|
}
|
|
1000
|
-
|
|
1019
|
+
const requestId = resolveStickyRequestId(record, runtime);
|
|
1020
|
+
return requestId || undefined;
|
|
1021
|
+
}
|
|
1022
|
+
function resolveStickyRequestId(context, runtime) {
|
|
1023
|
+
return (readTextFromAny(context.requestId) ||
|
|
1024
|
+
readTextFromAny(context.request_id) ||
|
|
1025
|
+
readTextFromAny(runtime?.requestId) ||
|
|
1026
|
+
readTextFromAny(runtime?.request_id));
|
|
1027
|
+
}
|
|
1028
|
+
function resolveResponsesResumePreviousRequestId(context, runtime) {
|
|
1029
|
+
const contextMetadata = asRecord(context.metadata);
|
|
1030
|
+
const contextMetadataContext = asRecord(contextMetadata?.context);
|
|
1031
|
+
const originalRequest = asRecord(context.originalRequest);
|
|
1032
|
+
const originalMetadata = asRecord(originalRequest?.metadata);
|
|
1033
|
+
const candidates = [
|
|
1034
|
+
context.responsesResume,
|
|
1035
|
+
contextMetadata?.responsesResume,
|
|
1036
|
+
contextMetadataContext?.responsesResume,
|
|
1037
|
+
originalRequest?.responsesResume,
|
|
1038
|
+
originalMetadata?.responsesResume,
|
|
1039
|
+
runtime?.responsesResume
|
|
1040
|
+
];
|
|
1041
|
+
for (const candidate of candidates) {
|
|
1042
|
+
const resume = asRecord(candidate);
|
|
1043
|
+
if (!resume) {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
const previousRequestId = readTextFromAny(resume.previousRequestId) || readTextFromAny(resume.previous_request_id);
|
|
1047
|
+
if (previousRequestId) {
|
|
1048
|
+
return previousRequestId;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return '';
|
|
1052
|
+
}
|
|
1053
|
+
function asRecord(value) {
|
|
1054
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
return value;
|
|
1058
|
+
}
|
|
1059
|
+
function readTextFromAny(value) {
|
|
1060
|
+
return typeof value === 'string' && value.trim().length ? value.trim() : '';
|
|
1001
1061
|
}
|
|
1002
1062
|
function cloneRoutingInstructionState(state) {
|
|
1003
1063
|
if (!state) {
|
|
@@ -4,6 +4,7 @@ import { readRuntimeMetadata } from '../../conversion/shared/runtime-metadata.js
|
|
|
4
4
|
import { findNextUndeliveredDueAtMs, listClockTasks, reserveDueTasksForRequest, resolveClockConfig, startClockDaemonIfNeeded } from '../clock/task-store.js';
|
|
5
5
|
import { nowMs } from '../clock/state.js';
|
|
6
6
|
import { logClock } from '../clock/log.js';
|
|
7
|
+
import { resolveClockSessionScope } from '../clock/session-scope.js';
|
|
7
8
|
import { isStopEligibleForServerTool } from '../stop-gateway-context.js';
|
|
8
9
|
const FLOW_ID = 'clock_hold_flow';
|
|
9
10
|
function resolveClientConnectionState(value) {
|
|
@@ -40,13 +41,6 @@ function clientWantsStreaming(adapterContext) {
|
|
|
40
41
|
}
|
|
41
42
|
return false;
|
|
42
43
|
}
|
|
43
|
-
function resolveSessionId(adapterContext) {
|
|
44
|
-
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
const sessionId = typeof adapterContext.sessionId === 'string' ? String(adapterContext.sessionId).trim() : '';
|
|
48
|
-
return sessionId || null;
|
|
49
|
-
}
|
|
50
44
|
function computeHoldSleepMs(remainingMs) {
|
|
51
45
|
if (remainingMs <= 0)
|
|
52
46
|
return 0;
|
|
@@ -81,7 +75,7 @@ const handler = async (ctx) => {
|
|
|
81
75
|
return null;
|
|
82
76
|
}
|
|
83
77
|
const rt = readRuntimeMetadata(ctx.adapterContext);
|
|
84
|
-
const sessionId =
|
|
78
|
+
const sessionId = resolveClockSessionScope(ctx.adapterContext, rt);
|
|
85
79
|
if (!sessionId) {
|
|
86
80
|
return null;
|
|
87
81
|
}
|
|
@@ -6,6 +6,7 @@ import { cancelClockTask, clearClockTasks, listClockTasks, resolveClockConfig, p
|
|
|
6
6
|
import { getClockTimeSnapshot } from '../clock/ntp.js';
|
|
7
7
|
import { nowMs } from '../clock/state.js';
|
|
8
8
|
import { logClock } from '../clock/log.js';
|
|
9
|
+
import { resolveClockSessionScope } from '../clock/session-scope.js';
|
|
9
10
|
const FLOW_ID = 'clock_flow';
|
|
10
11
|
let fallbackClockToolCallSeq = 0;
|
|
11
12
|
function ensureClockToolCall(toolCall, requestId) {
|
|
@@ -86,13 +87,6 @@ function parseToolArguments(toolCall) {
|
|
|
86
87
|
return {};
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
|
-
function resolveSessionId(adapterContext) {
|
|
90
|
-
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
const sessionId = typeof adapterContext.sessionId === 'string' ? String(adapterContext.sessionId).trim() : '';
|
|
94
|
-
return sessionId || null;
|
|
95
|
-
}
|
|
96
90
|
function injectClockToolOutput(base, toolCall, content) {
|
|
97
91
|
const cloned = cloneJson(base);
|
|
98
92
|
const existingOutputs = Array.isArray(cloned.tool_outputs)
|
|
@@ -277,7 +271,7 @@ const handler = async (ctx) => {
|
|
|
277
271
|
return null;
|
|
278
272
|
}
|
|
279
273
|
const rt = readRuntimeMetadata(ctx.adapterContext);
|
|
280
|
-
const sessionId =
|
|
274
|
+
const sessionId = resolveClockSessionScope(ctx.adapterContext, rt);
|
|
281
275
|
const rawConfig = rt?.clock ?? ctx.adapterContext.clock;
|
|
282
276
|
// Default-enable clock when config is absent, but keep "explicitly disabled" honored.
|
|
283
277
|
const clockConfig = resolveClockConfig(rawConfig);
|
|
@@ -391,7 +385,7 @@ const handler = async (ctx) => {
|
|
|
391
385
|
return respond({
|
|
392
386
|
ok: false,
|
|
393
387
|
action,
|
|
394
|
-
message: 'clock requires
|
|
388
|
+
message: 'clock requires session scope (sessionId/conversationId or clockDaemonId).'
|
|
395
389
|
});
|
|
396
390
|
}
|
|
397
391
|
if (action === 'list') {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface StopMessageBlockedReport {
|
|
2
|
+
summary: string;
|
|
3
|
+
blocker: string;
|
|
4
|
+
impact?: string;
|
|
5
|
+
nextAction?: string;
|
|
6
|
+
evidence: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface StopMessageBlockedIssueContext {
|
|
9
|
+
requestId?: string;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function extractBlockedReportFromMessages(messages: unknown[]): StopMessageBlockedReport | null;
|
|
13
|
+
export declare function extractBlockedReportFromMessagesForTests(messages: unknown[]): StopMessageBlockedReport | null;
|
|
14
|
+
export declare function createBdIssueFromBlockedReport(blockedReport: StopMessageBlockedReport, context?: StopMessageBlockedIssueContext, cwdOverride?: string): string | null;
|
|
15
|
+
export declare function extractCapturedMessageText(message: unknown): string;
|
|
16
|
+
export declare function extractTextFromMessageContent(content: unknown): string;
|