@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,9 +1,10 @@
|
|
|
1
1
|
import { registerServerToolHandler } from '../registry.js';
|
|
2
2
|
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
3
3
|
import { readRuntimeMetadata } from '../../conversion/shared/runtime-metadata.js';
|
|
4
|
-
import { findNextUndeliveredDueAtMs, listClockTasks, resolveClockConfig, startClockDaemonIfNeeded } from '../clock/task-store.js';
|
|
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 { isStopEligibleForServerTool } from '../stop-gateway-context.js';
|
|
7
8
|
const FLOW_ID = 'clock_hold_flow';
|
|
8
9
|
function resolveClientConnectionState(value) {
|
|
9
10
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -20,7 +21,14 @@ function clientWantsStreaming(adapterContext) {
|
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
22
23
|
const record = adapterContext;
|
|
23
|
-
if (record.stream === true
|
|
24
|
+
if (record.stream === true ||
|
|
25
|
+
record.inboundStream === true ||
|
|
26
|
+
record.outboundStream === true ||
|
|
27
|
+
record.clientStream === true) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const streamingHint = typeof record.streamingHint === 'string' ? record.streamingHint.trim().toLowerCase() : '';
|
|
31
|
+
if (streamingHint === 'force') {
|
|
24
32
|
return true;
|
|
25
33
|
}
|
|
26
34
|
const clientHeaders = record.clientHeaders;
|
|
@@ -32,50 +40,6 @@ function clientWantsStreaming(adapterContext) {
|
|
|
32
40
|
}
|
|
33
41
|
return false;
|
|
34
42
|
}
|
|
35
|
-
function isStopFinishReason(base) {
|
|
36
|
-
if (!base || typeof base !== 'object' || Array.isArray(base)) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
const payload = base;
|
|
40
|
-
const choicesRaw = payload.choices;
|
|
41
|
-
if (Array.isArray(choicesRaw) && choicesRaw.length) {
|
|
42
|
-
const first = choicesRaw[0];
|
|
43
|
-
if (!first || typeof first !== 'object' || Array.isArray(first)) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
const finishReasonRaw = first.finish_reason;
|
|
47
|
-
const finishReason = typeof finishReasonRaw === 'string' && finishReasonRaw.trim()
|
|
48
|
-
? finishReasonRaw.trim().toLowerCase()
|
|
49
|
-
: '';
|
|
50
|
-
if (!finishReason || finishReason === 'tool_calls') {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
if (finishReason !== 'stop' && finishReason !== 'length') {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
const message = first.message &&
|
|
57
|
-
typeof first.message === 'object' &&
|
|
58
|
-
!Array.isArray(first.message)
|
|
59
|
-
? first.message
|
|
60
|
-
: null;
|
|
61
|
-
if (!message) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
65
|
-
if (toolCalls.length > 0) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
const statusRaw = typeof payload.status === 'string' ? payload.status.trim().toLowerCase() : '';
|
|
71
|
-
if (statusRaw && statusRaw !== 'completed') {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
if (payload.required_action && typeof payload.required_action === 'object') {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
43
|
function resolveSessionId(adapterContext) {
|
|
80
44
|
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
81
45
|
return null;
|
|
@@ -103,7 +67,7 @@ async function sleep(ms) {
|
|
|
103
67
|
const handler = async (ctx) => {
|
|
104
68
|
const record = ctx.adapterContext;
|
|
105
69
|
// Only trigger on stop/length completion (no tool calls).
|
|
106
|
-
if (!
|
|
70
|
+
if (!isStopEligibleForServerTool(ctx.base, ctx.adapterContext)) {
|
|
107
71
|
return null;
|
|
108
72
|
}
|
|
109
73
|
// When client already disconnected, skip holding.
|
|
@@ -147,33 +111,47 @@ const handler = async (ctx) => {
|
|
|
147
111
|
}
|
|
148
112
|
// Wait until the "due window" is reached (now >= dueAt - dueWindowMs).
|
|
149
113
|
const thresholdMs = nextDueAtMs - clockConfig.dueWindowMs;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (at >= thresholdMs) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
const remainingMs = thresholdMs - at;
|
|
156
|
-
if (clockConfig.holdMaxMs >= 0 && remainingMs > clockConfig.holdMaxMs) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
logClock('hold_start', { sessionId, nextDueAtMs, thresholdMs });
|
|
160
|
-
while (nowMs() < thresholdMs) {
|
|
161
|
-
const state = resolveClientConnectionState(ctx.adapterContext.clientConnectionState);
|
|
162
|
-
if (state?.disconnected === true) {
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
const remaining = thresholdMs - nowMs();
|
|
166
|
-
await sleep(computeHoldSleepMs(remaining));
|
|
167
|
-
// Best-effort: if tasks were cleared/cancelled while holding, stop holding.
|
|
114
|
+
const inDueWindow = at >= thresholdMs;
|
|
115
|
+
if (inDueWindow) {
|
|
168
116
|
try {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
117
|
+
const probe = await reserveDueTasksForRequest({
|
|
118
|
+
reservationId: `${ctx.requestId}:clock_auto_probe`,
|
|
119
|
+
sessionId,
|
|
120
|
+
config: clockConfig,
|
|
121
|
+
requestId: ctx.requestId
|
|
122
|
+
});
|
|
123
|
+
if (!probe.reservation || !Array.isArray(probe.reservation.taskIds) || probe.reservation.taskIds.length === 0) {
|
|
172
124
|
return null;
|
|
173
125
|
}
|
|
174
126
|
}
|
|
175
127
|
catch {
|
|
176
|
-
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const remainingMs = thresholdMs - at;
|
|
133
|
+
if (clockConfig.holdMaxMs >= 0 && remainingMs > clockConfig.holdMaxMs) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
logClock('hold_start', { sessionId, nextDueAtMs, thresholdMs });
|
|
137
|
+
while (nowMs() < thresholdMs) {
|
|
138
|
+
const state = resolveClientConnectionState(ctx.adapterContext.clientConnectionState);
|
|
139
|
+
if (state?.disconnected === true) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const remaining = thresholdMs - nowMs();
|
|
143
|
+
await sleep(computeHoldSleepMs(remaining));
|
|
144
|
+
// Best-effort: if tasks were cleared/cancelled while holding, stop holding.
|
|
145
|
+
try {
|
|
146
|
+
const refreshed = await listClockTasks(sessionId, clockConfig);
|
|
147
|
+
const refreshedNext = findNextUndeliveredDueAtMs(refreshed, nowMs());
|
|
148
|
+
if (!refreshedNext) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// ignore refresh errors; keep holding
|
|
154
|
+
}
|
|
177
155
|
}
|
|
178
156
|
}
|
|
179
157
|
return {
|
|
@@ -191,10 +169,15 @@ const handler = async (ctx) => {
|
|
|
191
169
|
{ op: 'append_user_text', text: 'continue' }
|
|
192
170
|
]
|
|
193
171
|
},
|
|
194
|
-
metadata:
|
|
172
|
+
metadata: {
|
|
173
|
+
...(connectionState ? { clientConnectionState: connectionState } : {}),
|
|
174
|
+
__rt: {
|
|
175
|
+
clockFollowupInjectReminders: true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
195
178
|
}
|
|
196
179
|
}
|
|
197
180
|
})
|
|
198
181
|
};
|
|
199
182
|
};
|
|
200
|
-
registerServerToolHandler('clock_auto', handler, { trigger: 'auto' });
|
|
183
|
+
registerServerToolHandler('clock_auto', handler, { trigger: 'auto', hook: { phase: 'post', priority: 50 } });
|
|
@@ -2,11 +2,27 @@ import { registerServerToolHandler } from '../registry.js';
|
|
|
2
2
|
import { cloneJson } from '../server-side-tools.js';
|
|
3
3
|
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
4
4
|
import { ensureRuntimeMetadata, readRuntimeMetadata } from '../../conversion/shared/runtime-metadata.js';
|
|
5
|
-
import { cancelClockTask, clearClockTasks, listClockTasks, resolveClockConfig, parseDueAtMs, scheduleClockTasks, startClockDaemonIfNeeded } from '../clock/task-store.js';
|
|
5
|
+
import { cancelClockTask, clearClockTasks, listClockTasks, resolveClockConfig, parseDueAtMs, scheduleClockTasks, startClockDaemonIfNeeded, updateClockTask } from '../clock/task-store.js';
|
|
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
9
|
const FLOW_ID = 'clock_flow';
|
|
10
|
+
let fallbackClockToolCallSeq = 0;
|
|
11
|
+
function ensureClockToolCall(toolCall, requestId) {
|
|
12
|
+
if (!toolCall || toolCall.name !== 'clock') {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const existingId = typeof toolCall.id === 'string' ? toolCall.id.trim() : '';
|
|
16
|
+
if (existingId) {
|
|
17
|
+
return toolCall;
|
|
18
|
+
}
|
|
19
|
+
fallbackClockToolCallSeq += 1;
|
|
20
|
+
const reqToken = String(requestId || 'req').replace(/[^a-zA-Z0-9_-]+/g, '_') || 'req';
|
|
21
|
+
return {
|
|
22
|
+
...toolCall,
|
|
23
|
+
id: `call_clock_fallback_${reqToken}_${fallbackClockToolCallSeq}`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
10
26
|
function extractAssistantMessageFromChatLike(chatResponse) {
|
|
11
27
|
if (!chatResponse || typeof chatResponse !== 'object') {
|
|
12
28
|
return null;
|
|
@@ -112,7 +128,7 @@ function asPlainObject(value) {
|
|
|
112
128
|
}
|
|
113
129
|
function normalizeAction(value) {
|
|
114
130
|
const raw = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
115
|
-
if (raw === 'get' || raw === 'list' || raw === 'cancel' || raw === 'clear' || raw === 'schedule') {
|
|
131
|
+
if (raw === 'get' || raw === 'list' || raw === 'cancel' || raw === 'clear' || raw === 'schedule' || raw === 'update') {
|
|
116
132
|
return raw;
|
|
117
133
|
}
|
|
118
134
|
return 'schedule';
|
|
@@ -125,6 +141,58 @@ function toIso(ms) {
|
|
|
125
141
|
return new Date(0).toISOString();
|
|
126
142
|
}
|
|
127
143
|
}
|
|
144
|
+
function parseRecurrenceKind(raw) {
|
|
145
|
+
const value = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
|
|
146
|
+
if (!value) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
if (value === 'daily' || value === 'day') {
|
|
150
|
+
return 'daily';
|
|
151
|
+
}
|
|
152
|
+
if (value === 'weekly' || value === 'week') {
|
|
153
|
+
return 'weekly';
|
|
154
|
+
}
|
|
155
|
+
if (value === 'interval' || value === 'every_minutes' || value === 'every-minutes' || value === 'everyminutes') {
|
|
156
|
+
return 'interval';
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
function parseRecurrenceFromRecord(record) {
|
|
161
|
+
const recurrenceRaw = record.recurrence ?? record.repeat ?? record.cycle;
|
|
162
|
+
const maxRunsRaw = Number(record.maxRuns ?? (recurrenceRaw && typeof recurrenceRaw === 'object' && !Array.isArray(recurrenceRaw)
|
|
163
|
+
? recurrenceRaw.maxRuns
|
|
164
|
+
: undefined));
|
|
165
|
+
if (recurrenceRaw === undefined || recurrenceRaw === null || recurrenceRaw === false) {
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
let kind;
|
|
169
|
+
let everyMinutesRaw;
|
|
170
|
+
if (typeof recurrenceRaw === 'string') {
|
|
171
|
+
kind = parseRecurrenceKind(recurrenceRaw);
|
|
172
|
+
everyMinutesRaw = record.everyMinutes;
|
|
173
|
+
}
|
|
174
|
+
else if (recurrenceRaw && typeof recurrenceRaw === 'object' && !Array.isArray(recurrenceRaw)) {
|
|
175
|
+
const recurrenceRecord = recurrenceRaw;
|
|
176
|
+
kind = parseRecurrenceKind(recurrenceRecord.kind ?? recurrenceRecord.type ?? recurrenceRecord.mode ?? recurrenceRecord.every);
|
|
177
|
+
everyMinutesRaw = recurrenceRecord.everyMinutes ?? recurrenceRecord.minutes ?? record.everyMinutes;
|
|
178
|
+
}
|
|
179
|
+
if (!kind) {
|
|
180
|
+
return { message: 'clock.schedule recurrence kind must be daily|weekly|interval' };
|
|
181
|
+
}
|
|
182
|
+
const maxRuns = Number.isFinite(maxRunsRaw) ? Math.floor(maxRunsRaw) : NaN;
|
|
183
|
+
if (!Number.isFinite(maxRuns) || maxRuns <= 0) {
|
|
184
|
+
return { message: 'clock.schedule recurring task requires maxRuns >= 1' };
|
|
185
|
+
}
|
|
186
|
+
if (kind === 'interval') {
|
|
187
|
+
const everyMinutesNum = Number(everyMinutesRaw);
|
|
188
|
+
const everyMinutes = Number.isFinite(everyMinutesNum) ? Math.floor(everyMinutesNum) : NaN;
|
|
189
|
+
if (!Number.isFinite(everyMinutes) || everyMinutes <= 0) {
|
|
190
|
+
return { message: 'clock.schedule interval recurrence requires everyMinutes >= 1' };
|
|
191
|
+
}
|
|
192
|
+
return { recurrence: { kind: 'interval', maxRuns, everyMinutes } };
|
|
193
|
+
}
|
|
194
|
+
return { recurrence: { kind, maxRuns } };
|
|
195
|
+
}
|
|
128
196
|
function normalizeScheduleItems(parsed) {
|
|
129
197
|
const itemsRaw = parsed.items;
|
|
130
198
|
if (!Array.isArray(itemsRaw) || itemsRaw.length === 0) {
|
|
@@ -161,16 +229,33 @@ function normalizeScheduleItems(parsed) {
|
|
|
161
229
|
}
|
|
162
230
|
return undefined;
|
|
163
231
|
})();
|
|
232
|
+
const recurrenceParsed = parseRecurrenceFromRecord(rec);
|
|
233
|
+
if (recurrenceParsed.message) {
|
|
234
|
+
return { ok: false, items: [], message: recurrenceParsed.message };
|
|
235
|
+
}
|
|
164
236
|
items.push({
|
|
165
237
|
dueAtMs,
|
|
166
238
|
task,
|
|
167
239
|
...(tool ? { tool } : {}),
|
|
168
|
-
...(argsObj ? { arguments: argsObj } : {})
|
|
240
|
+
...(argsObj ? { arguments: argsObj } : {}),
|
|
241
|
+
...(recurrenceParsed.recurrence ? { recurrence: recurrenceParsed.recurrence } : {})
|
|
169
242
|
});
|
|
170
243
|
}
|
|
171
244
|
return { ok: true, items };
|
|
172
245
|
}
|
|
173
246
|
function mapTaskForTool(t) {
|
|
247
|
+
const recurrence = t.recurrence && typeof t.recurrence === 'object'
|
|
248
|
+
? {
|
|
249
|
+
kind: t.recurrence.kind,
|
|
250
|
+
maxRuns: t.recurrence.maxRuns,
|
|
251
|
+
...(t.recurrence.kind === 'interval' && typeof t.recurrence.everyMinutes === 'number'
|
|
252
|
+
? { everyMinutes: t.recurrence.everyMinutes }
|
|
253
|
+
: {})
|
|
254
|
+
}
|
|
255
|
+
: undefined;
|
|
256
|
+
const remainingRuns = recurrence
|
|
257
|
+
? Math.max(0, Math.floor(Number(recurrence.maxRuns) || 0) - Math.max(0, Math.floor(Number(t.deliveryCount) || 0)))
|
|
258
|
+
: undefined;
|
|
174
259
|
return {
|
|
175
260
|
taskId: t.taskId,
|
|
176
261
|
dueAt: toIso(t.dueAtMs),
|
|
@@ -178,12 +263,14 @@ function mapTaskForTool(t) {
|
|
|
178
263
|
...(t.tool ? { tool: t.tool } : {}),
|
|
179
264
|
...(t.arguments ? { arguments: t.arguments } : {}),
|
|
180
265
|
...(typeof t.deliveredAtMs === 'number' ? { deliveredAt: toIso(t.deliveredAtMs) } : {}),
|
|
181
|
-
deliveryCount: t.deliveryCount
|
|
266
|
+
deliveryCount: t.deliveryCount,
|
|
267
|
+
...(recurrence ? { recurrence } : {}),
|
|
268
|
+
...(typeof remainingRuns === 'number' ? { remainingRuns } : {})
|
|
182
269
|
};
|
|
183
270
|
}
|
|
184
271
|
const handler = async (ctx) => {
|
|
185
|
-
const toolCall = ctx.toolCall;
|
|
186
|
-
if (!toolCall
|
|
272
|
+
const toolCall = ensureClockToolCall(ctx.toolCall, ctx.requestId);
|
|
273
|
+
if (!toolCall) {
|
|
187
274
|
return null;
|
|
188
275
|
}
|
|
189
276
|
const rt = readRuntimeMetadata(ctx.adapterContext);
|
|
@@ -324,6 +411,34 @@ const handler = async (ctx) => {
|
|
|
324
411
|
logClock('cancel', { sessionId, taskId, removed });
|
|
325
412
|
return respond({ ok: true, action, removed: removed ? taskId : null });
|
|
326
413
|
}
|
|
414
|
+
if (action === 'update') {
|
|
415
|
+
const taskId = typeof parsedArgs.taskId === 'string' ? parsedArgs.taskId.trim() : '';
|
|
416
|
+
if (!taskId) {
|
|
417
|
+
logClock('update_invalid', { sessionId, action, reason: 'missing_task_id' });
|
|
418
|
+
return respond({ ok: false, action, message: 'clock.update requires taskId' });
|
|
419
|
+
}
|
|
420
|
+
const normalized = normalizeScheduleItems(parsedArgs);
|
|
421
|
+
if (!normalized.ok || normalized.items.length < 1) {
|
|
422
|
+
logClock('update_invalid', { sessionId, action, message: normalized.message ?? 'invalid update payload' });
|
|
423
|
+
return respond({ ok: false, action, message: normalized.message ?? 'clock.update requires items[0] with dueAt/task' });
|
|
424
|
+
}
|
|
425
|
+
const item = normalized.items[0];
|
|
426
|
+
const patch = {
|
|
427
|
+
dueAtMs: item.dueAtMs,
|
|
428
|
+
task: item.task,
|
|
429
|
+
resetDelivery: true,
|
|
430
|
+
...(item.tool ? { tool: item.tool } : {}),
|
|
431
|
+
...(item.arguments ? { arguments: item.arguments } : {}),
|
|
432
|
+
...(item.recurrence ? { recurrence: item.recurrence } : {})
|
|
433
|
+
};
|
|
434
|
+
const updated = await updateClockTask(sessionId, taskId, patch, clockConfig);
|
|
435
|
+
if (!updated) {
|
|
436
|
+
logClock('update_miss', { sessionId, taskId });
|
|
437
|
+
return respond({ ok: false, action, message: 'clock.update failed: task not found or patch invalid' });
|
|
438
|
+
}
|
|
439
|
+
logClock('update', { sessionId, taskId });
|
|
440
|
+
return respond({ ok: true, action, updated: mapTaskForTool(updated) });
|
|
441
|
+
}
|
|
327
442
|
const normalized = normalizeScheduleItems(parsedArgs);
|
|
328
443
|
if (!normalized.ok) {
|
|
329
444
|
logClock('schedule_invalid', { sessionId, message: normalized.message ?? 'invalid schedule items' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
+
import { cloneJson } from '../server-side-tools.js';
|
|
3
|
+
const FLOW_ID = 'continue_execution_flow';
|
|
4
|
+
const TOOL_NAME = 'continue_execution';
|
|
5
|
+
function parseToolArguments(toolCall) {
|
|
6
|
+
if (!toolCall.arguments || typeof toolCall.arguments !== 'string') {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(toolCall.arguments);
|
|
11
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function injectToolOutput(base, toolCall, content) {
|
|
18
|
+
const cloned = cloneJson(base);
|
|
19
|
+
const existingOutputs = Array.isArray(cloned.tool_outputs)
|
|
20
|
+
? cloned.tool_outputs
|
|
21
|
+
: [];
|
|
22
|
+
let payloadText;
|
|
23
|
+
if (typeof content === 'string') {
|
|
24
|
+
payloadText = content;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
try {
|
|
28
|
+
payloadText = JSON.stringify(content ?? {});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
payloadText = String(content ?? '');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
cloned.tool_outputs = [
|
|
35
|
+
...existingOutputs,
|
|
36
|
+
{
|
|
37
|
+
tool_call_id: toolCall.id,
|
|
38
|
+
name: TOOL_NAME,
|
|
39
|
+
content: payloadText
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
return cloned;
|
|
43
|
+
}
|
|
44
|
+
function normalizeReason(parsed) {
|
|
45
|
+
const candidate = (typeof parsed.reason === 'string' ? parsed.reason : '') ||
|
|
46
|
+
(typeof parsed.note === 'string' ? parsed.note : '') ||
|
|
47
|
+
(typeof parsed.message === 'string' ? parsed.message : '');
|
|
48
|
+
return candidate.trim();
|
|
49
|
+
}
|
|
50
|
+
const handler = async (ctx) => {
|
|
51
|
+
const toolCall = ctx.toolCall;
|
|
52
|
+
if (!toolCall || toolCall.name !== TOOL_NAME) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const parsed = parseToolArguments(toolCall);
|
|
56
|
+
const reason = normalizeReason(parsed);
|
|
57
|
+
const followupText = '继续执行当前任务,不要中断到总结/汇报。仅在任务真正完成后再给最终总结。该工具用于避免打断会话并提高执行效率。';
|
|
58
|
+
return {
|
|
59
|
+
flowId: FLOW_ID,
|
|
60
|
+
finalize: async () => {
|
|
61
|
+
const patched = injectToolOutput(ctx.base, toolCall, {
|
|
62
|
+
ok: true,
|
|
63
|
+
executed: true,
|
|
64
|
+
noop: true,
|
|
65
|
+
action: 'continue_execution',
|
|
66
|
+
...(reason ? { reason } : {}),
|
|
67
|
+
message: 'No-op acknowledged. Continue execution without pausing for status summaries or ending turn early. If you were about to stop/end_turn, emit continue_execution first; server auto-followup keeps execution moving.'
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
chatResponse: patched,
|
|
71
|
+
execution: {
|
|
72
|
+
flowId: FLOW_ID,
|
|
73
|
+
followup: {
|
|
74
|
+
requestIdSuffix: ':continue_execution_followup',
|
|
75
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
76
|
+
injection: {
|
|
77
|
+
ops: [
|
|
78
|
+
{ op: 'preserve_tools' },
|
|
79
|
+
{ op: 'ensure_standard_tools' },
|
|
80
|
+
{ op: 'append_assistant_message', required: true },
|
|
81
|
+
{ op: 'append_tool_messages_from_tool_outputs', required: true },
|
|
82
|
+
{ op: 'append_user_text', text: followupText }
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
registerServerToolHandler(TOOL_NAME, handler);
|
|
@@ -327,6 +327,19 @@ function buildStandardFollowupTools() {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
},
|
|
330
|
+
{
|
|
331
|
+
type: 'function',
|
|
332
|
+
function: {
|
|
333
|
+
name: 'continue_execution',
|
|
334
|
+
description: 'No-op control tool. Use this before summaries or before ending the turn. If you are about to return stop/end_turn, emit continue_execution first instead of ending directly. Keep it lightweight; server auto-followup continues execution.',
|
|
335
|
+
parameters: {
|
|
336
|
+
type: 'object',
|
|
337
|
+
properties: {},
|
|
338
|
+
required: [],
|
|
339
|
+
additionalProperties: false
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
330
343
|
{
|
|
331
344
|
type: 'function',
|
|
332
345
|
function: {
|
|
@@ -99,7 +99,7 @@ const handler = async (ctx) => {
|
|
|
99
99
|
})
|
|
100
100
|
};
|
|
101
101
|
};
|
|
102
|
-
registerServerToolHandler('empty_reply_continue', handler, { trigger: 'auto' });
|
|
102
|
+
registerServerToolHandler('empty_reply_continue', handler, { trigger: 'auto', hook: { phase: 'default', priority: 20 } });
|
|
103
103
|
function decideEmptyReply(base) {
|
|
104
104
|
// 1) OpenAI Chat shape
|
|
105
105
|
const choices = Array.isArray(base.choices) ? base.choices : [];
|
|
@@ -69,7 +69,7 @@ const handler = async (ctx) => {
|
|
|
69
69
|
})
|
|
70
70
|
};
|
|
71
71
|
};
|
|
72
|
-
registerServerToolHandler('iflow_model_error_retry', handler, { trigger: 'auto' });
|
|
72
|
+
registerServerToolHandler('iflow_model_error_retry', handler, { trigger: 'auto', hook: { phase: 'pre', priority: 10 } });
|
|
73
73
|
function getCapturedRequest(adapterContext) {
|
|
74
74
|
if (!adapterContext || typeof adapterContext !== 'object') {
|
|
75
75
|
return null;
|
|
@@ -332,4 +332,4 @@ const handler = async (ctx) => {
|
|
|
332
332
|
sessionStates.set(sessionKey, state);
|
|
333
333
|
return null;
|
|
334
334
|
};
|
|
335
|
-
registerServerToolHandler('recursive_detection_guard', handler, { trigger: 'auto' });
|
|
335
|
+
registerServerToolHandler('recursive_detection_guard', handler, { trigger: 'auto', hook: { phase: 'pre', priority: 5 } });
|