@jsonstudio/llms 0.6.938 → 0.6.1164
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/hub/operation-table/operation-table-runner.d.ts +18 -0
- package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
- package/dist/conversion/hub/ops/operations.d.ts +19 -0
- package/dist/conversion/hub/ops/operations.js +126 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +533 -24
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
- package/dist/conversion/hub/policy/policy-engine.js +41 -9
- package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
- package/dist/conversion/hub/policy/protocol-spec.js +73 -23
- package/dist/conversion/hub/process/chat-process.js +252 -41
- package/dist/conversion/hub/response/provider-response.js +175 -2
- package/dist/conversion/hub/response/response-runtime.js +1 -1
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
- package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -436
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -894
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
- package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
- package/dist/conversion/responses/responses-openai-bridge.js +14 -2
- package/dist/conversion/shared/bridge-message-utils.js +2 -8
- package/dist/conversion/shared/bridge-policies.js +5 -105
- package/dist/conversion/shared/gemini-tool-utils.js +121 -4
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
- package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
- package/dist/conversion/shared/snapshot-hooks.js +166 -3
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +345 -9
- package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
- package/dist/conversion/shared/thought-signature-validator.js +170 -0
- package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
- package/dist/conversion/shared/tool-argument-repairer.js +56 -0
- package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
- package/dist/conversion/shared/tool-call-id-manager.js +231 -0
- package/dist/conversion/shared/tool-canonicalizer.js +2 -11
- package/dist/router/virtual-router/bootstrap.js +54 -5
- package/dist/router/virtual-router/engine-selection.js +132 -42
- package/dist/router/virtual-router/engine.d.ts +3 -0
- package/dist/router/virtual-router/engine.js +142 -33
- package/dist/router/virtual-router/health-weighted.d.ts +25 -0
- package/dist/router/virtual-router/health-weighted.js +63 -0
- package/dist/router/virtual-router/load-balancer.d.ts +2 -0
- package/dist/router/virtual-router/load-balancer.js +45 -16
- package/dist/router/virtual-router/routing-instructions.js +17 -1
- package/dist/router/virtual-router/sticky-session-store.js +136 -24
- package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
- package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
- package/dist/router/virtual-router/types.d.ts +70 -0
- package/dist/servertool/clock/config.d.ts +7 -0
- package/dist/servertool/clock/config.js +27 -0
- package/dist/servertool/clock/daemon.d.ts +3 -0
- package/dist/servertool/clock/daemon.js +79 -0
- package/dist/servertool/clock/io.d.ts +2 -0
- package/dist/servertool/clock/io.js +13 -0
- package/dist/servertool/clock/paths.d.ts +4 -0
- package/dist/servertool/clock/paths.js +25 -0
- package/dist/servertool/clock/session-store.d.ts +3 -0
- package/dist/servertool/clock/session-store.js +56 -0
- package/dist/servertool/clock/state.d.ts +5 -0
- package/dist/servertool/clock/state.js +62 -0
- package/dist/servertool/clock/task-store.d.ts +5 -0
- package/dist/servertool/clock/task-store.js +4 -0
- package/dist/servertool/clock/tasks.d.ts +17 -0
- package/dist/servertool/clock/tasks.js +221 -0
- package/dist/servertool/clock/types.d.ts +36 -0
- package/dist/servertool/clock/types.js +1 -0
- package/dist/servertool/engine.d.ts +2 -0
- package/dist/servertool/engine.js +164 -8
- package/dist/servertool/followup-shadow.d.ts +16 -0
- package/dist/servertool/followup-shadow.js +145 -0
- package/dist/servertool/handlers/apply-patch-guard.js +1 -265
- package/dist/servertool/handlers/clock-auto.d.ts +1 -0
- package/dist/servertool/handlers/clock-auto.js +160 -0
- package/dist/servertool/handlers/clock.d.ts +1 -0
- package/dist/servertool/handlers/clock.js +197 -0
- package/dist/servertool/handlers/exec-command-guard.js +7 -555
- package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
- package/dist/servertool/handlers/followup-request-builder.js +248 -28
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
- package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
- package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
- package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
- package/dist/servertool/handlers/stop-message-auto.js +47 -175
- package/dist/servertool/handlers/vision.d.ts +7 -1
- package/dist/servertool/handlers/vision.js +61 -117
- package/dist/servertool/handlers/web-search.d.ts +7 -1
- package/dist/servertool/handlers/web-search.js +122 -105
- package/dist/servertool/reenter-backend.d.ts +23 -0
- package/dist/servertool/reenter-backend.js +18 -0
- package/dist/servertool/server-side-tools.d.ts +3 -2
- package/dist/servertool/server-side-tools.js +64 -10
- package/dist/servertool/types.d.ts +92 -3
- package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
- package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
- package/dist/sse/shared/writer.js +24 -7
- package/dist/tools/apply-patch/execution-capturer.js +3 -1
- package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
- package/dist/tools/apply-patch/json/parse-loose.js +139 -0
- package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
- package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
- package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
- package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
- package/dist/tools/apply-patch/structured/coercion.js +82 -0
- package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
- package/dist/tools/apply-patch/validation/shared.js +6 -0
- package/dist/tools/apply-patch/validator.d.ts +2 -2
- package/dist/tools/apply-patch/validator.js +6 -556
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
3
|
+
import { findNextUndeliveredDueAtMs, listClockTasks, normalizeClockConfig, startClockDaemonIfNeeded } from '../clock/task-store.js';
|
|
4
|
+
const FLOW_ID = 'clock_hold_flow';
|
|
5
|
+
function resolveClientConnectionState(value) {
|
|
6
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const record = value;
|
|
10
|
+
if (typeof record.disconnected !== 'boolean') {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return { disconnected: record.disconnected };
|
|
14
|
+
}
|
|
15
|
+
function isStopFinishReason(base) {
|
|
16
|
+
if (!base || typeof base !== 'object' || Array.isArray(base)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const payload = base;
|
|
20
|
+
const choicesRaw = payload.choices;
|
|
21
|
+
if (Array.isArray(choicesRaw) && choicesRaw.length) {
|
|
22
|
+
const first = choicesRaw[0];
|
|
23
|
+
if (!first || typeof first !== 'object' || Array.isArray(first)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const finishReasonRaw = first.finish_reason;
|
|
27
|
+
const finishReason = typeof finishReasonRaw === 'string' && finishReasonRaw.trim()
|
|
28
|
+
? finishReasonRaw.trim().toLowerCase()
|
|
29
|
+
: '';
|
|
30
|
+
if (!finishReason || finishReason === 'tool_calls') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (finishReason !== 'stop' && finishReason !== 'length') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const message = first.message &&
|
|
37
|
+
typeof first.message === 'object' &&
|
|
38
|
+
!Array.isArray(first.message)
|
|
39
|
+
? first.message
|
|
40
|
+
: null;
|
|
41
|
+
if (!message) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
45
|
+
if (toolCalls.length > 0) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const statusRaw = typeof payload.status === 'string' ? payload.status.trim().toLowerCase() : '';
|
|
51
|
+
if (statusRaw && statusRaw !== 'completed') {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (payload.required_action && typeof payload.required_action === 'object') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
function resolveSessionId(adapterContext) {
|
|
60
|
+
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const sessionId = typeof adapterContext.sessionId === 'string' ? String(adapterContext.sessionId).trim() : '';
|
|
64
|
+
return sessionId || null;
|
|
65
|
+
}
|
|
66
|
+
function computeHoldSleepMs(remainingMs) {
|
|
67
|
+
if (remainingMs <= 0)
|
|
68
|
+
return 0;
|
|
69
|
+
if (remainingMs > 10 * 60_000)
|
|
70
|
+
return 30_000;
|
|
71
|
+
if (remainingMs > 60_000)
|
|
72
|
+
return 10_000;
|
|
73
|
+
if (remainingMs > 10_000)
|
|
74
|
+
return 1_000;
|
|
75
|
+
return 200;
|
|
76
|
+
}
|
|
77
|
+
async function sleep(ms) {
|
|
78
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
82
|
+
}
|
|
83
|
+
const handler = async (ctx) => {
|
|
84
|
+
const record = ctx.adapterContext;
|
|
85
|
+
// Only trigger on stop/length completion (no tool calls).
|
|
86
|
+
if (!isStopFinishReason(ctx.base)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// When client already disconnected, skip holding.
|
|
90
|
+
const connectionState = resolveClientConnectionState(record.clientConnectionState);
|
|
91
|
+
if (connectionState?.disconnected === true) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const clientDisconnectedRaw = record.clientDisconnected;
|
|
95
|
+
if (clientDisconnectedRaw === true ||
|
|
96
|
+
(typeof clientDisconnectedRaw === 'string' && clientDisconnectedRaw.trim().toLowerCase() === 'true')) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const clockConfig = normalizeClockConfig(record.clock);
|
|
100
|
+
if (!clockConfig) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
await startClockDaemonIfNeeded(clockConfig);
|
|
104
|
+
const sessionId = resolveSessionId(ctx.adapterContext);
|
|
105
|
+
if (!sessionId) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const seed = extractCapturedChatSeed(record.capturedChatRequest);
|
|
109
|
+
if (!seed) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const tasks = await listClockTasks(sessionId, clockConfig);
|
|
113
|
+
const at = Date.now();
|
|
114
|
+
const nextDueAtMs = findNextUndeliveredDueAtMs(tasks, at);
|
|
115
|
+
if (!nextDueAtMs) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
// Wait until the "due window" is reached (now >= dueAt - dueWindowMs).
|
|
119
|
+
const thresholdMs = nextDueAtMs - clockConfig.dueWindowMs;
|
|
120
|
+
while (Date.now() < thresholdMs) {
|
|
121
|
+
const state = resolveClientConnectionState(ctx.adapterContext.clientConnectionState);
|
|
122
|
+
if (state?.disconnected === true) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const remaining = thresholdMs - Date.now();
|
|
126
|
+
await sleep(computeHoldSleepMs(remaining));
|
|
127
|
+
// Best-effort: if tasks were cleared/cancelled while holding, stop holding.
|
|
128
|
+
try {
|
|
129
|
+
const refreshed = await listClockTasks(sessionId, clockConfig);
|
|
130
|
+
const refreshedNext = findNextUndeliveredDueAtMs(refreshed, Date.now());
|
|
131
|
+
if (!refreshedNext) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// ignore refresh errors; keep holding
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
flowId: FLOW_ID,
|
|
141
|
+
finalize: async () => ({
|
|
142
|
+
chatResponse: ctx.base,
|
|
143
|
+
execution: {
|
|
144
|
+
flowId: FLOW_ID,
|
|
145
|
+
followup: {
|
|
146
|
+
requestIdSuffix: ':clock_hold_followup',
|
|
147
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
148
|
+
injection: {
|
|
149
|
+
ops: [
|
|
150
|
+
{ op: 'append_assistant_message', required: false },
|
|
151
|
+
{ op: 'append_user_text', text: 'continue' }
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
metadata: (connectionState ? { clientConnectionState: connectionState } : {})
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
registerServerToolHandler('clock_auto', handler, { trigger: 'auto' });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
+
import { cloneJson } from '../server-side-tools.js';
|
|
3
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
4
|
+
import { cancelClockTask, clearClockTasks, listClockTasks, normalizeClockConfig, parseDueAtMs, scheduleClockTasks, startClockDaemonIfNeeded } from '../clock/task-store.js';
|
|
5
|
+
const FLOW_ID = 'clock_flow';
|
|
6
|
+
function parseToolArguments(toolCall) {
|
|
7
|
+
if (!toolCall.arguments || typeof toolCall.arguments !== 'string') {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(toolCall.arguments);
|
|
12
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function resolveSessionId(adapterContext) {
|
|
19
|
+
if (!adapterContext || typeof adapterContext !== 'object' || Array.isArray(adapterContext)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const sessionId = typeof adapterContext.sessionId === 'string' ? String(adapterContext.sessionId).trim() : '';
|
|
23
|
+
return sessionId || null;
|
|
24
|
+
}
|
|
25
|
+
function injectClockToolOutput(base, toolCall, content) {
|
|
26
|
+
const cloned = cloneJson(base);
|
|
27
|
+
const existingOutputs = Array.isArray(cloned.tool_outputs)
|
|
28
|
+
? cloned.tool_outputs
|
|
29
|
+
: [];
|
|
30
|
+
let payloadText;
|
|
31
|
+
if (typeof content === 'string') {
|
|
32
|
+
payloadText = content;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
try {
|
|
36
|
+
payloadText = JSON.stringify(content ?? {});
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
payloadText = String(content ?? '');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
cloned.tool_outputs = [
|
|
43
|
+
...existingOutputs,
|
|
44
|
+
{
|
|
45
|
+
tool_call_id: toolCall.id,
|
|
46
|
+
name: 'clock',
|
|
47
|
+
content: payloadText
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
return cloned;
|
|
51
|
+
}
|
|
52
|
+
function asPlainObject(value) {
|
|
53
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function normalizeAction(value) {
|
|
59
|
+
const raw = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
60
|
+
if (raw === 'list' || raw === 'cancel' || raw === 'clear' || raw === 'schedule') {
|
|
61
|
+
return raw;
|
|
62
|
+
}
|
|
63
|
+
return 'schedule';
|
|
64
|
+
}
|
|
65
|
+
function toIso(ms) {
|
|
66
|
+
try {
|
|
67
|
+
return new Date(ms).toISOString();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return new Date(0).toISOString();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function normalizeScheduleItems(parsed) {
|
|
74
|
+
const itemsRaw = parsed.items;
|
|
75
|
+
if (!Array.isArray(itemsRaw) || itemsRaw.length === 0) {
|
|
76
|
+
return { ok: false, items: [], message: 'clock.schedule requires items: [{ dueAt, task, tool?, arguments? }]' };
|
|
77
|
+
}
|
|
78
|
+
const items = [];
|
|
79
|
+
for (const entry of itemsRaw) {
|
|
80
|
+
const rec = asPlainObject(entry);
|
|
81
|
+
if (!rec) {
|
|
82
|
+
return { ok: false, items: [], message: 'clock.schedule items must be objects' };
|
|
83
|
+
}
|
|
84
|
+
const dueAtMs = parseDueAtMs(rec.dueAt);
|
|
85
|
+
if (!dueAtMs) {
|
|
86
|
+
return { ok: false, items: [], message: 'clock.schedule dueAt must be an ISO8601 string' };
|
|
87
|
+
}
|
|
88
|
+
const task = typeof rec.task === 'string' ? rec.task.trim() : '';
|
|
89
|
+
if (!task) {
|
|
90
|
+
return { ok: false, items: [], message: 'clock.schedule task must be a non-empty string' };
|
|
91
|
+
}
|
|
92
|
+
const tool = typeof rec.tool === 'string' && rec.tool.trim().length ? rec.tool.trim() : undefined;
|
|
93
|
+
const argsObj = rec.arguments && typeof rec.arguments === 'object' && !Array.isArray(rec.arguments)
|
|
94
|
+
? cloneJson(rec.arguments)
|
|
95
|
+
: undefined;
|
|
96
|
+
items.push({
|
|
97
|
+
dueAtMs,
|
|
98
|
+
task,
|
|
99
|
+
...(tool ? { tool } : {}),
|
|
100
|
+
...(argsObj ? { arguments: argsObj } : {})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return { ok: true, items };
|
|
104
|
+
}
|
|
105
|
+
function mapTaskForTool(t) {
|
|
106
|
+
return {
|
|
107
|
+
taskId: t.taskId,
|
|
108
|
+
dueAt: toIso(t.dueAtMs),
|
|
109
|
+
task: t.task,
|
|
110
|
+
...(t.tool ? { tool: t.tool } : {}),
|
|
111
|
+
...(t.arguments ? { arguments: t.arguments } : {}),
|
|
112
|
+
...(typeof t.deliveredAtMs === 'number' ? { deliveredAt: toIso(t.deliveredAtMs) } : {}),
|
|
113
|
+
deliveryCount: t.deliveryCount
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const handler = async (ctx) => {
|
|
117
|
+
const toolCall = ctx.toolCall;
|
|
118
|
+
if (!toolCall || toolCall.name !== 'clock') {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const clockConfig = normalizeClockConfig(ctx.adapterContext.clock);
|
|
122
|
+
const sessionId = resolveSessionId(ctx.adapterContext);
|
|
123
|
+
const parsedArgs = parseToolArguments(toolCall);
|
|
124
|
+
const action = normalizeAction(parsedArgs.action);
|
|
125
|
+
const respond = (payload) => {
|
|
126
|
+
return {
|
|
127
|
+
flowId: FLOW_ID,
|
|
128
|
+
finalize: async () => {
|
|
129
|
+
const patched = injectClockToolOutput(ctx.base, toolCall, payload);
|
|
130
|
+
const seed = extractCapturedChatSeed(ctx.adapterContext?.capturedChatRequest);
|
|
131
|
+
const canFollowup = Boolean(seed);
|
|
132
|
+
return {
|
|
133
|
+
chatResponse: patched,
|
|
134
|
+
execution: {
|
|
135
|
+
flowId: FLOW_ID,
|
|
136
|
+
...(canFollowup
|
|
137
|
+
? {
|
|
138
|
+
followup: {
|
|
139
|
+
requestIdSuffix: ':clock_followup',
|
|
140
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
141
|
+
injection: {
|
|
142
|
+
ops: [
|
|
143
|
+
{ op: 'append_assistant_message', required: true },
|
|
144
|
+
{ op: 'append_tool_messages_from_tool_outputs', required: true }
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
: {})
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
if (!clockConfig) {
|
|
156
|
+
return respond({
|
|
157
|
+
ok: false,
|
|
158
|
+
action,
|
|
159
|
+
message: 'clock tool is not enabled (virtualrouter.clock.enabled=true required).'
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
await startClockDaemonIfNeeded(clockConfig);
|
|
163
|
+
if (!sessionId) {
|
|
164
|
+
return respond({
|
|
165
|
+
ok: false,
|
|
166
|
+
action,
|
|
167
|
+
message: 'clock requires sessionId (x-session-id header or metadata.sessionId).'
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (action === 'list') {
|
|
171
|
+
const items = await listClockTasks(sessionId, clockConfig);
|
|
172
|
+
return respond({ ok: true, action, items: items.map(mapTaskForTool) });
|
|
173
|
+
}
|
|
174
|
+
if (action === 'clear') {
|
|
175
|
+
const removedCount = await clearClockTasks(sessionId, clockConfig);
|
|
176
|
+
return respond({ ok: true, action, removedCount });
|
|
177
|
+
}
|
|
178
|
+
if (action === 'cancel') {
|
|
179
|
+
const taskId = typeof parsedArgs.taskId === 'string' ? parsedArgs.taskId.trim() : '';
|
|
180
|
+
if (!taskId) {
|
|
181
|
+
return respond({ ok: false, action, message: 'clock.cancel requires taskId' });
|
|
182
|
+
}
|
|
183
|
+
const removed = await cancelClockTask(sessionId, taskId, clockConfig);
|
|
184
|
+
return respond({ ok: true, action, removed: removed ? taskId : null });
|
|
185
|
+
}
|
|
186
|
+
const normalized = normalizeScheduleItems(parsedArgs);
|
|
187
|
+
if (!normalized.ok) {
|
|
188
|
+
return respond({ ok: false, action, message: normalized.message ?? 'invalid schedule items' });
|
|
189
|
+
}
|
|
190
|
+
const scheduled = await scheduleClockTasks(sessionId, normalized.items, clockConfig);
|
|
191
|
+
return respond({
|
|
192
|
+
ok: true,
|
|
193
|
+
action,
|
|
194
|
+
scheduled: scheduled.map(mapTaskForTool)
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
registerServerToolHandler('clock', handler);
|