@j0hanz/cortex-mcp 1.2.1 โ 1.4.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/README.md +2 -2
- package/dist/engine/config.d.ts +3 -2
- package/dist/engine/config.js +15 -4
- package/dist/engine/context.d.ts +4 -5
- package/dist/engine/context.js +3 -1
- package/dist/engine/events.d.ts +8 -10
- package/dist/engine/events.js +6 -4
- package/dist/engine/reasoner.d.ts +7 -2
- package/dist/engine/reasoner.js +82 -39
- package/dist/engine/session-store.d.ts +14 -3
- package/dist/engine/session-store.js +134 -80
- package/dist/index.d.ts +0 -1
- package/dist/index.js +14 -8
- package/dist/lib/errors.d.ts +0 -1
- package/dist/lib/errors.js +16 -8
- package/dist/lib/formatting.d.ts +0 -1
- package/dist/lib/formatting.js +32 -25
- package/dist/lib/prompt-contracts.d.ts +14 -0
- package/dist/lib/prompt-contracts.js +124 -0
- package/dist/lib/text.d.ts +0 -1
- package/dist/lib/text.js +23 -18
- package/dist/lib/tool-contracts.d.ts +15 -0
- package/dist/lib/tool-contracts.js +92 -0
- package/dist/lib/tool-response.d.ts +0 -1
- package/dist/lib/tool-response.js +9 -7
- package/dist/lib/types.d.ts +23 -13
- package/dist/lib/types.js +0 -1
- package/dist/lib/validators.d.ts +0 -1
- package/dist/lib/validators.js +12 -11
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +98 -112
- package/dist/resources/index.d.ts +0 -1
- package/dist/resources/index.js +92 -68
- package/dist/resources/instructions.d.ts +1 -0
- package/dist/resources/instructions.js +95 -0
- package/dist/resources/tool-catalog.d.ts +1 -0
- package/dist/resources/tool-catalog.js +20 -0
- package/dist/resources/tool-info.d.ts +2 -0
- package/dist/resources/tool-info.js +35 -0
- package/dist/resources/workflows.d.ts +1 -0
- package/dist/resources/workflows.js +60 -0
- package/dist/schemas/inputs.d.ts +7 -2
- package/dist/schemas/inputs.js +62 -48
- package/dist/schemas/outputs.d.ts +3 -1
- package/dist/schemas/outputs.js +50 -25
- package/dist/server.d.ts +0 -1
- package/dist/server.js +71 -56
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.js +4 -5
- package/dist/tools/reasoning-think.d.ts +0 -1
- package/dist/tools/reasoning-think.js +273 -109
- package/package.json +11 -9
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/context.d.ts.map +0 -1
- package/dist/engine/context.js.map +0 -1
- package/dist/engine/events.d.ts.map +0 -1
- package/dist/engine/events.js.map +0 -1
- package/dist/engine/reasoner.d.ts.map +0 -1
- package/dist/engine/reasoner.js.map +0 -1
- package/dist/engine/session-store.d.ts.map +0 -1
- package/dist/engine/session-store.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/instructions.md +0 -147
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/formatting.d.ts.map +0 -1
- package/dist/lib/formatting.js.map +0 -1
- package/dist/lib/instructions.d.ts +0 -5
- package/dist/lib/instructions.d.ts.map +0 -1
- package/dist/lib/instructions.js +0 -14
- package/dist/lib/instructions.js.map +0 -1
- package/dist/lib/text.d.ts.map +0 -1
- package/dist/lib/text.js.map +0 -1
- package/dist/lib/tool-response.d.ts.map +0 -1
- package/dist/lib/tool-response.js.map +0 -1
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/validators.d.ts.map +0 -1
- package/dist/lib/validators.js.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/index.js.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/schemas/inputs.d.ts.map +0 -1
- package/dist/schemas/inputs.js.map +0 -1
- package/dist/schemas/outputs.d.ts.map +0 -1
- package/dist/schemas/outputs.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/reasoning-think.d.ts.map +0 -1
- package/dist/tools/reasoning-think.js.map +0 -1
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import {
|
|
3
|
+
import { getLevelConfig } from './config.js';
|
|
4
4
|
import { engineEvents } from './events.js';
|
|
5
5
|
const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
6
6
|
const DEFAULT_MAX_SESSIONS = 100;
|
|
7
7
|
const DEFAULT_MAX_TOTAL_TOKENS = 500_000;
|
|
8
|
+
const TOKEN_ESTIMATE_DIVISOR = 4;
|
|
9
|
+
const MIN_SWEEP_INTERVAL_MS = 10;
|
|
10
|
+
const MAX_SWEEP_INTERVAL_MS = 60_000;
|
|
8
11
|
function estimateTokens(text) {
|
|
9
12
|
const byteLength = Buffer.byteLength(text, 'utf8');
|
|
10
|
-
return Math.max(1, Math.ceil(byteLength /
|
|
13
|
+
return Math.max(1, Math.ceil(byteLength / TOKEN_ESTIMATE_DIVISOR));
|
|
14
|
+
}
|
|
15
|
+
function resolveSweepInterval(ttlMs) {
|
|
16
|
+
return Math.max(MIN_SWEEP_INTERVAL_MS, Math.min(MAX_SWEEP_INTERVAL_MS, ttlMs));
|
|
11
17
|
}
|
|
12
18
|
export class SessionStore {
|
|
13
19
|
sessions = new Map();
|
|
@@ -24,7 +30,7 @@ export class SessionStore {
|
|
|
24
30
|
this.ttlMs = ttlMs;
|
|
25
31
|
this.maxSessions = maxSessions;
|
|
26
32
|
this.maxTotalTokens = maxTotalTokens;
|
|
27
|
-
const sweepInterval =
|
|
33
|
+
const sweepInterval = resolveSweepInterval(ttlMs);
|
|
28
34
|
this.cleanupInterval = setInterval(() => {
|
|
29
35
|
this.sweep();
|
|
30
36
|
}, sweepInterval);
|
|
@@ -32,7 +38,7 @@ export class SessionStore {
|
|
|
32
38
|
}
|
|
33
39
|
create(level, totalThoughts) {
|
|
34
40
|
this.evictIfAtCapacity();
|
|
35
|
-
const config =
|
|
41
|
+
const config = getLevelConfig(level);
|
|
36
42
|
const now = Date.now();
|
|
37
43
|
const session = {
|
|
38
44
|
id: randomUUID(),
|
|
@@ -56,10 +62,13 @@ export class SessionStore {
|
|
|
56
62
|
const session = this.sessions.get(id);
|
|
57
63
|
return session ? this.snapshotSession(session) : undefined;
|
|
58
64
|
}
|
|
65
|
+
getSummary(id) {
|
|
66
|
+
const session = this.sessions.get(id);
|
|
67
|
+
return session ? this.snapshotSessionSummary(session) : undefined;
|
|
68
|
+
}
|
|
59
69
|
list() {
|
|
60
|
-
this.sortedSessionIdsCache ??= this.buildSortedSessionIdsCache();
|
|
61
70
|
const sessions = [];
|
|
62
|
-
for (const sessionId of this.
|
|
71
|
+
for (const sessionId of this.getSessionIdsForIteration()) {
|
|
63
72
|
const session = this.sessions.get(sessionId);
|
|
64
73
|
if (session) {
|
|
65
74
|
sessions.push(this.snapshotSession(session));
|
|
@@ -67,6 +76,20 @@ export class SessionStore {
|
|
|
67
76
|
}
|
|
68
77
|
return sessions;
|
|
69
78
|
}
|
|
79
|
+
listSessionIds() {
|
|
80
|
+
this.sortedSessionIdsCache ??= this.buildSortedSessionIdsCache();
|
|
81
|
+
return [...this.sortedSessionIdsCache];
|
|
82
|
+
}
|
|
83
|
+
listSummaries() {
|
|
84
|
+
const summaries = [];
|
|
85
|
+
for (const sessionId of this.getSessionIdsForIteration()) {
|
|
86
|
+
const session = this.sessions.get(sessionId);
|
|
87
|
+
if (session) {
|
|
88
|
+
summaries.push(this.snapshotSessionSummary(session));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return summaries;
|
|
92
|
+
}
|
|
70
93
|
getTtlMs() {
|
|
71
94
|
return this.ttlMs;
|
|
72
95
|
}
|
|
@@ -86,11 +109,10 @@ export class SessionStore {
|
|
|
86
109
|
return false;
|
|
87
110
|
}
|
|
88
111
|
engineEvents.emit('session:deleted', { sessionId: id });
|
|
89
|
-
this.
|
|
90
|
-
this.emitSessionsResourceUpdated();
|
|
112
|
+
this.emitSessionsCollectionUpdated();
|
|
91
113
|
return true;
|
|
92
114
|
}
|
|
93
|
-
addThought(sessionId, content) {
|
|
115
|
+
addThought(sessionId, content, stepSummary) {
|
|
94
116
|
const session = this.sessions.get(sessionId);
|
|
95
117
|
if (!session) {
|
|
96
118
|
throw new Error(`Session not found: ${sessionId}`);
|
|
@@ -101,16 +123,34 @@ export class SessionStore {
|
|
|
101
123
|
index: session.thoughts.length,
|
|
102
124
|
content,
|
|
103
125
|
revision: 0,
|
|
126
|
+
...(stepSummary !== undefined ? { stepSummary } : {}),
|
|
104
127
|
};
|
|
105
128
|
session.thoughts.push(thought);
|
|
106
129
|
session.tokensUsed += tokens;
|
|
107
130
|
this.totalTokens += tokens;
|
|
108
|
-
|
|
109
|
-
this.touchOrder(session.id);
|
|
110
|
-
this.sortedSessionIdsCache = null;
|
|
111
|
-
this.emitSessionResourcesUpdated(sessionId);
|
|
131
|
+
this.markSessionTouched(session);
|
|
112
132
|
return this.snapshotThought(thought);
|
|
113
133
|
}
|
|
134
|
+
rollback(sessionId, toIndex) {
|
|
135
|
+
const session = this.sessions.get(sessionId);
|
|
136
|
+
if (!session) {
|
|
137
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
138
|
+
}
|
|
139
|
+
// If toIndex is out of bounds or implies no change, return.
|
|
140
|
+
// We keep thoughts up to and including toIndex.
|
|
141
|
+
if (toIndex < 0 || toIndex >= session.thoughts.length - 1) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const removedThoughts = session.thoughts.slice(toIndex + 1);
|
|
145
|
+
session.thoughts = session.thoughts.slice(0, toIndex + 1);
|
|
146
|
+
let removedTokens = 0;
|
|
147
|
+
for (const t of removedThoughts) {
|
|
148
|
+
removedTokens += estimateTokens(t.content);
|
|
149
|
+
}
|
|
150
|
+
session.tokensUsed -= removedTokens;
|
|
151
|
+
this.totalTokens -= removedTokens;
|
|
152
|
+
this.markSessionTouched(session);
|
|
153
|
+
}
|
|
114
154
|
reviseThought(sessionId, thoughtIndex, content) {
|
|
115
155
|
const session = this.sessions.get(sessionId);
|
|
116
156
|
if (!session) {
|
|
@@ -134,30 +174,20 @@ export class SessionStore {
|
|
|
134
174
|
session.thoughts[thoughtIndex] = revised;
|
|
135
175
|
session.tokensUsed = session.tokensUsed - oldTokens + newTokens;
|
|
136
176
|
this.totalTokens += delta;
|
|
137
|
-
|
|
138
|
-
this.touchOrder(session.id);
|
|
139
|
-
this.sortedSessionIdsCache = null;
|
|
140
|
-
this.emitSessionResourcesUpdated(sessionId);
|
|
177
|
+
this.markSessionTouched(session);
|
|
141
178
|
return this.snapshotThought(revised);
|
|
142
179
|
}
|
|
143
180
|
markCompleted(sessionId) {
|
|
144
|
-
|
|
145
|
-
if (session?.status === 'active') {
|
|
146
|
-
session.status = 'completed';
|
|
147
|
-
session.updatedAt = Date.now();
|
|
148
|
-
this.touchOrder(session.id);
|
|
149
|
-
this.sortedSessionIdsCache = null;
|
|
150
|
-
this.emitSessionResourcesUpdated(sessionId);
|
|
151
|
-
}
|
|
181
|
+
this.updateSessionStatus(sessionId, 'completed');
|
|
152
182
|
}
|
|
153
183
|
markCancelled(sessionId) {
|
|
184
|
+
this.updateSessionStatus(sessionId, 'cancelled');
|
|
185
|
+
}
|
|
186
|
+
updateSessionStatus(sessionId, status) {
|
|
154
187
|
const session = this.sessions.get(sessionId);
|
|
155
188
|
if (session?.status === 'active') {
|
|
156
|
-
session.status =
|
|
157
|
-
|
|
158
|
-
this.touchOrder(session.id);
|
|
159
|
-
this.sortedSessionIdsCache = null;
|
|
160
|
-
this.emitSessionResourcesUpdated(sessionId);
|
|
189
|
+
session.status = status;
|
|
190
|
+
this.markSessionTouched(session);
|
|
161
191
|
}
|
|
162
192
|
}
|
|
163
193
|
evictIfAtCapacity() {
|
|
@@ -166,12 +196,7 @@ export class SessionStore {
|
|
|
166
196
|
if (!oldest)
|
|
167
197
|
break;
|
|
168
198
|
this.deleteSessionInternal(oldest.id);
|
|
169
|
-
|
|
170
|
-
sessionId: oldest.id,
|
|
171
|
-
reason: 'max_sessions',
|
|
172
|
-
});
|
|
173
|
-
this.emitSessionsListChanged();
|
|
174
|
-
this.emitSessionsResourceUpdated();
|
|
199
|
+
this.emitSessionEvicted(oldest.id, 'max_sessions');
|
|
175
200
|
}
|
|
176
201
|
}
|
|
177
202
|
evictForTokenHeadroom(neededTokens, protectedSessionId) {
|
|
@@ -181,12 +206,7 @@ export class SessionStore {
|
|
|
181
206
|
if (!oldest)
|
|
182
207
|
break;
|
|
183
208
|
this.deleteSessionInternal(oldest.id);
|
|
184
|
-
|
|
185
|
-
sessionId: oldest.id,
|
|
186
|
-
reason: 'max_total_tokens',
|
|
187
|
-
});
|
|
188
|
-
this.emitSessionsListChanged();
|
|
189
|
-
this.emitSessionsResourceUpdated();
|
|
209
|
+
this.emitSessionEvicted(oldest.id, 'max_total_tokens');
|
|
190
210
|
}
|
|
191
211
|
}
|
|
192
212
|
findOldestSession(excludeId) {
|
|
@@ -201,23 +221,26 @@ export class SessionStore {
|
|
|
201
221
|
}
|
|
202
222
|
sweep() {
|
|
203
223
|
const now = Date.now();
|
|
204
|
-
const expiredSessionIds = [];
|
|
205
224
|
let changed = false;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (!this.deleteSessionInternal(sessionId)) {
|
|
225
|
+
let currentId = this.oldestSessionId;
|
|
226
|
+
while (currentId) {
|
|
227
|
+
const nextId = this.sessionOrder.get(currentId)?.nextId;
|
|
228
|
+
const session = this.sessions.get(currentId);
|
|
229
|
+
if (!session) {
|
|
230
|
+
currentId = nextId;
|
|
213
231
|
continue;
|
|
214
232
|
}
|
|
215
|
-
|
|
216
|
-
|
|
233
|
+
if (session.updatedAt + this.ttlMs >= now) {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
if (this.deleteSessionInternal(currentId)) {
|
|
237
|
+
engineEvents.emit('session:expired', { sessionId: currentId });
|
|
238
|
+
changed = true;
|
|
239
|
+
}
|
|
240
|
+
currentId = nextId;
|
|
217
241
|
}
|
|
218
242
|
if (changed) {
|
|
219
|
-
this.
|
|
220
|
-
this.emitSessionsResourceUpdated();
|
|
243
|
+
this.emitSessionsCollectionUpdated();
|
|
221
244
|
}
|
|
222
245
|
}
|
|
223
246
|
buildSortedSessionIdsCache() {
|
|
@@ -238,10 +261,7 @@ export class SessionStore {
|
|
|
238
261
|
}
|
|
239
262
|
const node = { prevId: undefined, nextId: undefined };
|
|
240
263
|
if (this.newestSessionId) {
|
|
241
|
-
|
|
242
|
-
if (newest) {
|
|
243
|
-
newest.nextId = sessionId;
|
|
244
|
-
}
|
|
264
|
+
this.setNextId(this.newestSessionId, sessionId);
|
|
245
265
|
node.prevId = this.newestSessionId;
|
|
246
266
|
}
|
|
247
267
|
else {
|
|
@@ -261,27 +281,18 @@ export class SessionStore {
|
|
|
261
281
|
}
|
|
262
282
|
const { prevId, nextId } = node;
|
|
263
283
|
if (prevId) {
|
|
264
|
-
|
|
265
|
-
if (previous) {
|
|
266
|
-
previous.nextId = nextId;
|
|
267
|
-
}
|
|
284
|
+
this.setNextId(prevId, nextId);
|
|
268
285
|
}
|
|
269
286
|
else {
|
|
270
287
|
this.oldestSessionId = nextId;
|
|
271
288
|
}
|
|
272
289
|
if (nextId) {
|
|
273
|
-
|
|
274
|
-
if (next) {
|
|
275
|
-
next.prevId = prevId;
|
|
276
|
-
}
|
|
290
|
+
this.setPrevId(nextId, prevId);
|
|
277
291
|
}
|
|
278
292
|
node.prevId = this.newestSessionId;
|
|
279
293
|
node.nextId = undefined;
|
|
280
294
|
if (this.newestSessionId) {
|
|
281
|
-
|
|
282
|
-
if (newest) {
|
|
283
|
-
newest.nextId = sessionId;
|
|
284
|
-
}
|
|
295
|
+
this.setNextId(this.newestSessionId, sessionId);
|
|
285
296
|
}
|
|
286
297
|
else {
|
|
287
298
|
this.oldestSessionId = sessionId;
|
|
@@ -295,25 +306,31 @@ export class SessionStore {
|
|
|
295
306
|
}
|
|
296
307
|
const { prevId, nextId } = node;
|
|
297
308
|
if (prevId) {
|
|
298
|
-
|
|
299
|
-
if (previous) {
|
|
300
|
-
previous.nextId = nextId;
|
|
301
|
-
}
|
|
309
|
+
this.setNextId(prevId, nextId);
|
|
302
310
|
}
|
|
303
311
|
else {
|
|
304
312
|
this.oldestSessionId = nextId;
|
|
305
313
|
}
|
|
306
314
|
if (nextId) {
|
|
307
|
-
|
|
308
|
-
if (next) {
|
|
309
|
-
next.prevId = prevId;
|
|
310
|
-
}
|
|
315
|
+
this.setPrevId(nextId, prevId);
|
|
311
316
|
}
|
|
312
317
|
else {
|
|
313
318
|
this.newestSessionId = prevId;
|
|
314
319
|
}
|
|
315
320
|
this.sessionOrder.delete(sessionId);
|
|
316
321
|
}
|
|
322
|
+
setNextId(sessionId, nextId) {
|
|
323
|
+
const node = this.sessionOrder.get(sessionId);
|
|
324
|
+
if (node) {
|
|
325
|
+
node.nextId = nextId;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
setPrevId(sessionId, prevId) {
|
|
329
|
+
const node = this.sessionOrder.get(sessionId);
|
|
330
|
+
if (node) {
|
|
331
|
+
node.prevId = prevId;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
317
334
|
deleteSessionInternal(id) {
|
|
318
335
|
const session = this.sessions.get(id);
|
|
319
336
|
if (!session) {
|
|
@@ -325,19 +342,46 @@ export class SessionStore {
|
|
|
325
342
|
this.sortedSessionIdsCache = null;
|
|
326
343
|
return session;
|
|
327
344
|
}
|
|
345
|
+
markSessionTouched(session) {
|
|
346
|
+
session.updatedAt = Date.now();
|
|
347
|
+
this.touchOrder(session.id);
|
|
348
|
+
this.sortedSessionIdsCache = null;
|
|
349
|
+
this.emitSessionResourcesUpdated(session.id);
|
|
350
|
+
}
|
|
351
|
+
getSessionIdsForIteration() {
|
|
352
|
+
this.sortedSessionIdsCache ??= this.buildSortedSessionIdsCache();
|
|
353
|
+
return this.sortedSessionIdsCache;
|
|
354
|
+
}
|
|
328
355
|
snapshotThought(thought) {
|
|
329
356
|
return {
|
|
330
357
|
index: thought.index,
|
|
331
358
|
content: thought.content,
|
|
332
359
|
revision: thought.revision,
|
|
360
|
+
...(thought.stepSummary !== undefined
|
|
361
|
+
? { stepSummary: thought.stepSummary }
|
|
362
|
+
: {}),
|
|
333
363
|
};
|
|
334
364
|
}
|
|
335
365
|
snapshotSession(session) {
|
|
366
|
+
const thoughts = session.thoughts.map((thought) => this.snapshotThought(thought));
|
|
367
|
+
return {
|
|
368
|
+
id: session.id,
|
|
369
|
+
level: session.level,
|
|
370
|
+
status: session.status,
|
|
371
|
+
thoughts,
|
|
372
|
+
totalThoughts: session.totalThoughts,
|
|
373
|
+
tokenBudget: session.tokenBudget,
|
|
374
|
+
tokensUsed: session.tokensUsed,
|
|
375
|
+
createdAt: session.createdAt,
|
|
376
|
+
updatedAt: session.updatedAt,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
snapshotSessionSummary(session) {
|
|
336
380
|
return {
|
|
337
381
|
id: session.id,
|
|
338
382
|
level: session.level,
|
|
339
383
|
status: session.status,
|
|
340
|
-
|
|
384
|
+
generatedThoughts: session.thoughts.length,
|
|
341
385
|
totalThoughts: session.totalThoughts,
|
|
342
386
|
tokenBudget: session.tokenBudget,
|
|
343
387
|
tokensUsed: session.tokensUsed,
|
|
@@ -351,6 +395,17 @@ export class SessionStore {
|
|
|
351
395
|
emitSessionsResourceUpdated() {
|
|
352
396
|
engineEvents.emit('resource:updated', { uri: 'reasoning://sessions' });
|
|
353
397
|
}
|
|
398
|
+
emitSessionsCollectionUpdated() {
|
|
399
|
+
this.emitSessionsListChanged();
|
|
400
|
+
this.emitSessionsResourceUpdated();
|
|
401
|
+
}
|
|
402
|
+
emitSessionEvicted(sessionId, reason) {
|
|
403
|
+
engineEvents.emit('session:evicted', {
|
|
404
|
+
sessionId,
|
|
405
|
+
reason,
|
|
406
|
+
});
|
|
407
|
+
this.emitSessionsCollectionUpdated();
|
|
408
|
+
}
|
|
354
409
|
emitSessionResourcesUpdated(sessionId) {
|
|
355
410
|
engineEvents.emit('resource:updated', {
|
|
356
411
|
uri: `reasoning://sessions/${sessionId}`,
|
|
@@ -358,4 +413,3 @@ export class SessionStore {
|
|
|
358
413
|
this.emitSessionsResourceUpdated();
|
|
359
414
|
}
|
|
360
415
|
}
|
|
361
|
-
//# sourceMappingURL=session-store.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { isMainThread, threadId } from 'node:worker_threads';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { createServer } from './server.js';
|
|
5
|
+
const FATAL_SHUTDOWN_REASON = 'fatal error';
|
|
6
|
+
const SHUTDOWN_SIGNALS = ['SIGTERM', 'SIGINT'];
|
|
5
7
|
let activeServer;
|
|
6
8
|
let shutdownPromise;
|
|
7
9
|
function assertMainThread() {
|
|
@@ -16,6 +18,11 @@ async function main() {
|
|
|
16
18
|
const transport = new StdioServerTransport();
|
|
17
19
|
await activeServer.connect(transport);
|
|
18
20
|
}
|
|
21
|
+
function registerShutdownSignals() {
|
|
22
|
+
for (const signal of SHUTDOWN_SIGNALS) {
|
|
23
|
+
registerShutdownSignal(signal);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
19
26
|
async function shutdown(exitCode, reason) {
|
|
20
27
|
if (shutdownPromise) {
|
|
21
28
|
return shutdownPromise;
|
|
@@ -35,14 +42,13 @@ async function shutdown(exitCode, reason) {
|
|
|
35
42
|
})();
|
|
36
43
|
return shutdownPromise;
|
|
37
44
|
}
|
|
45
|
+
function registerShutdownSignal(signal) {
|
|
46
|
+
process.once(signal, () => {
|
|
47
|
+
void shutdown(0, signal);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
registerShutdownSignals();
|
|
38
51
|
main().catch((err) => {
|
|
39
52
|
console.error('Fatal error:', err);
|
|
40
|
-
void shutdown(1,
|
|
41
|
-
});
|
|
42
|
-
process.once('SIGTERM', () => {
|
|
43
|
-
void shutdown(0, 'SIGTERM');
|
|
44
|
-
});
|
|
45
|
-
process.once('SIGINT', () => {
|
|
46
|
-
void shutdown(0, 'SIGINT');
|
|
53
|
+
void shutdown(1, FATAL_SHUTDOWN_REASON);
|
|
47
54
|
});
|
|
48
|
-
//# sourceMappingURL=index.js.map
|
package/dist/lib/errors.d.ts
CHANGED
package/dist/lib/errors.js
CHANGED
|
@@ -3,6 +3,10 @@ const INSPECT_OPTIONS = {
|
|
|
3
3
|
depth: 3,
|
|
4
4
|
breakLength: 120,
|
|
5
5
|
};
|
|
6
|
+
const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null;
|
|
9
|
+
}
|
|
6
10
|
function stringifyUnknown(value) {
|
|
7
11
|
try {
|
|
8
12
|
return JSON.stringify(value);
|
|
@@ -12,6 +16,12 @@ function stringifyUnknown(value) {
|
|
|
12
16
|
}
|
|
13
17
|
return inspect(value, INSPECT_OPTIONS);
|
|
14
18
|
}
|
|
19
|
+
function getMessageFromErrorLike(value) {
|
|
20
|
+
if (!isRecord(value)) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return typeof value.message === 'string' ? value.message : undefined;
|
|
24
|
+
}
|
|
15
25
|
export function getErrorMessage(error) {
|
|
16
26
|
if (typeof error === 'string') {
|
|
17
27
|
return error;
|
|
@@ -20,22 +30,20 @@ export function getErrorMessage(error) {
|
|
|
20
30
|
return error.message;
|
|
21
31
|
}
|
|
22
32
|
if (error === null || error === undefined) {
|
|
23
|
-
return
|
|
33
|
+
return UNKNOWN_ERROR_MESSAGE;
|
|
24
34
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return maybeError.message;
|
|
29
|
-
}
|
|
35
|
+
const errorLikeMessage = getMessageFromErrorLike(error);
|
|
36
|
+
if (errorLikeMessage !== undefined) {
|
|
37
|
+
return errorLikeMessage;
|
|
30
38
|
}
|
|
31
39
|
return stringifyUnknown(error);
|
|
32
40
|
}
|
|
33
41
|
export function createErrorResponse(code, message) {
|
|
34
42
|
const structured = { ok: false, error: { code, message } };
|
|
43
|
+
const text = JSON.stringify(structured);
|
|
35
44
|
return {
|
|
36
|
-
content: [{ type: 'text', text
|
|
45
|
+
content: [{ type: 'text', text }],
|
|
37
46
|
structuredContent: structured,
|
|
38
47
|
isError: true,
|
|
39
48
|
};
|
|
40
49
|
}
|
|
41
|
-
//# sourceMappingURL=errors.js.map
|
package/dist/lib/formatting.d.ts
CHANGED
package/dist/lib/formatting.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
const PIN_START = '<!-- pin:';
|
|
5
5
|
const PIN_END = '<!-- /pin -->';
|
|
6
|
+
const TRACE_SEPARATOR = '\n\n---\n\n';
|
|
7
|
+
const PINNED_SECTION_TITLE = '## ๐ Pinned';
|
|
6
8
|
/**
|
|
7
9
|
* Extract pinned sections from thought content.
|
|
8
10
|
*
|
|
@@ -18,28 +20,31 @@ const PIN_END = '<!-- /pin -->';
|
|
|
18
20
|
export function extractPinnedSections(thoughts) {
|
|
19
21
|
const byTitle = new Map();
|
|
20
22
|
for (const thought of thoughts) {
|
|
23
|
+
const { content } = thought;
|
|
21
24
|
let searchFrom = 0;
|
|
22
|
-
while (searchFrom <
|
|
23
|
-
const startIdx =
|
|
25
|
+
while (searchFrom < content.length) {
|
|
26
|
+
const startIdx = content.indexOf(PIN_START, searchFrom);
|
|
24
27
|
if (startIdx === -1) {
|
|
25
28
|
break;
|
|
26
29
|
}
|
|
27
|
-
const arrowIdx =
|
|
30
|
+
const arrowIdx = content.indexOf('-->', startIdx + PIN_START.length);
|
|
28
31
|
if (arrowIdx === -1) {
|
|
29
32
|
break;
|
|
30
33
|
}
|
|
31
|
-
const title =
|
|
32
|
-
.slice(startIdx + PIN_START.length, arrowIdx)
|
|
33
|
-
.trim();
|
|
34
|
+
const title = content.slice(startIdx + PIN_START.length, arrowIdx).trim();
|
|
34
35
|
const contentStart = arrowIdx + 3;
|
|
35
|
-
const endIdx =
|
|
36
|
+
const endIdx = content.indexOf(PIN_END, contentStart);
|
|
36
37
|
if (endIdx === -1) {
|
|
37
38
|
break;
|
|
38
39
|
}
|
|
39
|
-
const
|
|
40
|
+
const pinContent = content.slice(contentStart, endIdx).trim();
|
|
40
41
|
searchFrom = endIdx + PIN_END.length;
|
|
41
42
|
if (title.length > 0) {
|
|
42
|
-
byTitle.set(title, {
|
|
43
|
+
byTitle.set(title, {
|
|
44
|
+
title,
|
|
45
|
+
content: pinContent,
|
|
46
|
+
thoughtIndex: thought.index,
|
|
47
|
+
});
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
}
|
|
@@ -52,7 +57,7 @@ function renderPinnedSections(sections) {
|
|
|
52
57
|
if (sections.length === 0) {
|
|
53
58
|
return '';
|
|
54
59
|
}
|
|
55
|
-
const lines = [
|
|
60
|
+
const lines = [PINNED_SECTION_TITLE, ''];
|
|
56
61
|
for (const pin of sections) {
|
|
57
62
|
lines.push(`### ${pin.title} *(Thought ${String(pin.thoughtIndex + 1)})*`);
|
|
58
63
|
if (pin.content.length > 0) {
|
|
@@ -70,6 +75,17 @@ function formatThoughtHeading(thought) {
|
|
|
70
75
|
const suffix = thought.revision > 0 ? ' [Revised]' : '';
|
|
71
76
|
return `๐ฆน Thought [${String(thoughtNumber)}]${suffix}`;
|
|
72
77
|
}
|
|
78
|
+
function renderThoughtSection(thought) {
|
|
79
|
+
return `${formatThoughtHeading(thought)}\n\n${thought.content}`;
|
|
80
|
+
}
|
|
81
|
+
function selectThoughts(allThoughts, range) {
|
|
82
|
+
if (!range) {
|
|
83
|
+
return allThoughts;
|
|
84
|
+
}
|
|
85
|
+
const startIndex = Math.max(0, range.start - 1);
|
|
86
|
+
const endIndex = Math.min(allThoughts.length, range.end);
|
|
87
|
+
return allThoughts.slice(startIndex, endIndex);
|
|
88
|
+
}
|
|
73
89
|
/**
|
|
74
90
|
* Format a session's thoughts as Markdown.
|
|
75
91
|
*
|
|
@@ -82,23 +98,16 @@ function formatThoughtHeading(thought) {
|
|
|
82
98
|
export function formatThoughtsToMarkdown(session, range) {
|
|
83
99
|
const { thoughts: allThoughts } = session;
|
|
84
100
|
const isFullTrace = range === undefined;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const startIndex = Math.max(0, range.start - 1);
|
|
88
|
-
const endIndex = Math.min(allThoughts.length, range.end);
|
|
89
|
-
thoughts = allThoughts.slice(startIndex, endIndex);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
thoughts = allThoughts;
|
|
93
|
-
}
|
|
101
|
+
const thoughts = selectThoughts(allThoughts, range);
|
|
102
|
+
const hasFullTraceThoughts = isFullTrace && thoughts.length > 0;
|
|
94
103
|
const sections = [];
|
|
95
104
|
// --- Header ---
|
|
96
|
-
if (
|
|
105
|
+
if (hasFullTraceThoughts) {
|
|
97
106
|
sections.push(`# Reasoning Trace โ [${session.level}]\n` +
|
|
98
107
|
`> Session [${session.id}] ยท [${String(allThoughts.length)}] thoughts`);
|
|
99
108
|
}
|
|
100
109
|
// --- Pinned sections (full trace only) ---
|
|
101
|
-
if (
|
|
110
|
+
if (hasFullTraceThoughts) {
|
|
102
111
|
const pinned = extractPinnedSections(thoughts);
|
|
103
112
|
const pinnedMd = renderPinnedSections(pinned);
|
|
104
113
|
if (pinnedMd.length > 0) {
|
|
@@ -107,9 +116,7 @@ export function formatThoughtsToMarkdown(session, range) {
|
|
|
107
116
|
}
|
|
108
117
|
// --- Thought narrative ---
|
|
109
118
|
for (const thought of thoughts) {
|
|
110
|
-
|
|
111
|
-
sections.push(`${heading}\n\n${thought.content}`);
|
|
119
|
+
sections.push(renderThoughtSection(thought));
|
|
112
120
|
}
|
|
113
|
-
return sections.join(
|
|
121
|
+
return sections.join(TRACE_SEPARATOR);
|
|
114
122
|
}
|
|
115
|
-
//# sourceMappingURL=formatting.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export interface PromptArg {
|
|
3
|
+
name: string;
|
|
4
|
+
type: z.ZodType;
|
|
5
|
+
description: string;
|
|
6
|
+
required: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PromptContract {
|
|
9
|
+
name: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
args: PromptArg[];
|
|
13
|
+
}
|
|
14
|
+
export declare function getPromptContracts(): PromptContract[];
|