@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.
Files changed (95) hide show
  1. package/README.md +2 -2
  2. package/dist/engine/config.d.ts +3 -2
  3. package/dist/engine/config.js +15 -4
  4. package/dist/engine/context.d.ts +4 -5
  5. package/dist/engine/context.js +3 -1
  6. package/dist/engine/events.d.ts +8 -10
  7. package/dist/engine/events.js +6 -4
  8. package/dist/engine/reasoner.d.ts +7 -2
  9. package/dist/engine/reasoner.js +82 -39
  10. package/dist/engine/session-store.d.ts +14 -3
  11. package/dist/engine/session-store.js +134 -80
  12. package/dist/index.d.ts +0 -1
  13. package/dist/index.js +14 -8
  14. package/dist/lib/errors.d.ts +0 -1
  15. package/dist/lib/errors.js +16 -8
  16. package/dist/lib/formatting.d.ts +0 -1
  17. package/dist/lib/formatting.js +32 -25
  18. package/dist/lib/prompt-contracts.d.ts +14 -0
  19. package/dist/lib/prompt-contracts.js +124 -0
  20. package/dist/lib/text.d.ts +0 -1
  21. package/dist/lib/text.js +23 -18
  22. package/dist/lib/tool-contracts.d.ts +15 -0
  23. package/dist/lib/tool-contracts.js +92 -0
  24. package/dist/lib/tool-response.d.ts +0 -1
  25. package/dist/lib/tool-response.js +9 -7
  26. package/dist/lib/types.d.ts +23 -13
  27. package/dist/lib/types.js +0 -1
  28. package/dist/lib/validators.d.ts +0 -1
  29. package/dist/lib/validators.js +12 -11
  30. package/dist/prompts/index.d.ts +0 -1
  31. package/dist/prompts/index.js +98 -112
  32. package/dist/resources/index.d.ts +0 -1
  33. package/dist/resources/index.js +92 -68
  34. package/dist/resources/instructions.d.ts +1 -0
  35. package/dist/resources/instructions.js +95 -0
  36. package/dist/resources/tool-catalog.d.ts +1 -0
  37. package/dist/resources/tool-catalog.js +20 -0
  38. package/dist/resources/tool-info.d.ts +2 -0
  39. package/dist/resources/tool-info.js +35 -0
  40. package/dist/resources/workflows.d.ts +1 -0
  41. package/dist/resources/workflows.js +60 -0
  42. package/dist/schemas/inputs.d.ts +7 -2
  43. package/dist/schemas/inputs.js +62 -48
  44. package/dist/schemas/outputs.d.ts +3 -1
  45. package/dist/schemas/outputs.js +50 -25
  46. package/dist/server.d.ts +0 -1
  47. package/dist/server.js +71 -56
  48. package/dist/tools/index.d.ts +0 -4
  49. package/dist/tools/index.js +4 -5
  50. package/dist/tools/reasoning-think.d.ts +0 -1
  51. package/dist/tools/reasoning-think.js +273 -109
  52. package/package.json +11 -9
  53. package/dist/engine/config.d.ts.map +0 -1
  54. package/dist/engine/config.js.map +0 -1
  55. package/dist/engine/context.d.ts.map +0 -1
  56. package/dist/engine/context.js.map +0 -1
  57. package/dist/engine/events.d.ts.map +0 -1
  58. package/dist/engine/events.js.map +0 -1
  59. package/dist/engine/reasoner.d.ts.map +0 -1
  60. package/dist/engine/reasoner.js.map +0 -1
  61. package/dist/engine/session-store.d.ts.map +0 -1
  62. package/dist/engine/session-store.js.map +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/index.js.map +0 -1
  65. package/dist/instructions.md +0 -147
  66. package/dist/lib/errors.d.ts.map +0 -1
  67. package/dist/lib/errors.js.map +0 -1
  68. package/dist/lib/formatting.d.ts.map +0 -1
  69. package/dist/lib/formatting.js.map +0 -1
  70. package/dist/lib/instructions.d.ts +0 -5
  71. package/dist/lib/instructions.d.ts.map +0 -1
  72. package/dist/lib/instructions.js +0 -14
  73. package/dist/lib/instructions.js.map +0 -1
  74. package/dist/lib/text.d.ts.map +0 -1
  75. package/dist/lib/text.js.map +0 -1
  76. package/dist/lib/tool-response.d.ts.map +0 -1
  77. package/dist/lib/tool-response.js.map +0 -1
  78. package/dist/lib/types.d.ts.map +0 -1
  79. package/dist/lib/types.js.map +0 -1
  80. package/dist/lib/validators.d.ts.map +0 -1
  81. package/dist/lib/validators.js.map +0 -1
  82. package/dist/prompts/index.d.ts.map +0 -1
  83. package/dist/prompts/index.js.map +0 -1
  84. package/dist/resources/index.d.ts.map +0 -1
  85. package/dist/resources/index.js.map +0 -1
  86. package/dist/schemas/inputs.d.ts.map +0 -1
  87. package/dist/schemas/inputs.js.map +0 -1
  88. package/dist/schemas/outputs.d.ts.map +0 -1
  89. package/dist/schemas/outputs.js.map +0 -1
  90. package/dist/server.d.ts.map +0 -1
  91. package/dist/server.js.map +0 -1
  92. package/dist/tools/index.d.ts.map +0 -1
  93. package/dist/tools/index.js.map +0 -1
  94. package/dist/tools/reasoning-think.d.ts.map +0 -1
  95. 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 { LEVEL_CONFIGS } from './config.js';
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 / 4));
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 = Math.max(10, Math.min(60_000, ttlMs));
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 = LEVEL_CONFIGS[level];
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.sortedSessionIdsCache) {
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.emitSessionsListChanged();
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
- session.updatedAt = Date.now();
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
- session.updatedAt = Date.now();
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
- const session = this.sessions.get(sessionId);
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 = 'cancelled';
157
- session.updatedAt = Date.now();
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
- engineEvents.emit('session:evicted', {
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
- engineEvents.emit('session:evicted', {
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
- for (const session of this.sessions.values()) {
207
- if (session.updatedAt + this.ttlMs < now) {
208
- expiredSessionIds.push(session.id);
209
- }
210
- }
211
- for (const sessionId of expiredSessionIds) {
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
- engineEvents.emit('session:expired', { sessionId });
216
- changed = true;
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.emitSessionsListChanged();
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
- const newest = this.sessionOrder.get(this.newestSessionId);
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
- const previous = this.sessionOrder.get(prevId);
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
- const next = this.sessionOrder.get(nextId);
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
- const newest = this.sessionOrder.get(this.newestSessionId);
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
- const previous = this.sessionOrder.get(prevId);
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
- const next = this.sessionOrder.get(nextId);
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
- thoughts: session.thoughts.map((thought) => this.snapshotThought(thought)),
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
@@ -1,3 +1,2 @@
1
1
  #!/usr/bin/env node
2
2
  export {};
3
- //# sourceMappingURL=index.d.ts.map
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, 'fatal error');
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
@@ -16,4 +16,3 @@ interface ErrorResponse {
16
16
  export declare function getErrorMessage(error: unknown): string;
17
17
  export declare function createErrorResponse(code: string, message: string): ErrorResponse;
18
18
  export {};
19
- //# sourceMappingURL=errors.d.ts.map
@@ -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 'Unknown error';
33
+ return UNKNOWN_ERROR_MESSAGE;
24
34
  }
25
- if (typeof error === 'object') {
26
- const maybeError = error;
27
- if (typeof maybeError.message === 'string') {
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: JSON.stringify(structured) }],
45
+ content: [{ type: 'text', text }],
37
46
  structuredContent: structured,
38
47
  isError: true,
39
48
  };
40
49
  }
41
- //# sourceMappingURL=errors.js.map
@@ -30,4 +30,3 @@ export declare function formatThoughtsToMarkdown(session: Readonly<Session>, ran
30
30
  start: number;
31
31
  end: number;
32
32
  }): string;
33
- //# sourceMappingURL=formatting.d.ts.map
@@ -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 < thought.content.length) {
23
- const startIdx = thought.content.indexOf(PIN_START, searchFrom);
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 = thought.content.indexOf('-->', startIdx + PIN_START.length);
30
+ const arrowIdx = content.indexOf('-->', startIdx + PIN_START.length);
28
31
  if (arrowIdx === -1) {
29
32
  break;
30
33
  }
31
- const title = thought.content
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 = thought.content.indexOf(PIN_END, contentStart);
36
+ const endIdx = content.indexOf(PIN_END, contentStart);
36
37
  if (endIdx === -1) {
37
38
  break;
38
39
  }
39
- const content = thought.content.slice(contentStart, endIdx).trim();
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, { title, content, thoughtIndex: thought.index });
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 = ['## ๐Ÿ“Œ Pinned', ''];
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
- let thoughts;
86
- if (range) {
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 (isFullTrace && thoughts.length > 0) {
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 (isFullTrace && thoughts.length > 0) {
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
- const heading = formatThoughtHeading(thought);
111
- sections.push(`${heading}\n\n${thought.content}`);
119
+ sections.push(renderThoughtSection(thought));
112
120
  }
113
- return sections.join('\n\n---\n\n');
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[];