@j0hanz/cortex-mcp 1.4.0 → 1.5.0
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/engine/config.d.ts +4 -4
- package/dist/engine/config.js +6 -4
- package/dist/engine/context.d.ts +0 -1
- package/dist/engine/context.js +0 -3
- package/dist/engine/events.d.ts +1 -1
- package/dist/engine/events.js +2 -3
- package/dist/engine/heuristics.d.ts +4 -0
- package/dist/engine/heuristics.js +65 -0
- package/dist/engine/reasoner.js +21 -97
- package/dist/engine/session-store.d.ts +4 -0
- package/dist/engine/session-store.js +54 -26
- package/dist/lib/concurrency.d.ts +5 -0
- package/dist/lib/concurrency.js +17 -0
- package/dist/lib/errors.d.ts +23 -7
- package/dist/lib/errors.js +47 -5
- package/dist/lib/formatting.d.ts +13 -0
- package/dist/lib/formatting.js +18 -0
- package/dist/lib/prompt-contracts.d.ts +1 -9
- package/dist/lib/prompt-contracts.js +33 -122
- package/dist/lib/tool-contracts.d.ts +1 -1
- package/dist/lib/tool-contracts.js +91 -90
- package/dist/lib/tool-response.d.ts +4 -0
- package/dist/lib/tool-response.js +3 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.js +8 -1
- package/dist/lib/validators.d.ts +7 -1
- package/dist/lib/validators.js +30 -7
- package/dist/prompts/index.js +132 -47
- package/dist/prompts/templates.d.ts +2 -0
- package/dist/prompts/templates.js +106 -0
- package/dist/resources/index.js +14 -60
- package/dist/resources/instructions.js +10 -2
- package/dist/resources/workflows.js +4 -3
- package/dist/schemas/inputs.js +2 -2
- package/dist/schemas/outputs.d.ts +29 -25
- package/dist/schemas/outputs.js +13 -13
- package/dist/server.js +1 -3
- package/dist/tools/reasoning-think.js +106 -149
- package/package.json +1 -1
package/dist/schemas/outputs.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { REASONING_LEVELS, SESSION_STATUSES } from '../lib/types.js';
|
|
2
3
|
const ErrorInfoSchema = z.strictObject({
|
|
3
4
|
code: z.string(),
|
|
4
5
|
message: z.string(),
|
|
5
6
|
});
|
|
6
7
|
const MISSING_RESULT_PATH = ['result'];
|
|
7
8
|
const MISSING_ERROR_PATH = ['error'];
|
|
8
|
-
const LEVEL_VALUES = ['basic', 'normal', 'high'];
|
|
9
|
-
const STATUS_VALUES = ['active', 'completed', 'cancelled'];
|
|
10
9
|
const ThoughtSchema = z.strictObject({
|
|
11
10
|
index: z.number(),
|
|
12
11
|
content: z.string(),
|
|
@@ -16,17 +15,12 @@ const ThoughtSchema = z.strictObject({
|
|
|
16
15
|
.optional()
|
|
17
16
|
.describe('A 1-sentence summary of the conclusion reached in this step, if provided.'),
|
|
18
17
|
});
|
|
19
|
-
export const DefaultOutputSchema = z.strictObject({
|
|
20
|
-
ok: z.boolean(),
|
|
21
|
-
result: z.unknown().optional(),
|
|
22
|
-
error: ErrorInfoSchema.optional(),
|
|
23
|
-
});
|
|
24
18
|
const ReasoningThinkSuccessSchema = z.strictObject({
|
|
25
19
|
ok: z.literal(true),
|
|
26
20
|
result: z.strictObject({
|
|
27
21
|
sessionId: z.string(),
|
|
28
|
-
level: z.enum(
|
|
29
|
-
status: z.enum(
|
|
22
|
+
level: z.enum(REASONING_LEVELS),
|
|
23
|
+
status: z.enum(SESSION_STATUSES),
|
|
30
24
|
thoughts: z.array(ThoughtSchema),
|
|
31
25
|
generatedThoughts: z.number(),
|
|
32
26
|
requestedThoughts: z.number(),
|
|
@@ -53,10 +47,6 @@ const ReasoningThinkErrorSchema = z.strictObject({
|
|
|
53
47
|
ok: z.literal(false),
|
|
54
48
|
error: ErrorInfoSchema,
|
|
55
49
|
});
|
|
56
|
-
export const ReasoningThinkResultSchema = z.discriminatedUnion('ok', [
|
|
57
|
-
ReasoningThinkSuccessSchema,
|
|
58
|
-
ReasoningThinkErrorSchema,
|
|
59
|
-
]);
|
|
60
50
|
function getMissingFieldIssue(data) {
|
|
61
51
|
if (data.ok && data.result === undefined) {
|
|
62
52
|
return {
|
|
@@ -92,3 +82,13 @@ export const ReasoningThinkToolOutputSchema = z
|
|
|
92
82
|
});
|
|
93
83
|
}
|
|
94
84
|
});
|
|
85
|
+
/** Generic ok/error envelope — useful for contract tests and external validators. */
|
|
86
|
+
export const DefaultOutputSchema = z.union([
|
|
87
|
+
z.strictObject({ ok: z.literal(true), result: z.unknown() }),
|
|
88
|
+
z.strictObject({ ok: z.literal(false), error: ErrorInfoSchema }),
|
|
89
|
+
]);
|
|
90
|
+
/** Full discriminated union for the reasoning_think tool result. */
|
|
91
|
+
export const ReasoningThinkResultSchema = z.union([
|
|
92
|
+
ReasoningThinkSuccessSchema,
|
|
93
|
+
ReasoningThinkErrorSchema,
|
|
94
|
+
]);
|
package/dist/server.js
CHANGED
|
@@ -7,7 +7,6 @@ import { getErrorMessage } from './lib/errors.js';
|
|
|
7
7
|
import { registerAllTools } from './tools/index.js';
|
|
8
8
|
import { registerAllPrompts } from './prompts/index.js';
|
|
9
9
|
import { registerAllResources } from './resources/index.js';
|
|
10
|
-
import { buildServerInstructions } from './resources/instructions.js';
|
|
11
10
|
const ICON_MIME = 'image/svg+xml';
|
|
12
11
|
const SERVER_NAME = 'cortex-mcp';
|
|
13
12
|
const SERVER_TITLE = 'Cortex MCP';
|
|
@@ -147,7 +146,6 @@ function installCloseCleanup(server, cleanup) {
|
|
|
147
146
|
};
|
|
148
147
|
}
|
|
149
148
|
export function createServer() {
|
|
150
|
-
const instructions = buildServerInstructions();
|
|
151
149
|
const version = loadVersion();
|
|
152
150
|
const taskStore = new InMemoryTaskStore();
|
|
153
151
|
const localIcon = getLocalIconData();
|
|
@@ -180,7 +178,7 @@ export function createServer() {
|
|
|
180
178
|
},
|
|
181
179
|
},
|
|
182
180
|
taskStore,
|
|
183
|
-
|
|
181
|
+
instructions: 'Multi-level reasoning MCP server. Use reasoning_think to decompose queries into sequential thought steps at basic (3–5), normal (6–10), or high (15–25) depth. Full usage guide: read internal://instructions or invoke get-help.',
|
|
184
182
|
});
|
|
185
183
|
registerAllTools(server, iconMeta);
|
|
186
184
|
registerAllPrompts(server, iconMeta);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getLevelDescriptionString } from '../engine/config.js';
|
|
2
2
|
import { reason, sessionStore } from '../engine/reasoner.js';
|
|
3
3
|
import { ReasoningThinkInputSchema, } from '../schemas/inputs.js';
|
|
4
|
-
import { ReasoningThinkToolOutputSchema } from '../schemas/outputs.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { ReasoningThinkToolOutputSchema, } from '../schemas/outputs.js';
|
|
5
|
+
import { createTaskLimiter } from '../lib/concurrency.js';
|
|
6
|
+
import { createErrorResponse, getErrorMessage, InsufficientThoughtsError, InvalidRunModeArgsError, isObjectRecord, ReasoningAbortedError, ReasoningError, ServerBusyError, SessionNotFoundError, } from '../lib/errors.js';
|
|
7
|
+
import { formatProgressMessage, formatThoughtsToMarkdown, } from '../lib/formatting.js';
|
|
8
|
+
import { createToolResponse, withIconMeta } from '../lib/tool-response.js';
|
|
9
|
+
import { parsePositiveIntEnv } from '../lib/validators.js';
|
|
8
10
|
const DEFAULT_MAX_ACTIVE_REASONING_TASKS = 32;
|
|
9
|
-
// Use explicit server busy error code for better client handling
|
|
10
|
-
const TASK_OVERLOAD_MESSAGE = 'Server busy: too many active reasoning tasks';
|
|
11
11
|
function buildTraceResource(session) {
|
|
12
12
|
return {
|
|
13
13
|
uri: `file:///cortex/sessions/${session.id}/trace.md`,
|
|
@@ -15,37 +15,7 @@ function buildTraceResource(session) {
|
|
|
15
15
|
text: formatThoughtsToMarkdown(session),
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
if (rawValue === undefined) {
|
|
20
|
-
return fallbackValue;
|
|
21
|
-
}
|
|
22
|
-
const parsed = Number.parseInt(rawValue, 10);
|
|
23
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
24
|
-
return fallbackValue;
|
|
25
|
-
}
|
|
26
|
-
return parsed;
|
|
27
|
-
}
|
|
28
|
-
function createTaskLimiter(maxActiveTasks) {
|
|
29
|
-
let activeTasks = 0;
|
|
30
|
-
return {
|
|
31
|
-
tryAcquire() {
|
|
32
|
-
if (activeTasks >= maxActiveTasks) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
activeTasks += 1;
|
|
36
|
-
return true;
|
|
37
|
-
},
|
|
38
|
-
release() {
|
|
39
|
-
if (activeTasks > 0) {
|
|
40
|
-
activeTasks -= 1;
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const reasoningTaskLimiter = createTaskLimiter(parsePositiveInt(process.env.CORTEX_MAX_ACTIVE_REASONING_TASKS, DEFAULT_MAX_ACTIVE_REASONING_TASKS));
|
|
46
|
-
function isObjectRecord(value) {
|
|
47
|
-
return typeof value === 'object' && value !== null;
|
|
48
|
-
}
|
|
18
|
+
const reasoningTaskLimiter = createTaskLimiter(parsePositiveIntEnv('CORTEX_MAX_ACTIVE_REASONING_TASKS', DEFAULT_MAX_ACTIVE_REASONING_TASKS));
|
|
49
19
|
function isTaskStoreLike(value) {
|
|
50
20
|
if (!isObjectRecord(value)) {
|
|
51
21
|
return false;
|
|
@@ -134,27 +104,11 @@ function assertCallToolResult(value) {
|
|
|
134
104
|
throw new Error('Stored task result is not a valid CallToolResult.');
|
|
135
105
|
}
|
|
136
106
|
}
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
case message === 'Reasoning task cancelled':
|
|
141
|
-
return 'E_ABORTED';
|
|
142
|
-
case message.startsWith('targetThoughts must be'):
|
|
143
|
-
case message.startsWith('Cannot change targetThoughts'):
|
|
144
|
-
return 'E_INVALID_THOUGHT_COUNT';
|
|
145
|
-
case message.startsWith('Session not found:'):
|
|
146
|
-
return 'E_SESSION_NOT_FOUND';
|
|
147
|
-
case message.startsWith('Session level mismatch:'):
|
|
148
|
-
return 'E_SESSION_LEVEL_MISMATCH';
|
|
149
|
-
case message.startsWith('run_to_completion requires at least'):
|
|
150
|
-
return 'E_INSUFFICIENT_THOUGHTS';
|
|
151
|
-
case message.startsWith('targetThoughts is required for run_to_completion when sessionId is not provided'):
|
|
152
|
-
return 'E_INVALID_RUN_MODE_ARGS';
|
|
153
|
-
case message === TASK_OVERLOAD_MESSAGE:
|
|
154
|
-
return 'E_SERVER_BUSY';
|
|
155
|
-
default:
|
|
156
|
-
return 'E_REASONING';
|
|
107
|
+
function getReasoningErrorCode(error) {
|
|
108
|
+
if (error instanceof ReasoningError) {
|
|
109
|
+
return error.code;
|
|
157
110
|
}
|
|
111
|
+
return 'E_REASONING';
|
|
158
112
|
}
|
|
159
113
|
function shouldEmitProgress(progress, total, level) {
|
|
160
114
|
if (progress <= 1 || progress >= total) {
|
|
@@ -167,8 +121,22 @@ function shouldEmitProgress(progress, total, level) {
|
|
|
167
121
|
// Basic/Normal: emit every step
|
|
168
122
|
return true;
|
|
169
123
|
}
|
|
170
|
-
function
|
|
171
|
-
|
|
124
|
+
async function notifyProgress(args) {
|
|
125
|
+
const { server, progressToken, progress, total, message } = args;
|
|
126
|
+
try {
|
|
127
|
+
await server.server.notification({
|
|
128
|
+
method: 'notifications/progress',
|
|
129
|
+
params: {
|
|
130
|
+
progressToken,
|
|
131
|
+
progress,
|
|
132
|
+
total,
|
|
133
|
+
message,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Ignore notification errors
|
|
139
|
+
}
|
|
172
140
|
}
|
|
173
141
|
function buildThoughtInputs(params) {
|
|
174
142
|
const primary = Array.isArray(params.thought)
|
|
@@ -203,6 +171,20 @@ async function executeReasoningSteps(args) {
|
|
|
203
171
|
rollbackToStep !== undefined)) {
|
|
204
172
|
maxSteps = 1;
|
|
205
173
|
}
|
|
174
|
+
// Build first-step-only extras once, outside the loop.
|
|
175
|
+
const firstStepExtras = {
|
|
176
|
+
...(observation !== undefined ? { observation } : {}),
|
|
177
|
+
...(hypothesis !== undefined ? { hypothesis } : {}),
|
|
178
|
+
...(evaluation !== undefined ? { evaluation } : {}),
|
|
179
|
+
...(stepSummary !== undefined ? { stepSummary } : {}),
|
|
180
|
+
...(isConclusion !== undefined ? { isConclusion } : {}),
|
|
181
|
+
...(rollbackToStep !== undefined ? { rollbackToStep } : {}),
|
|
182
|
+
};
|
|
183
|
+
const baseOptions = {
|
|
184
|
+
...(targetThoughts !== undefined ? { targetThoughts } : {}),
|
|
185
|
+
abortSignal: controller.signal,
|
|
186
|
+
onProgress,
|
|
187
|
+
};
|
|
206
188
|
for (let index = 0; index < maxSteps; index++) {
|
|
207
189
|
await ensureTaskIsActive(taskStore, taskId, controller);
|
|
208
190
|
const inputThought = thoughtInputs[index];
|
|
@@ -217,30 +199,11 @@ async function executeReasoningSteps(args) {
|
|
|
217
199
|
break;
|
|
218
200
|
}
|
|
219
201
|
const reasonOptions = {
|
|
202
|
+
...baseOptions,
|
|
220
203
|
...(inputThought !== undefined ? { thought: inputThought } : {}),
|
|
221
|
-
|
|
222
|
-
|
|
204
|
+
...(activeSessionId !== undefined ? { sessionId: activeSessionId } : {}),
|
|
205
|
+
...(index === 0 ? firstStepExtras : {}),
|
|
223
206
|
};
|
|
224
|
-
if (index === 0) {
|
|
225
|
-
if (observation !== undefined)
|
|
226
|
-
reasonOptions.observation = observation;
|
|
227
|
-
if (hypothesis !== undefined)
|
|
228
|
-
reasonOptions.hypothesis = hypothesis;
|
|
229
|
-
if (evaluation !== undefined)
|
|
230
|
-
reasonOptions.evaluation = evaluation;
|
|
231
|
-
if (stepSummary !== undefined)
|
|
232
|
-
reasonOptions.stepSummary = stepSummary;
|
|
233
|
-
if (isConclusion !== undefined)
|
|
234
|
-
reasonOptions.isConclusion = isConclusion;
|
|
235
|
-
if (rollbackToStep !== undefined)
|
|
236
|
-
reasonOptions.rollbackToStep = rollbackToStep;
|
|
237
|
-
}
|
|
238
|
-
if (activeSessionId !== undefined) {
|
|
239
|
-
reasonOptions.sessionId = activeSessionId;
|
|
240
|
-
}
|
|
241
|
-
if (targetThoughts !== undefined) {
|
|
242
|
-
reasonOptions.targetThoughts = targetThoughts;
|
|
243
|
-
}
|
|
244
207
|
session = await reason(queryText, level, reasonOptions);
|
|
245
208
|
activeSessionId = session.id;
|
|
246
209
|
if (shouldStopReasoningLoop(session, runMode)) {
|
|
@@ -263,7 +226,7 @@ function buildStructuredResult(session, generatedThoughts, targetThoughts) {
|
|
|
263
226
|
sessionId: session.id,
|
|
264
227
|
level: session.level,
|
|
265
228
|
status: session.status,
|
|
266
|
-
thoughts: session.thoughts,
|
|
229
|
+
thoughts: [...session.thoughts],
|
|
267
230
|
generatedThoughts,
|
|
268
231
|
requestedThoughts,
|
|
269
232
|
totalThoughts: session.totalThoughts,
|
|
@@ -280,10 +243,10 @@ function buildStructuredResult(session, generatedThoughts, targetThoughts) {
|
|
|
280
243
|
}
|
|
281
244
|
function buildSummary(session, remainingThoughts) {
|
|
282
245
|
if (session.status === 'completed') {
|
|
283
|
-
return `Reasoning complete
|
|
246
|
+
return `Reasoning complete — ${String(session.thoughts.length)} thoughts at [${session.level}] level. Session ${session.id}.`;
|
|
284
247
|
}
|
|
285
248
|
if (session.status === 'cancelled') {
|
|
286
|
-
return `
|
|
249
|
+
return `Reasoning cancelled at thought ${String(session.thoughts.length)}/${String(session.totalThoughts)}. Session ${session.id}.`;
|
|
287
250
|
}
|
|
288
251
|
const recentSummaries = session.thoughts
|
|
289
252
|
.filter((t) => t.stepSummary)
|
|
@@ -301,7 +264,7 @@ function buildSummary(session, remainingThoughts) {
|
|
|
301
264
|
else if (progress < 0.7) {
|
|
302
265
|
prompt = 'Formulate and critique hypotheses based on the facts.';
|
|
303
266
|
}
|
|
304
|
-
return (`CONTINUE: ${prompt} Call reasoning_think with { sessionId: "${session.id}",
|
|
267
|
+
return (`CONTINUE: ${prompt} Call reasoning_think with { sessionId: "${session.id}", thought: "<your next reasoning step>" }. ` +
|
|
305
268
|
`${summaryText}Progress: ${String(session.thoughts.length)}/${String(session.totalThoughts)} thoughts, ${String(remainingThoughts)} remaining.`);
|
|
306
269
|
}
|
|
307
270
|
async function emitLog(server, level, data, sessionId) {
|
|
@@ -343,12 +306,12 @@ async function isTaskCancelled(taskStore, taskId) {
|
|
|
343
306
|
async function ensureTaskIsActive(taskStore, taskId, controller) {
|
|
344
307
|
if (await isTaskCancelled(taskStore, taskId)) {
|
|
345
308
|
controller.abort();
|
|
346
|
-
throw new
|
|
309
|
+
throw new ReasoningAbortedError('Reasoning task cancelled');
|
|
347
310
|
}
|
|
348
311
|
}
|
|
349
312
|
function createProgressHandler(args) {
|
|
350
313
|
const { server, taskStore, taskId, level, progressToken, controller, startingCount, batchTotal, } = args;
|
|
351
|
-
return async (progress
|
|
314
|
+
return async (progress) => {
|
|
352
315
|
await ensureTaskIsActive(taskStore, taskId, controller);
|
|
353
316
|
if (progressToken === undefined) {
|
|
354
317
|
return;
|
|
@@ -359,26 +322,23 @@ function createProgressHandler(args) {
|
|
|
359
322
|
const isTerminal = displayProgress >= batchTotal;
|
|
360
323
|
// We must emit if it's the terminal update for this batch,
|
|
361
324
|
// otherwise we respect the session-level skipping rules.
|
|
362
|
-
if (!isTerminal &&
|
|
325
|
+
if (!isTerminal &&
|
|
326
|
+
!shouldEmitProgress(displayProgress, batchTotal, level)) {
|
|
363
327
|
return;
|
|
364
328
|
}
|
|
365
|
-
const message =
|
|
366
|
-
|
|
367
|
-
:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
catch {
|
|
380
|
-
// Ignore notification errors
|
|
381
|
-
}
|
|
329
|
+
const message = formatProgressMessage({
|
|
330
|
+
toolName: TOOL_NAME,
|
|
331
|
+
context: 'Thought',
|
|
332
|
+
metadata: `[${String(displayProgress)}/${String(batchTotal)}]`,
|
|
333
|
+
...(isTerminal ? { outcome: 'complete' } : {}),
|
|
334
|
+
});
|
|
335
|
+
await notifyProgress({
|
|
336
|
+
server,
|
|
337
|
+
progressToken,
|
|
338
|
+
progress: displayProgress,
|
|
339
|
+
total: batchTotal,
|
|
340
|
+
message,
|
|
341
|
+
});
|
|
382
342
|
};
|
|
383
343
|
}
|
|
384
344
|
async function storeTaskFailure(taskStore, taskId, response) {
|
|
@@ -410,31 +370,29 @@ async function notifyTaskStatus(server, taskId, status) {
|
|
|
410
370
|
}
|
|
411
371
|
function assertRunToCompletionInputCount(params, thoughtInputs) {
|
|
412
372
|
const { sessionId, targetThoughts } = params;
|
|
413
|
-
if (sessionId
|
|
414
|
-
throw new
|
|
373
|
+
if (!sessionId && !targetThoughts) {
|
|
374
|
+
throw new InvalidRunModeArgsError('targetThoughts is required for run_to_completion when sessionId is not provided');
|
|
415
375
|
}
|
|
416
376
|
let requiredInputs = targetThoughts ?? 0;
|
|
417
|
-
if (sessionId
|
|
377
|
+
if (sessionId) {
|
|
418
378
|
const existing = sessionStore.get(sessionId);
|
|
419
379
|
if (!existing) {
|
|
420
|
-
throw new
|
|
380
|
+
throw new SessionNotFoundError(sessionId);
|
|
421
381
|
}
|
|
422
382
|
requiredInputs = Math.max(0, existing.totalThoughts - existing.thoughts.length);
|
|
423
383
|
}
|
|
424
384
|
if (thoughtInputs.length < requiredInputs) {
|
|
425
|
-
throw new
|
|
385
|
+
throw new InsufficientThoughtsError(`run_to_completion requires at least ${String(requiredInputs)} thought inputs; received ${String(thoughtInputs.length)}`);
|
|
426
386
|
}
|
|
427
387
|
}
|
|
428
388
|
function getActionableMessage(errorCode, originalMessage) {
|
|
429
389
|
switch (errorCode) {
|
|
430
|
-
case 'E_SESSION_LEVEL_MISMATCH':
|
|
431
|
-
return `${originalMessage} ACTION REQUIRED: Re-call reasoning_think with the correct level matching the session.`;
|
|
432
390
|
case 'E_INVALID_THOUGHT_COUNT':
|
|
433
|
-
return `${originalMessage}
|
|
391
|
+
return `${originalMessage} Fix: set targetThoughts within the level range (basic 3–5, normal 6–10, high 15–25).`;
|
|
434
392
|
case 'E_INSUFFICIENT_THOUGHTS':
|
|
435
|
-
return `${originalMessage}
|
|
393
|
+
return `${originalMessage} Fix: provide enough thought inputs for the remaining steps, or use runMode: "step".`;
|
|
436
394
|
case 'E_INVALID_RUN_MODE_ARGS':
|
|
437
|
-
return `${originalMessage}
|
|
395
|
+
return `${originalMessage} Fix: set targetThoughts when starting a new session with runMode: "run_to_completion".`;
|
|
438
396
|
default:
|
|
439
397
|
return originalMessage;
|
|
440
398
|
}
|
|
@@ -442,10 +400,13 @@ function getActionableMessage(errorCode, originalMessage) {
|
|
|
442
400
|
async function handleTaskFailure(args) {
|
|
443
401
|
const { server, taskStore, taskId, sessionId, error } = args;
|
|
444
402
|
const originalMessage = getErrorMessage(error);
|
|
445
|
-
const errorCode =
|
|
403
|
+
const errorCode = getReasoningErrorCode(error);
|
|
446
404
|
const message = getActionableMessage(errorCode, originalMessage);
|
|
447
405
|
const response = createErrorResponse(errorCode, message);
|
|
448
406
|
if (await isTaskCancelled(taskStore, taskId)) {
|
|
407
|
+
if (sessionId) {
|
|
408
|
+
sessionStore.markCancelled(sessionId);
|
|
409
|
+
}
|
|
449
410
|
await emitLog(server, 'notice', { event: 'task_cancelled', taskId, reason: message }, sessionId);
|
|
450
411
|
return;
|
|
451
412
|
}
|
|
@@ -466,9 +427,10 @@ async function handleTaskFailure(args) {
|
|
|
466
427
|
async function runReasoningTask(args) {
|
|
467
428
|
const { server, taskStore, taskId, params, progressToken, controller, sessionId, } = args;
|
|
468
429
|
const { query, level, targetThoughts } = params;
|
|
469
|
-
const runMode =
|
|
430
|
+
const runMode = params.runMode ?? 'step';
|
|
470
431
|
const thoughtInputs = buildThoughtInputs(params);
|
|
471
432
|
const queryText = query ?? '';
|
|
433
|
+
let resolvedSessionId = params.sessionId ?? sessionId;
|
|
472
434
|
await emitLog(server, 'info', {
|
|
473
435
|
event: 'task_started',
|
|
474
436
|
taskId,
|
|
@@ -477,7 +439,7 @@ async function runReasoningTask(args) {
|
|
|
477
439
|
hasSessionId: params.sessionId !== undefined,
|
|
478
440
|
targetThoughts: targetThoughts ?? null,
|
|
479
441
|
thoughtInputs: thoughtInputs.length,
|
|
480
|
-
},
|
|
442
|
+
}, resolvedSessionId);
|
|
481
443
|
try {
|
|
482
444
|
if (runMode === 'run_to_completion') {
|
|
483
445
|
assertRunToCompletionInputCount(params, thoughtInputs);
|
|
@@ -492,23 +454,20 @@ async function runReasoningTask(args) {
|
|
|
492
454
|
params.rollback_to_step !== undefined)) {
|
|
493
455
|
batchTotal = 1;
|
|
494
456
|
}
|
|
457
|
+
const normalizedBatchTotal = Math.max(1, batchTotal);
|
|
495
458
|
if (progressToken !== undefined) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
catch {
|
|
510
|
-
// Ignore notification errors
|
|
511
|
-
}
|
|
459
|
+
const message = formatProgressMessage({
|
|
460
|
+
toolName: TOOL_NAME,
|
|
461
|
+
context: 'reasoning',
|
|
462
|
+
metadata: level ? `starting [${level}]` : 'continuing session',
|
|
463
|
+
});
|
|
464
|
+
await notifyProgress({
|
|
465
|
+
server,
|
|
466
|
+
progressToken,
|
|
467
|
+
progress: 0,
|
|
468
|
+
total: normalizedBatchTotal,
|
|
469
|
+
message,
|
|
470
|
+
});
|
|
512
471
|
}
|
|
513
472
|
const progressArgs = {
|
|
514
473
|
server,
|
|
@@ -517,7 +476,7 @@ async function runReasoningTask(args) {
|
|
|
517
476
|
level,
|
|
518
477
|
controller,
|
|
519
478
|
startingCount,
|
|
520
|
-
batchTotal:
|
|
479
|
+
batchTotal: normalizedBatchTotal,
|
|
521
480
|
};
|
|
522
481
|
if (progressToken !== undefined) {
|
|
523
482
|
progressArgs.progressToken = progressToken;
|
|
@@ -552,8 +511,10 @@ async function runReasoningTask(args) {
|
|
|
552
511
|
if (params.rollback_to_step !== undefined)
|
|
553
512
|
executeArgs.rollbackToStep = params.rollback_to_step;
|
|
554
513
|
const session = await executeReasoningSteps(executeArgs);
|
|
514
|
+
resolvedSessionId = session.id;
|
|
555
515
|
if (await isTaskCancelled(taskStore, taskId)) {
|
|
556
|
-
|
|
516
|
+
sessionStore.markCancelled(resolvedSessionId);
|
|
517
|
+
await emitLog(server, 'notice', { event: 'task_cancelled_before_result', taskId }, resolvedSessionId);
|
|
557
518
|
return;
|
|
558
519
|
}
|
|
559
520
|
const generatedThoughts = Math.max(0, session.thoughts.length - startingCount);
|
|
@@ -575,8 +536,8 @@ async function runReasoningTask(args) {
|
|
|
575
536
|
taskId,
|
|
576
537
|
error,
|
|
577
538
|
};
|
|
578
|
-
if (
|
|
579
|
-
failureArgs.sessionId =
|
|
539
|
+
if (resolvedSessionId !== undefined) {
|
|
540
|
+
failureArgs.sessionId = resolvedSessionId;
|
|
580
541
|
}
|
|
581
542
|
await handleTaskFailure(failureArgs);
|
|
582
543
|
}
|
|
@@ -588,9 +549,6 @@ function getTaskId(extra) {
|
|
|
588
549
|
return extra.taskId;
|
|
589
550
|
}
|
|
590
551
|
const TOOL_NAME = 'reasoning_think';
|
|
591
|
-
function withIconMeta(iconMeta) {
|
|
592
|
-
return iconMeta ? { icons: [iconMeta] } : undefined;
|
|
593
|
-
}
|
|
594
552
|
export function registerReasoningThinkTool(server, iconMeta) {
|
|
595
553
|
server.experimental.tasks.registerToolTask(TOOL_NAME, {
|
|
596
554
|
title: 'Reasoning Think',
|
|
@@ -598,17 +556,16 @@ export function registerReasoningThinkTool(server, iconMeta) {
|
|
|
598
556
|
|
|
599
557
|
USAGE PATTERN:
|
|
600
558
|
1. Start: { query: "...", level: "basic"|"normal"|"high", thought: "your analysis..." }
|
|
601
|
-
2. Continue: { sessionId: "<from response>",
|
|
602
|
-
3. Repeat
|
|
559
|
+
2. Continue: { sessionId: "<from response>", thought: "next step..." } — level is optional; session level is used
|
|
560
|
+
3. Repeat until status: "completed" — the summary field contains the exact next call to make
|
|
603
561
|
|
|
604
|
-
IMPORTANT:
|
|
562
|
+
IMPORTANT: Pass the returned sessionId on every continuation call.
|
|
605
563
|
The thought parameter stores YOUR reasoning verbatim — write thorough analysis in each step.
|
|
606
|
-
Use step_summary
|
|
564
|
+
Use step_summary for a 1-sentence conclusion per step — these accumulate in the summary field for navigation.
|
|
607
565
|
|
|
608
566
|
Levels: ${getLevelDescriptionString()}.
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
Error recovery: If E_SESSION_NOT_FOUND, the session expired — start a new session. If E_INVALID_THOUGHT_COUNT, check level ranges.`,
|
|
567
|
+
Alternatives: runMode="run_to_completion" (batch), or observation/hypothesis/evaluation fields (structured).
|
|
568
|
+
Errors: E_SESSION_NOT_FOUND (expired — start new), E_INVALID_THOUGHT_COUNT (check level ranges).`,
|
|
612
569
|
inputSchema: ReasoningThinkInputSchema,
|
|
613
570
|
outputSchema: ReasoningThinkToolOutputSchema,
|
|
614
571
|
annotations: {
|
|
@@ -632,7 +589,7 @@ Error recovery: If E_SESSION_NOT_FOUND, the session expired — start a new sess
|
|
|
632
589
|
const extra = parseReasoningTaskExtra(rawExtra);
|
|
633
590
|
const progressToken = extra._meta?.progressToken;
|
|
634
591
|
if (!reasoningTaskLimiter.tryAcquire()) {
|
|
635
|
-
throw new
|
|
592
|
+
throw new ServerBusyError();
|
|
636
593
|
}
|
|
637
594
|
let task;
|
|
638
595
|
try {
|