tracelattice 1.3.3 → 1.3.4
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/ServerConfig.d.ts +14 -0
- package/dist/ServerConfig.d.ts.map +1 -1
- package/dist/ServerConfig.js +12 -1
- package/dist/ServerConfig.js.map +1 -1
- package/dist/__tests__/core/HistoryManager.ownership.test.d.ts +2 -0
- package/dist/__tests__/core/HistoryManager.ownership.test.d.ts.map +1 -0
- package/dist/__tests__/core/SessionLock.test.d.ts +6 -0
- package/dist/__tests__/core/SessionLock.test.d.ts.map +1 -0
- package/dist/__tests__/core/SessionManager.test.d.ts +8 -0
- package/dist/__tests__/core/SessionManager.test.d.ts.map +1 -0
- package/dist/__tests__/core/ThoughtProcessor.toolAllowlist.test.d.ts +2 -0
- package/dist/__tests__/core/ThoughtProcessor.toolAllowlist.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/factories.d.ts +7 -0
- package/dist/__tests__/helpers/factories.d.ts.map +1 -1
- package/dist/__tests__/sanitize.enforceJsonShape.test.d.ts +2 -0
- package/dist/__tests__/sanitize.enforceJsonShape.test.d.ts.map +1 -0
- package/dist/__tests__/transport-owner-context.test.d.ts +8 -0
- package/dist/__tests__/transport-owner-context.test.d.ts.map +1 -0
- package/dist/cli.js +176 -57
- package/dist/config/ConfigLoader.d.ts +7 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +6 -1
- package/dist/config/ConfigLoader.js.map +1 -1
- package/dist/context/RequestContext.d.ts +26 -0
- package/dist/context/RequestContext.d.ts.map +1 -1
- package/dist/context/RequestContext.js +7 -1
- package/dist/context/RequestContext.js.map +1 -1
- package/dist/contracts/features.d.ts +2 -2
- package/dist/contracts/features.js.map +1 -1
- package/dist/contracts/interfaces.d.ts +42 -0
- package/dist/contracts/interfaces.d.ts.map +1 -1
- package/dist/core/HistoryManager.d.ts +13 -1
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +25 -14
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/InputNormalizer.d.ts.map +1 -1
- package/dist/core/InputNormalizer.js +5 -2
- package/dist/core/InputNormalizer.js.map +1 -1
- package/dist/core/SessionLock.d.ts +56 -0
- package/dist/core/SessionLock.d.ts.map +1 -0
- package/dist/core/SessionLock.js +43 -0
- package/dist/core/SessionLock.js.map +1 -0
- package/dist/core/SessionManager.d.ts +18 -3
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +34 -1
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts +21 -2
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +33 -13
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/di/ServiceRegistry.d.ts +3 -3
- package/dist/di/ServiceRegistry.d.ts.map +1 -1
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +38 -2
- package/dist/errors.js.map +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +9 -3
- package/dist/lib.js.map +1 -1
- package/dist/registry/ToolRegistry.d.ts +1 -0
- package/dist/registry/ToolRegistry.d.ts.map +1 -1
- package/dist/registry/ToolRegistry.js +3 -0
- package/dist/registry/ToolRegistry.js.map +1 -1
- package/dist/sanitize.d.ts +70 -0
- package/dist/sanitize.d.ts.map +1 -1
- package/dist/sanitize.js +77 -1
- package/dist/sanitize.js.map +1 -1
- package/dist/schema.d.ts +35 -35
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +15 -5
- package/dist/schema.js.map +1 -1
- package/dist/transport/HttpTransport.d.ts.map +1 -1
- package/dist/transport/HttpTransport.js +9 -3
- package/dist/transport/HttpTransport.js.map +1 -1
- package/dist/transport/SseTransport.d.ts.map +1 -1
- package/dist/transport/SseTransport.js +10 -3
- package/dist/transport/SseTransport.js.map +1 -1
- package/dist/transport/StreamableHttpTransport.d.ts.map +1 -1
- package/dist/transport/StreamableHttpTransport.js +8 -3
- package/dist/transport/StreamableHttpTransport.js.map +1 -1
- package/dist/types/tool.d.ts +1 -1
- package/dist/types/tool.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core/SessionLock.js","sources":["../../src/core/SessionLock.ts"],"sourcesContent":["/**\n * Per-session async lock to prevent concurrent state mutations.\n *\n * Uses chained promises: each lock acquisition waits for the previous\n * holder's promise to resolve. The lock map entry is purged after release\n * (when no waiters chained on top), preventing unbounded memory growth.\n *\n * Different sessions are fully independent: locks for distinct session\n * ids never block each other. `undefined`/empty session ids share a\n * single global slot.\n *\n * @module session-lock\n */\n\nimport type { ISessionLock } from '../contracts/interfaces.js';\nimport { LockTimeoutError } from '../errors.js';\n\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\n\n/**\n * Normalize a session id for keying the internal lock map.\n * Treats `undefined`, `null`, and empty strings as the global session.\n *\n * @internal\n */\nfunction lockKey(sessionId: string | undefined): string {\n\treturn sessionId && sessionId.length > 0 ? sessionId : '__global__';\n}\n\n/**\n * In-memory implementation of {@link ISessionLock}.\n *\n * Maintains a `Map<string, Promise<void>>` where each value is the tail\n * of the lock chain for that session. New acquisitions chain onto the\n * tail and replace it; once they release, the entry is purged unless a\n * later acquisition chained on top.\n *\n * Lock chain integrity is preserved across timeouts and `fn` errors: a\n * waiter's slot is only released after the previous holder actually\n * finishes, even if the waiter aborted via {@link LockTimeoutError}.\n *\n * @example\n * ```typescript\n * const lock = new SessionLock();\n * await lock.withLock('session-a', async () => {\n * // critical section — concurrent calls for 'session-a' wait\n * });\n * ```\n */\nexport class SessionLock implements ISessionLock {\n\tprivate readonly _locks = new Map<string, Promise<void>>();\n\n\t/**\n\t * Number of currently held lock chains. Useful for diagnostics\n\t * and leak assertions in tests.\n\t */\n\tpublic get size(): number {\n\t\treturn this._locks.size;\n\t}\n\n\t/**\n\t * Execute `fn` while holding the lock for the given session.\n\t *\n\t * Concurrent calls for the same session are serialized. Calls for\n\t * different sessions run in parallel. The lock is always released\n\t * (via `finally`) even if `fn` throws.\n\t *\n\t * @param sessionId - Session to lock. Falsy values share a global slot.\n\t * @param fn - Critical section to run while holding the lock.\n\t * @param timeoutMs - Maximum time to wait for the lock (default 5000ms).\n\t * @throws {LockTimeoutError} When the lock cannot be acquired within `timeoutMs`.\n\t */\n\tpublic async withLock<T>(\n\t\tsessionId: string | undefined,\n\t\tfn: () => Promise<T>,\n\t\ttimeoutMs: number = DEFAULT_LOCK_TIMEOUT_MS,\n\t): Promise<T> {\n\t\tconst key = lockKey(sessionId);\n\t\tconst previous = this._locks.get(key) ?? Promise.resolve();\n\n\t\t// `next` is the tail this acquirer publishes to the chain. It only\n\t\t// resolves after `previous` settles, guaranteeing serialization even\n\t\t// when the current acquirer times out before holding the lock.\n\t\tlet release!: () => void;\n\t\tconst next = previous.then(\n\t\t\t() => new Promise<void>((resolve) => {\n\t\t\t\trelease = resolve;\n\t\t\t}),\n\t\t\t() => new Promise<void>((resolve) => {\n\t\t\t\trelease = resolve;\n\t\t\t}),\n\t\t);\n\t\tthis._locks.set(key, next);\n\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\ttimeoutId = setTimeout(\n\t\t\t\t\t() => reject(new LockTimeoutError(key, timeoutMs)),\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t);\n\t\t\t\tprevious.then(\n\t\t\t\t\t() => resolve(),\n\t\t\t\t\t() => resolve(), // previous holder's failure must not poison the chain\n\t\t\t\t);\n\t\t\t});\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tif (timeoutId !== undefined) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\t// Only purge the chain tail if no later acquisition chained on top.\n\t\t\tif (this._locks.get(key) === next) {\n\t\t\t\tthis._locks.delete(key);\n\t\t\t}\n\t\t\t// `release` is assigned inside the `previous.then(...)` callback.\n\t\t\t// If the timeout fired before `previous` resolved, `release` may\n\t\t\t// not yet exist — wait for `previous` to settle, then release.\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t} else {\n\t\t\t\tconst safeRelease = (): void => {\n\t\t\t\t\tif (release) release();\n\t\t\t\t};\n\t\t\t\tprevious.then(safeRelease, safeRelease);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":["DEFAULT_LOCK_TIMEOUT_MS","lockKey","sessionId","SessionLock","Map","fn","timeoutMs","key","previous","Promise","release","next","resolve","timeoutId","reject","setTimeout","LockTimeoutError","undefined","clearTimeout","safeRelease"],"mappings":";AAiBA,MAAMA,0BAA0B;AAQhC,SAASC,QAAQC,SAA6B;IAC7C,OAAOA,aAAaA,UAAU,MAAM,GAAG,IAAIA,YAAY;AACxD;AAsBO,MAAMC;IACK,SAAS,IAAIC,MAA6B;IAM3D,IAAW,OAAe;QACzB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI;IACxB;IAcA,MAAa,SACZF,SAA6B,EAC7BG,EAAoB,EACpBC,YAAoBN,uBAAuB,EAC9B;QACb,MAAMO,MAAMN,QAAQC;QACpB,MAAMM,WAAW,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD,QAAQE,QAAQ,OAAO;QAKxD,IAAIC;QACJ,MAAMC,OAAOH,SAAS,IAAI,CACzB,IAAM,IAAIC,QAAc,CAACG;gBACxBF,UAAUE;YACX,IACA,IAAM,IAAIH,QAAc,CAACG;gBACxBF,UAAUE;YACX;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAACL,KAAKI;QAErB,IAAIE;QACJ,IAAI;YACH,MAAM,IAAIJ,QAAc,CAACG,SAASE;gBACjCD,YAAYE,WACX,IAAMD,OAAO,IAAIE,iBAAiBT,KAAKD,aACvCA;gBAEDE,SAAS,IAAI,CACZ,IAAMI,WACN,IAAMA;YAER;YACA,OAAO,MAAMP;QACd,SAAU;YACT,IAAIQ,AAAcI,WAAdJ,WACHK,aAAaL;YAGd,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAACN,SAASI,MAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAACJ;YAKpB,IAAIG,SACHA;iBACM;gBACN,MAAMS,cAAc;oBACnB,IAAIT,SAASA;gBACd;gBACAF,SAAS,IAAI,CAACW,aAAaA;YAC5B;QACD;IACD;AACD"}
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* @module SessionManager
|
|
8
8
|
*/
|
|
9
9
|
import type { Logger } from '../logger/StructuredLogger.js';
|
|
10
|
-
/** Minimal session contract — anything with a `lastAccessedAt` timestamp. */
|
|
10
|
+
/** Minimal session contract — anything with a `lastAccessedAt` timestamp and optional owner. */
|
|
11
11
|
export interface SessionLike {
|
|
12
12
|
lastAccessedAt: number;
|
|
13
|
+
/** Owner identifier for per-owner LRU quota. Undefined for stdio/global sessions. */
|
|
14
|
+
owner?: string;
|
|
13
15
|
}
|
|
14
16
|
/** Configuration options for SessionManager. */
|
|
15
17
|
export interface SessionManagerConfig {
|
|
@@ -21,6 +23,8 @@ export interface SessionManagerConfig {
|
|
|
21
23
|
cleanupIntervalMs: number;
|
|
22
24
|
/** Returns the current MAX_SESSIONS limit (callable so tests can mutate). */
|
|
23
25
|
getMaxSessions: () => number;
|
|
26
|
+
/** Maximum sessions per owner (per-owner LRU bucket). @default 50 */
|
|
27
|
+
maxSessionsPerOwner?: number;
|
|
24
28
|
logger?: Logger;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
@@ -32,6 +36,7 @@ export declare class SessionManager<S extends SessionLike> {
|
|
|
32
36
|
private readonly _sessionTtlMs;
|
|
33
37
|
private readonly _cleanupIntervalMs;
|
|
34
38
|
private readonly _getMaxSessions;
|
|
39
|
+
private readonly _maxSessionsPerOwner;
|
|
35
40
|
private readonly _logger;
|
|
36
41
|
private _cleanupTimer;
|
|
37
42
|
constructor(config: SessionManagerConfig);
|
|
@@ -50,9 +55,19 @@ export declare class SessionManager<S extends SessionLike> {
|
|
|
50
55
|
*/
|
|
51
56
|
cleanupStaleSessions(sessions: Map<string, S>): void;
|
|
52
57
|
/**
|
|
53
|
-
* Evicts oldest sessions when the configured
|
|
54
|
-
*
|
|
58
|
+
* Evicts oldest sessions when the configured maximums are exceeded.
|
|
59
|
+
*
|
|
60
|
+
* Two-stage policy:
|
|
61
|
+
* 1. Per-owner LRU: each owner is capped at `maxSessionsPerOwner`. Sessions
|
|
62
|
+
* without an owner (stdio path) are exempt from per-owner quota.
|
|
63
|
+
* 2. Global LRU cap: enforces overall `getMaxSessions()` limit.
|
|
64
|
+
*
|
|
65
|
+
* The default session is never evicted at either stage.
|
|
55
66
|
*/
|
|
56
67
|
evictExcessSessions(sessions: Map<string, S>): void;
|
|
68
|
+
/** Per-owner quota enforcement: oldest sessions per owner bucket are evicted. */
|
|
69
|
+
private _evictPerOwnerOverflow;
|
|
70
|
+
/** Global LRU cap enforcement: evicts oldest until under `getMaxSessions()`. */
|
|
71
|
+
private _evictGlobalOverflow;
|
|
57
72
|
}
|
|
58
73
|
//# sourceMappingURL=SessionManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/core/SessionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,
|
|
1
|
+
{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/core/SessionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,gGAAgG;AAChG,MAAM,WAAW,WAAW;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACpC,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,cAAc,CAAC,CAAC,SAAS,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,aAAa,CAA+C;gBAExD,MAAM,EAAE,oBAAoB;IASxC,qEAAqE;IACrE,IAAW,KAAK,IAAI,UAAU,CAAC,OAAO,WAAW,CAAC,GAAG,IAAI,CAExD;IAED;;;OAGG;IACI,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI;IAcxD,gDAAgD;IACzC,gBAAgB,IAAI,IAAI;IAO/B;;;OAGG;IACI,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI;IAW3D;;;;;;;;;OASG;IACI,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI;IAK1D,iFAAiF;IACjF,OAAO,CAAC,sBAAsB;IA8B9B,gFAAgF;IAChF,OAAO,CAAC,oBAAoB;CAoB5B"}
|
|
@@ -4,6 +4,7 @@ class SessionManager {
|
|
|
4
4
|
_sessionTtlMs;
|
|
5
5
|
_cleanupIntervalMs;
|
|
6
6
|
_getMaxSessions;
|
|
7
|
+
_maxSessionsPerOwner;
|
|
7
8
|
_logger;
|
|
8
9
|
_cleanupTimer = null;
|
|
9
10
|
constructor(config){
|
|
@@ -11,6 +12,7 @@ class SessionManager {
|
|
|
11
12
|
this._sessionTtlMs = config.sessionTtlMs;
|
|
12
13
|
this._cleanupIntervalMs = config.cleanupIntervalMs;
|
|
13
14
|
this._getMaxSessions = config.getMaxSessions;
|
|
15
|
+
this._maxSessionsPerOwner = config.maxSessionsPerOwner ?? 50;
|
|
14
16
|
this._logger = config.logger ?? new NullLogger();
|
|
15
17
|
}
|
|
16
18
|
get timer() {
|
|
@@ -41,6 +43,37 @@ class SessionManager {
|
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
evictExcessSessions(sessions) {
|
|
46
|
+
this._evictPerOwnerOverflow(sessions);
|
|
47
|
+
this._evictGlobalOverflow(sessions);
|
|
48
|
+
}
|
|
49
|
+
_evictPerOwnerOverflow(sessions) {
|
|
50
|
+
const ownerCounts = new Map();
|
|
51
|
+
for (const [key, session] of sessions)if (key !== this._defaultSessionId) {
|
|
52
|
+
if (void 0 !== session.owner) ownerCounts.set(session.owner, (ownerCounts.get(session.owner) ?? 0) + 1);
|
|
53
|
+
}
|
|
54
|
+
const maxPerOwner = this._maxSessionsPerOwner;
|
|
55
|
+
for (const [owner, count] of ownerCounts){
|
|
56
|
+
if (count <= maxPerOwner) continue;
|
|
57
|
+
const ownerSessions = [];
|
|
58
|
+
for (const [key, session] of sessions)if (key !== this._defaultSessionId) {
|
|
59
|
+
if (session.owner === owner) ownerSessions.push([
|
|
60
|
+
key,
|
|
61
|
+
session
|
|
62
|
+
]);
|
|
63
|
+
}
|
|
64
|
+
ownerSessions.sort(([, a], [, b])=>a.lastAccessedAt - b.lastAccessedAt);
|
|
65
|
+
const toEvict = count - maxPerOwner;
|
|
66
|
+
for(let i = 0; i < toEvict && i < ownerSessions.length; i++){
|
|
67
|
+
const [evictKey] = ownerSessions[i];
|
|
68
|
+
sessions.delete(evictKey);
|
|
69
|
+
this._logger.info('Evicted oldest session (per-owner LRU)', {
|
|
70
|
+
sessionId: evictKey,
|
|
71
|
+
owner
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
_evictGlobalOverflow(sessions) {
|
|
44
77
|
const max = this._getMaxSessions();
|
|
45
78
|
while(sessions.size > max){
|
|
46
79
|
let oldestKey = null;
|
|
@@ -53,7 +86,7 @@ class SessionManager {
|
|
|
53
86
|
}
|
|
54
87
|
if (null !== oldestKey) {
|
|
55
88
|
sessions.delete(oldestKey);
|
|
56
|
-
this._logger.info('Evicted oldest session (LRU)', {
|
|
89
|
+
this._logger.info('Evicted oldest session (global LRU)', {
|
|
57
90
|
sessionId: oldestKey
|
|
58
91
|
});
|
|
59
92
|
} else break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/SessionManager.js","sources":["../../src/core/SessionManager.ts"],"sourcesContent":["/**\n * SessionManager — owns periodic session cleanup timer and TTL/LRU eviction logic.\n *\n * Extracted from HistoryManager. The session Map remains owned by HistoryManager\n * (passed by reference) so that public access patterns are preserved.\n *\n * @module SessionManager\n */\n\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { NullLogger } from '../logger/NullLogger.js';\n\n/** Minimal session contract — anything with a `lastAccessedAt` timestamp. */\nexport interface SessionLike {\n\tlastAccessedAt: number;\n}\n\n/** Configuration options for SessionManager. */\nexport interface SessionManagerConfig {\n\t/** Default session key that must never be evicted. */\n\tdefaultSessionId: string;\n\t/** TTL for inactive sessions in milliseconds. */\n\tsessionTtlMs: number;\n\t/** Periodic cleanup interval in milliseconds. */\n\tcleanupIntervalMs: number;\n\t/** Returns the current MAX_SESSIONS limit (callable so tests can mutate). */\n\tgetMaxSessions: () => number;\n\tlogger?: Logger;\n}\n\n/**\n * Manages session lifecycle: periodic stale-session cleanup and LRU eviction\n * when the session count exceeds the configured maximum.\n */\nexport class SessionManager<S extends SessionLike> {\n\tprivate readonly _defaultSessionId: string;\n\tprivate readonly _sessionTtlMs: number;\n\tprivate readonly _cleanupIntervalMs: number;\n\tprivate readonly _getMaxSessions: () => number;\n\tprivate readonly _logger: Logger;\n\tprivate _cleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(config: SessionManagerConfig) {\n\t\tthis._defaultSessionId = config.defaultSessionId;\n\t\tthis._sessionTtlMs = config.sessionTtlMs;\n\t\tthis._cleanupIntervalMs = config.cleanupIntervalMs;\n\t\tthis._getMaxSessions = config.getMaxSessions;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t}\n\n\t/** Returns the underlying cleanup timer (for test introspection). */\n\tpublic get timer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._cleanupTimer;\n\t}\n\n\t/**\n\t * Starts the periodic session cleanup timer. No-op if already started.\n\t * The timer is unref'd so it does not block process exit.\n\t */\n\tpublic startCleanupTimer(sessions: Map<string, S>): void {\n\t\tif (this._cleanupTimer !== null) return;\n\t\tthis._cleanupTimer = setInterval(() => {\n\t\t\tthis.cleanupStaleSessions(sessions);\n\t\t}, this._cleanupIntervalMs);\n\t\tif (\n\t\t\tthis._cleanupTimer &&\n\t\t\ttypeof this._cleanupTimer === 'object' &&\n\t\t\t'unref' in this._cleanupTimer\n\t\t) {\n\t\t\tthis._cleanupTimer.unref();\n\t\t}\n\t}\n\n\t/** Stops the periodic session cleanup timer. */\n\tpublic stopCleanupTimer(): void {\n\t\tif (this._cleanupTimer !== null) {\n\t\t\tclearInterval(this._cleanupTimer);\n\t\t\tthis._cleanupTimer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Evicts sessions that have been inactive longer than `sessionTtlMs`.\n\t * The default session is never evicted.\n\t */\n\tpublic cleanupStaleSessions(sessions: Map<string, S>): void {\n\t\tconst now = Date.now();\n\t\tfor (const [key, session] of sessions) {\n\t\t\tif (key === this._defaultSessionId) continue;\n\t\t\tif (now - session.lastAccessedAt > this._sessionTtlMs) {\n\t\t\t\tsessions.delete(key);\n\t\t\t\tthis._logger.info('Evicted stale session', { sessionId: key });\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Evicts oldest sessions when the configured
|
|
1
|
+
{"version":3,"file":"core/SessionManager.js","sources":["../../src/core/SessionManager.ts"],"sourcesContent":["/**\n * SessionManager — owns periodic session cleanup timer and TTL/LRU eviction logic.\n *\n * Extracted from HistoryManager. The session Map remains owned by HistoryManager\n * (passed by reference) so that public access patterns are preserved.\n *\n * @module SessionManager\n */\n\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { NullLogger } from '../logger/NullLogger.js';\n\n/** Minimal session contract — anything with a `lastAccessedAt` timestamp and optional owner. */\nexport interface SessionLike {\n\tlastAccessedAt: number;\n\t/** Owner identifier for per-owner LRU quota. Undefined for stdio/global sessions. */\n\towner?: string;\n}\n\n/** Configuration options for SessionManager. */\nexport interface SessionManagerConfig {\n\t/** Default session key that must never be evicted. */\n\tdefaultSessionId: string;\n\t/** TTL for inactive sessions in milliseconds. */\n\tsessionTtlMs: number;\n\t/** Periodic cleanup interval in milliseconds. */\n\tcleanupIntervalMs: number;\n\t/** Returns the current MAX_SESSIONS limit (callable so tests can mutate). */\n\tgetMaxSessions: () => number;\n\t/** Maximum sessions per owner (per-owner LRU bucket). @default 50 */\n\tmaxSessionsPerOwner?: number;\n\tlogger?: Logger;\n}\n\n/**\n * Manages session lifecycle: periodic stale-session cleanup and LRU eviction\n * when the session count exceeds the configured maximum.\n */\nexport class SessionManager<S extends SessionLike> {\n\tprivate readonly _defaultSessionId: string;\n\tprivate readonly _sessionTtlMs: number;\n\tprivate readonly _cleanupIntervalMs: number;\n\tprivate readonly _getMaxSessions: () => number;\n\tprivate readonly _maxSessionsPerOwner: number;\n\tprivate readonly _logger: Logger;\n\tprivate _cleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(config: SessionManagerConfig) {\n\t\tthis._defaultSessionId = config.defaultSessionId;\n\t\tthis._sessionTtlMs = config.sessionTtlMs;\n\t\tthis._cleanupIntervalMs = config.cleanupIntervalMs;\n\t\tthis._getMaxSessions = config.getMaxSessions;\n\t\tthis._maxSessionsPerOwner = config.maxSessionsPerOwner ?? 50;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t}\n\n\t/** Returns the underlying cleanup timer (for test introspection). */\n\tpublic get timer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._cleanupTimer;\n\t}\n\n\t/**\n\t * Starts the periodic session cleanup timer. No-op if already started.\n\t * The timer is unref'd so it does not block process exit.\n\t */\n\tpublic startCleanupTimer(sessions: Map<string, S>): void {\n\t\tif (this._cleanupTimer !== null) return;\n\t\tthis._cleanupTimer = setInterval(() => {\n\t\t\tthis.cleanupStaleSessions(sessions);\n\t\t}, this._cleanupIntervalMs);\n\t\tif (\n\t\t\tthis._cleanupTimer &&\n\t\t\ttypeof this._cleanupTimer === 'object' &&\n\t\t\t'unref' in this._cleanupTimer\n\t\t) {\n\t\t\tthis._cleanupTimer.unref();\n\t\t}\n\t}\n\n\t/** Stops the periodic session cleanup timer. */\n\tpublic stopCleanupTimer(): void {\n\t\tif (this._cleanupTimer !== null) {\n\t\t\tclearInterval(this._cleanupTimer);\n\t\t\tthis._cleanupTimer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Evicts sessions that have been inactive longer than `sessionTtlMs`.\n\t * The default session is never evicted.\n\t */\n\tpublic cleanupStaleSessions(sessions: Map<string, S>): void {\n\t\tconst now = Date.now();\n\t\tfor (const [key, session] of sessions) {\n\t\t\tif (key === this._defaultSessionId) continue;\n\t\t\tif (now - session.lastAccessedAt > this._sessionTtlMs) {\n\t\t\t\tsessions.delete(key);\n\t\t\t\tthis._logger.info('Evicted stale session', { sessionId: key });\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Evicts oldest sessions when the configured maximums are exceeded.\n\t *\n\t * Two-stage policy:\n\t * 1. Per-owner LRU: each owner is capped at `maxSessionsPerOwner`. Sessions\n\t * without an owner (stdio path) are exempt from per-owner quota.\n\t * 2. Global LRU cap: enforces overall `getMaxSessions()` limit.\n\t *\n\t * The default session is never evicted at either stage.\n\t */\n\tpublic evictExcessSessions(sessions: Map<string, S>): void {\n\t\tthis._evictPerOwnerOverflow(sessions);\n\t\tthis._evictGlobalOverflow(sessions);\n\t}\n\n\t/** Per-owner quota enforcement: oldest sessions per owner bucket are evicted. */\n\tprivate _evictPerOwnerOverflow(sessions: Map<string, S>): void {\n\t\tconst ownerCounts = new Map<string, number>();\n\t\tfor (const [key, session] of sessions) {\n\t\t\tif (key === this._defaultSessionId) continue;\n\t\t\tif (session.owner === undefined) continue;\n\t\t\townerCounts.set(session.owner, (ownerCounts.get(session.owner) ?? 0) + 1);\n\t\t}\n\n\t\tconst maxPerOwner = this._maxSessionsPerOwner;\n\t\tfor (const [owner, count] of ownerCounts) {\n\t\t\tif (count <= maxPerOwner) continue;\n\t\t\tconst ownerSessions: Array<[string, S]> = [];\n\t\t\tfor (const [key, session] of sessions) {\n\t\t\t\tif (key === this._defaultSessionId) continue;\n\t\t\t\tif (session.owner !== owner) continue;\n\t\t\t\townerSessions.push([key, session]);\n\t\t\t}\n\t\t\townerSessions.sort(([, a], [, b]) => a.lastAccessedAt - b.lastAccessedAt);\n\t\t\tconst toEvict = count - maxPerOwner;\n\t\t\tfor (let i = 0; i < toEvict && i < ownerSessions.length; i++) {\n\t\t\t\tconst [evictKey] = ownerSessions[i]!;\n\t\t\t\tsessions.delete(evictKey);\n\t\t\t\tthis._logger.info('Evicted oldest session (per-owner LRU)', {\n\t\t\t\t\tsessionId: evictKey,\n\t\t\t\t\towner,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Global LRU cap enforcement: evicts oldest until under `getMaxSessions()`. */\n\tprivate _evictGlobalOverflow(sessions: Map<string, S>): void {\n\t\tconst max = this._getMaxSessions();\n\t\twhile (sessions.size > max) {\n\t\t\tlet oldestKey: string | null = null;\n\t\t\tlet oldestTime = Infinity;\n\t\t\tfor (const [key, session] of sessions) {\n\t\t\t\tif (key === this._defaultSessionId) continue;\n\t\t\t\tif (session.lastAccessedAt < oldestTime) {\n\t\t\t\t\toldestTime = session.lastAccessedAt;\n\t\t\t\t\toldestKey = key;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (oldestKey !== null) {\n\t\t\t\tsessions.delete(oldestKey);\n\t\t\t\tthis._logger.info('Evicted oldest session (global LRU)', { sessionId: oldestKey });\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":["SessionManager","config","NullLogger","sessions","setInterval","clearInterval","now","Date","key","session","ownerCounts","Map","undefined","maxPerOwner","owner","count","ownerSessions","a","b","toEvict","i","evictKey","max","oldestKey","oldestTime","Infinity"],"mappings":";AAsCO,MAAMA;IACK,kBAA0B;IAC1B,cAAsB;IACtB,mBAA2B;IAC3B,gBAA8B;IAC9B,qBAA6B;IAC7B,QAAgB;IACzB,gBAAuD,KAAK;IAEpE,YAAYC,MAA4B,CAAE;QACzC,IAAI,CAAC,iBAAiB,GAAGA,OAAO,gBAAgB;QAChD,IAAI,CAAC,aAAa,GAAGA,OAAO,YAAY;QACxC,IAAI,CAAC,kBAAkB,GAAGA,OAAO,iBAAiB;QAClD,IAAI,CAAC,eAAe,GAAGA,OAAO,cAAc;QAC5C,IAAI,CAAC,oBAAoB,GAAGA,OAAO,mBAAmB,IAAI;QAC1D,IAAI,CAAC,OAAO,GAAGA,OAAO,MAAM,IAAI,IAAIC;IACrC;IAGA,IAAW,QAA+C;QACzD,OAAO,IAAI,CAAC,aAAa;IAC1B;IAMO,kBAAkBC,QAAwB,EAAQ;QACxD,IAAI,AAAuB,SAAvB,IAAI,CAAC,aAAa,EAAW;QACjC,IAAI,CAAC,aAAa,GAAGC,YAAY;YAChC,IAAI,CAAC,oBAAoB,CAACD;QAC3B,GAAG,IAAI,CAAC,kBAAkB;QAC1B,IACC,IAAI,CAAC,aAAa,IAClB,AAA8B,YAA9B,OAAO,IAAI,CAAC,aAAa,IACzB,WAAW,IAAI,CAAC,aAAa,EAE7B,IAAI,CAAC,aAAa,CAAC,KAAK;IAE1B;IAGO,mBAAyB;QAC/B,IAAI,AAAuB,SAAvB,IAAI,CAAC,aAAa,EAAW;YAChCE,cAAc,IAAI,CAAC,aAAa;YAChC,IAAI,CAAC,aAAa,GAAG;QACtB;IACD;IAMO,qBAAqBF,QAAwB,EAAQ;QAC3D,MAAMG,MAAMC,KAAK,GAAG;QACpB,KAAK,MAAM,CAACC,KAAKC,QAAQ,IAAIN,SAC5B,IAAIK,QAAQ,IAAI,CAAC,iBAAiB,EAClC;YAAA,IAAIF,MAAMG,QAAQ,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE;gBACtDN,SAAS,MAAM,CAACK;gBAChB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,yBAAyB;oBAAE,WAAWA;gBAAI;YAC7D;QAAA;IAEF;IAYO,oBAAoBL,QAAwB,EAAQ;QAC1D,IAAI,CAAC,sBAAsB,CAACA;QAC5B,IAAI,CAAC,oBAAoB,CAACA;IAC3B;IAGQ,uBAAuBA,QAAwB,EAAQ;QAC9D,MAAMO,cAAc,IAAIC;QACxB,KAAK,MAAM,CAACH,KAAKC,QAAQ,IAAIN,SAC5B,IAAIK,QAAQ,IAAI,CAAC,iBAAiB,EAClC;YAAA,IAAIC,AAAkBG,WAAlBH,QAAQ,KAAK,EACjBC,YAAY,GAAG,CAACD,QAAQ,KAAK,EAAGC,AAAAA,CAAAA,YAAY,GAAG,CAACD,QAAQ,KAAK,KAAK,KAAK;QAD9B;QAI1C,MAAMI,cAAc,IAAI,CAAC,oBAAoB;QAC7C,KAAK,MAAM,CAACC,OAAOC,MAAM,IAAIL,YAAa;YACzC,IAAIK,SAASF,aAAa;YAC1B,MAAMG,gBAAoC,EAAE;YAC5C,KAAK,MAAM,CAACR,KAAKC,QAAQ,IAAIN,SAC5B,IAAIK,QAAQ,IAAI,CAAC,iBAAiB,EAClC;gBAAA,IAAIC,QAAQ,KAAK,KAAKK,OACtBE,cAAc,IAAI,CAAC;oBAACR;oBAAKC;iBAAQ;YADI;YAGtCO,cAAc,IAAI,CAAC,CAAC,GAAGC,EAAE,EAAE,GAAGC,EAAE,GAAKD,EAAE,cAAc,GAAGC,EAAE,cAAc;YACxE,MAAMC,UAAUJ,QAAQF;YACxB,IAAK,IAAIO,IAAI,GAAGA,IAAID,WAAWC,IAAIJ,cAAc,MAAM,EAAEI,IAAK;gBAC7D,MAAM,CAACC,SAAS,GAAGL,aAAa,CAACI,EAAE;gBACnCjB,SAAS,MAAM,CAACkB;gBAChB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,0CAA0C;oBAC3D,WAAWA;oBACXP;gBACD;YACD;QACD;IACD;IAGQ,qBAAqBX,QAAwB,EAAQ;QAC5D,MAAMmB,MAAM,IAAI,CAAC,eAAe;QAChC,MAAOnB,SAAS,IAAI,GAAGmB,IAAK;YAC3B,IAAIC,YAA2B;YAC/B,IAAIC,aAAaC;YACjB,KAAK,MAAM,CAACjB,KAAKC,QAAQ,IAAIN,SAC5B,IAAIK,QAAQ,IAAI,CAAC,iBAAiB,EAClC;gBAAA,IAAIC,QAAQ,cAAc,GAAGe,YAAY;oBACxCA,aAAaf,QAAQ,cAAc;oBACnCc,YAAYf;gBACb;YAAA;YAED,IAAIe,AAAc,SAAdA,WAAoB;gBACvBpB,SAAS,MAAM,CAACoB;gBAChB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,uCAAuC;oBAAE,WAAWA;gBAAU;YACjF,OACC;QAEF;IACD;AACD"}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import type { Logger } from '../logger/StructuredLogger.js';
|
|
11
11
|
import type { ISuspensionStore } from '../contracts/suspension.js';
|
|
12
12
|
import type { IReasoningStrategy } from '../contracts/strategy.js';
|
|
13
|
-
import type
|
|
13
|
+
import { type FeatureFlags } from '../contracts/features.js';
|
|
14
|
+
import type { ISessionLock, IToolRegistry } from '../contracts/interfaces.js';
|
|
14
15
|
import type { IHistoryManager } from './IHistoryManager.js';
|
|
15
16
|
import type { ThoughtData } from './thought.js';
|
|
16
17
|
import type { ThoughtEvaluator } from './ThoughtEvaluator.js';
|
|
@@ -64,7 +65,9 @@ export declare class ThoughtProcessor {
|
|
|
64
65
|
private readonly strategy;
|
|
65
66
|
private readonly _compressionService?;
|
|
66
67
|
private readonly _suspensionStore?;
|
|
68
|
+
private readonly _toolRegistry?;
|
|
67
69
|
private readonly _features;
|
|
70
|
+
private readonly _sessionLock?;
|
|
68
71
|
/** Logger for debugging and monitoring. */
|
|
69
72
|
private _logger;
|
|
70
73
|
/** Evaluator for quality signal computation. */
|
|
@@ -83,8 +86,12 @@ export declare class ThoughtProcessor {
|
|
|
83
86
|
* @param logger - Optional logger for diagnostics (defaults to NullLogger)
|
|
84
87
|
* @param strategy - Reasoning strategy controlling next-action decisions (defaults to SequentialStrategy)
|
|
85
88
|
* @param compressionService - Optional compression service for auto-compression on terminate
|
|
89
|
+
* @param suspensionStore - Optional suspension store enabling tool interleave
|
|
90
|
+
* @param toolRegistry - Optional tool registry for tool_name allowlist validation (required when toolInterleave is enabled)
|
|
91
|
+
* @param features - Optional feature flags (defaults to DEFAULT_FLAGS — all opt-in flags off)
|
|
92
|
+
* @param sessionLock - Optional per-session async lock; when provided, `process()` runs under it
|
|
86
93
|
*/
|
|
87
|
-
constructor(historyManager: IHistoryManager, thoughtFormatter: ThoughtFormatter, thoughtEvaluator: ThoughtEvaluator, logger?: Logger, strategy?: IReasoningStrategy, _compressionService?: CompressionService | undefined, _suspensionStore?: ISuspensionStore | undefined, _features?: FeatureFlags);
|
|
94
|
+
constructor(historyManager: IHistoryManager, thoughtFormatter: ThoughtFormatter, thoughtEvaluator: ThoughtEvaluator, logger?: Logger, strategy?: IReasoningStrategy, _compressionService?: CompressionService | undefined, _suspensionStore?: ISuspensionStore | undefined, _toolRegistry?: IToolRegistry | undefined, _features?: FeatureFlags, _sessionLock?: ISessionLock | undefined);
|
|
88
95
|
/**
|
|
89
96
|
* Internal logging method.
|
|
90
97
|
* @param message - The message to log
|
|
@@ -152,6 +159,7 @@ export declare class ThoughtProcessor {
|
|
|
152
159
|
* ```
|
|
153
160
|
*/
|
|
154
161
|
process(input: ThoughtData): Promise<CallToolResult>;
|
|
162
|
+
private _processInner;
|
|
155
163
|
/**
|
|
156
164
|
* Run the configured reasoning strategy and return its decision.
|
|
157
165
|
* Strategy errors degrade to `{ action: 'continue' }`.
|
|
@@ -210,6 +218,17 @@ export declare class ThoughtProcessor {
|
|
|
210
218
|
* @private
|
|
211
219
|
*/
|
|
212
220
|
private _validateNewTypes;
|
|
221
|
+
/**
|
|
222
|
+
* Validate a tool_call's tool_name against the configured allowlist.
|
|
223
|
+
*
|
|
224
|
+
* Fails closed: if no tool registry was wired, all tool_call invocations are
|
|
225
|
+
* rejected. This prevents arbitrary tool name injection through the protocol.
|
|
226
|
+
*
|
|
227
|
+
* @param toolName - The tool name from the tool_call thought
|
|
228
|
+
* @throws {UnknownToolError} When no registry is wired or the tool is not registered
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
private _validateToolName;
|
|
213
232
|
/**
|
|
214
233
|
* Checks whether a given thought_number exists in the session history or any branch.
|
|
215
234
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThoughtProcessor.d.ts","sourceRoot":"","sources":["../../src/core/ThoughtProcessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAoB,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ThoughtProcessor.d.ts","sourceRoot":"","sources":["../../src/core/ThoughtProcessor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
import { NullLogger } from "../logger/NullLogger.js";
|
|
2
2
|
import { GLOBAL_SESSION_ID, asSessionId } from "../contracts/ids.js";
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_FLAGS } from "../contracts/features.js";
|
|
4
|
+
import { InvalidBacktrackError, InvalidToolCallError, SequentialThinkingError, SuspensionExpiredError, SuspensionNotFoundError, UnknownToolError, ValidationError, WARNING_CODES, getErrorMessage } from "../errors.js";
|
|
5
|
+
import { JsonShapeError, enforceJsonShape } from "../sanitize.js";
|
|
4
6
|
import { GraphView } from "./graph/GraphView.js";
|
|
5
7
|
import { normalizeInput } from "./InputNormalizer.js";
|
|
6
8
|
import { SequentialStrategy } from "./reasoning/strategies/SequentialStrategy.js";
|
|
7
|
-
const DEFAULT_FEATURES = {
|
|
8
|
-
dagEdges: true,
|
|
9
|
-
reasoningStrategy: 'sequential',
|
|
10
|
-
calibration: true,
|
|
11
|
-
compression: true,
|
|
12
|
-
toolInterleave: true,
|
|
13
|
-
newThoughtTypes: true,
|
|
14
|
-
outcomeRecording: true
|
|
15
|
-
};
|
|
16
9
|
class ThoughtProcessor {
|
|
17
10
|
historyManager;
|
|
18
11
|
thoughtFormatter;
|
|
19
12
|
strategy;
|
|
20
13
|
_compressionService;
|
|
21
14
|
_suspensionStore;
|
|
15
|
+
_toolRegistry;
|
|
22
16
|
_features;
|
|
17
|
+
_sessionLock;
|
|
23
18
|
_logger;
|
|
24
19
|
_thoughtEvaluator;
|
|
25
20
|
_hintCooldowns = new Map();
|
|
26
|
-
constructor(historyManager, thoughtFormatter, thoughtEvaluator, logger, strategy = new SequentialStrategy(), _compressionService, _suspensionStore, _features =
|
|
21
|
+
constructor(historyManager, thoughtFormatter, thoughtEvaluator, logger, strategy = new SequentialStrategy(), _compressionService, _suspensionStore, _toolRegistry, _features = DEFAULT_FLAGS, _sessionLock){
|
|
27
22
|
this.historyManager = historyManager;
|
|
28
23
|
this.thoughtFormatter = thoughtFormatter;
|
|
29
24
|
this.strategy = strategy;
|
|
30
25
|
this._compressionService = _compressionService;
|
|
31
26
|
this._suspensionStore = _suspensionStore;
|
|
27
|
+
this._toolRegistry = _toolRegistry;
|
|
32
28
|
this._features = _features;
|
|
29
|
+
this._sessionLock = _sessionLock;
|
|
33
30
|
this._thoughtEvaluator = thoughtEvaluator;
|
|
34
31
|
this._logger = logger ?? new NullLogger();
|
|
35
32
|
}
|
|
@@ -65,6 +62,11 @@ class ThoughtProcessor {
|
|
|
65
62
|
return hints;
|
|
66
63
|
}
|
|
67
64
|
async process(input) {
|
|
65
|
+
const lock = this._sessionLock;
|
|
66
|
+
if (lock) return lock.withLock(input.session_id, ()=>this._processInner(input));
|
|
67
|
+
return this._processInner(input);
|
|
68
|
+
}
|
|
69
|
+
async _processInner(input) {
|
|
68
70
|
try {
|
|
69
71
|
const normalizedInput = normalizeInput(input);
|
|
70
72
|
const sessionId = normalizedInput.session_id;
|
|
@@ -140,7 +142,11 @@ class ThoughtProcessor {
|
|
|
140
142
|
{
|
|
141
143
|
type: 'text',
|
|
142
144
|
text: JSON.stringify({
|
|
145
|
+
...error instanceof SequentialThinkingError && {
|
|
146
|
+
code: error.code
|
|
147
|
+
},
|
|
143
148
|
error: getErrorMessage(error),
|
|
149
|
+
message: getErrorMessage(error),
|
|
144
150
|
status: 'failed'
|
|
145
151
|
}, null, 2)
|
|
146
152
|
}
|
|
@@ -200,7 +206,7 @@ class ThoughtProcessor {
|
|
|
200
206
|
const warnings = [];
|
|
201
207
|
if (input.thought_number > input.total_thoughts) {
|
|
202
208
|
const originalTotal = input.total_thoughts;
|
|
203
|
-
warnings.push(`Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`);
|
|
209
|
+
warnings.push(`[${WARNING_CODES.TOTAL_THOUGHTS_ADJUSTED}] Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`);
|
|
204
210
|
this._logger.warn('Auto-adjusted total_thoughts to match thought_number', {
|
|
205
211
|
thought_number: input.thought_number,
|
|
206
212
|
original_total_thoughts: originalTotal,
|
|
@@ -288,7 +294,10 @@ class ThoughtProcessor {
|
|
|
288
294
|
const t = input.thought_type;
|
|
289
295
|
if (('tool_call' === t || 'tool_observation' === t) && !this._features.toolInterleave) throw new ValidationError('thought_type', `Type '${t}' requires the toolInterleave feature flag. Set TRACELATTICE_FEATURES_TOOL_INTERLEAVE=true to enable it.`);
|
|
290
296
|
if (('assumption' === t || 'decomposition' === t || 'backtrack' === t) && !this._features.newThoughtTypes) throw new ValidationError('thought_type', `Type '${t}' requires the newThoughtTypes feature flag. Set TRACELATTICE_FEATURES_NEW_THOUGHT_TYPES=true to enable it, or use '${ThoughtProcessor._getWorkaroundType(t)}' type as a workaround.`);
|
|
291
|
-
if ('tool_call' === t
|
|
297
|
+
if ('tool_call' === t) {
|
|
298
|
+
if (!input.tool_name) throw new InvalidToolCallError('tool_call thought ' + input.thought_number + ' missing required tool_name');
|
|
299
|
+
this._validateToolName(input.tool_name);
|
|
300
|
+
}
|
|
292
301
|
if ('tool_observation' === t && !input.continuation_token) throw new ValidationError('continuation_token', 'tool_observation thought ' + input.thought_number + ' missing continuation_token');
|
|
293
302
|
if ('backtrack' === t) {
|
|
294
303
|
if (void 0 === input.backtrack_target) throw new ValidationError('backtrack_target', 'backtrack thought ' + input.thought_number + ' requires backtrack_target');
|
|
@@ -296,6 +305,10 @@ class ThoughtProcessor {
|
|
|
296
305
|
if (!this._thoughtNumberExists(input.backtrack_target, sessionId)) throw new InvalidBacktrackError('backtrack_target ' + input.backtrack_target + ' does not exist in session history');
|
|
297
306
|
}
|
|
298
307
|
}
|
|
308
|
+
_validateToolName(toolName) {
|
|
309
|
+
if (!this._toolRegistry) throw new UnknownToolError(toolName, `Tool '${toolName}' rejected: no tool registry configured. Tool interleave requires a registered allowlist.`);
|
|
310
|
+
if (!this._toolRegistry.has(toolName)) throw new UnknownToolError(toolName);
|
|
311
|
+
}
|
|
299
312
|
_thoughtNumberExists(thoughtNumber, sessionId) {
|
|
300
313
|
const history = this.historyManager.getHistory(sessionId);
|
|
301
314
|
for (const t of history)if (t.thought_number === thoughtNumber) return true;
|
|
@@ -319,6 +332,13 @@ class ThoughtProcessor {
|
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
_handleToolCall(input, sessionId) {
|
|
335
|
+
const args = input.tool_arguments ?? {};
|
|
336
|
+
try {
|
|
337
|
+
enforceJsonShape(args);
|
|
338
|
+
} catch (err) {
|
|
339
|
+
if (err instanceof JsonShapeError) throw new ValidationError('tool_arguments', err.reason);
|
|
340
|
+
throw err;
|
|
341
|
+
}
|
|
322
342
|
this.historyManager.addThought(input);
|
|
323
343
|
const record = this._suspensionStore.suspend({
|
|
324
344
|
sessionId: sessionId ? asSessionId(sessionId) : GLOBAL_SESSION_ID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/ThoughtProcessor.js","sources":["../../src/core/ThoughtProcessor.ts"],"sourcesContent":["/**\n * Core thought processing logic and validation.\n *\n * This module provides the `ThoughtProcessor` class which handles the main\n * sequential thinking request processing pipeline, including input validation,\n * history management, and response formatting.\n *\n * @module processor\n */\n\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { asSessionId, GLOBAL_SESSION_ID } from '../contracts/ids.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISuspensionStore, SuspensionRecord } from '../contracts/suspension.js';\nimport type { IReasoningStrategy, StrategyDecision } from '../contracts/strategy.js';\nimport type { FeatureFlags } from '../contracts/features.js';\nimport {\n\tInvalidBacktrackError,\n\tInvalidToolCallError,\n\tSuspensionExpiredError,\n\tSuspensionNotFoundError,\n\tValidationError,\n} from '../errors.js';\nimport { getErrorMessage } from '../errors.js';\nimport { GraphView } from './graph/GraphView.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport { normalizeInput } from './InputNormalizer.js';\nimport type { ThoughtData } from './thought.js';\nimport type { ThoughtEvaluator } from './ThoughtEvaluator.js';\nimport { ThoughtFormatter } from './ThoughtFormatter.js';\nimport type { PatternSignal } from './reasoning.js';\nimport { SequentialStrategy } from './reasoning/strategies/SequentialStrategy.js';\nimport type { CompressionService } from './compression/CompressionService.js';\n\n/**\n * Internal extension to ThoughtData carrying resume metadata.\n * Attached by `_handleToolObservation` so downstream consumers (e.g. edge\n * emission) can correlate the observation with the suspended `tool_call`.\n */\ntype ResumableThought = ThoughtData & { _resumedFrom?: number };\n\n/** Default feature flags used when none are supplied to ThoughtProcessor. */\nconst DEFAULT_FEATURES: FeatureFlags = {\n\tdagEdges: true,\n\treasoningStrategy: 'sequential',\n\tcalibration: true,\n\tcompression: true,\n\ttoolInterleave: true,\n\tnewThoughtTypes: true,\n\toutcomeRecording: true,\n};\n\n/**\n * The return type expected by MCP tool invocations.\n *\n * This structure matches the MCP protocol for tool results,\n * supporting both success and error responses.\n *\n * @example\n * ```typescript\n * const successResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"status\":\"success\"}' }]\n * };\n *\n * const errorResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"error\":\"Something went wrong\"}' }],\n * isError: true\n * };\n * ```\n */\nexport interface CallToolResult {\n\t/** Array of content blocks (typically text) to return to the client. */\n\tcontent: Array<{\n\t\ttype: 'text';\n\t\ttext: string;\n\t}>;\n\n\t/** Whether this result represents an error condition. */\n\tisError?: boolean;\n}\n\n/**\n * Core processor for sequential thinking requests.\n *\n * Pipeline: validate → normalize → add to history → format → evaluate signals\n * → run reasoning strategy → return structured response.\n *\n * Auto-adjusts `total_thoughts` if `thought_number` exceeds it. All errors are\n * caught and returned as formatted error responses with `isError: true`.\n *\n * @example\n * ```typescript\n * const processor = new ThoughtProcessor(historyManager, formatter, new ThoughtEvaluator());\n * const result = await processor.process({ thought: '...', thought_number: 1, total_thoughts: 5, next_thought_needed: true });\n * ```\n */\nexport class ThoughtProcessor {\n\t/** Logger for debugging and monitoring. */\n\tprivate _logger: Logger;\n\n\t/** Evaluator for quality signal computation. */\n\tprivate readonly _thoughtEvaluator: ThoughtEvaluator;\n\n\t/**\n\t * Per-session cooldown tracker: session_id → pattern → last_fired_thought_number.\n\t * Prevents re-firing the same pattern hint within 3 thoughts.\n\t */\n\tprivate _hintCooldowns = new Map<string, Map<string, number>>();\n\n\t/**\n\t * Creates a new ThoughtProcessor instance.\n\t *\n\t * @param historyManager - History manager for storing thoughts\n\t * @param thoughtFormatter - Formatter for output formatting\n\t * @param thoughtEvaluator - Evaluator for quality signal computation\n\t * @param logger - Optional logger for diagnostics (defaults to NullLogger)\n\t * @param strategy - Reasoning strategy controlling next-action decisions (defaults to SequentialStrategy)\n\t * @param compressionService - Optional compression service for auto-compression on terminate\n\t */\n\tconstructor(\n\t\tprivate historyManager: IHistoryManager,\n\t\tprivate thoughtFormatter: ThoughtFormatter,\n\t\tthoughtEvaluator: ThoughtEvaluator,\n\t\tlogger?: Logger,\n\t\tprivate readonly strategy: IReasoningStrategy = new SequentialStrategy(),\n\t\tprivate readonly _compressionService?: CompressionService,\n\t\tprivate readonly _suspensionStore?: ISuspensionStore,\n\t\tprivate readonly _features: FeatureFlags = DEFAULT_FEATURES\n\t) {\n\t\tthis._thoughtEvaluator = thoughtEvaluator;\n\t\tthis._logger = logger ?? new NullLogger();\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t * @private\n\t */\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Priority ordering for warning patterns (lower = higher priority).\n\t * Ensures the most actionable patterns fill the hint cap first.\n\t */\n\tprivate static readonly _HINT_PRIORITY: Readonly<Record<string, number>> = {\n\t\tconfidence_drift: 1, // Most actionable — degrading confidence\n\t\tunverified_hypothesis: 2, // Important for quality\n\t\tno_alternatives_explored: 3, // Breadth gap\n\t\tconsecutive_without_verification: 4, // Routine pattern\n\t};\n\n\t/**\n\t * Generate actionable hints from pattern signals.\n\t * Rules: max 3 hints, warning-severity only, cooldown of 3 thoughts per pattern per session.\n\t *\n\t * Warning patterns are sorted by priority before selection (see _HINT_PRIORITY).\n\t * Higher-priority patterns (lower number) fill the hint cap first.\n\t *\n\t * @param patterns - Detected pattern signals\n\t * @param currentThoughtNumber - The current thought number being processed\n\t * @param sessionId - Session identifier for cooldown scoping\n\t * @returns Array of hint strings (max 3), empty if no warnings\n\t */\n\tprivate _generateHints(\n\t\tpatterns: PatternSignal[],\n\t\tcurrentThoughtNumber: number,\n\t\tsessionId?: string\n\t): string[] {\n\t\tconst warnings = patterns.filter((p) => p.severity === 'warning');\n\t\tif (warnings.length === 0) return [];\n\n\t\t// Sort by priority (lower number = higher priority)\n\t\twarnings.sort((a, b) => {\n\t\t\tconst pa = ThoughtProcessor._HINT_PRIORITY[a.pattern] ?? 99;\n\t\t\tconst pb = ThoughtProcessor._HINT_PRIORITY[b.pattern] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\t\tconst sessionKey = sessionId ?? '__global__';\n\t\tif (!this._hintCooldowns.has(sessionKey)) {\n\t\t\tthis._hintCooldowns.set(sessionKey, new Map());\n\t\t}\n\t\tconst cooldowns = this._hintCooldowns.get(sessionKey)!;\n\n\t\tconst hints: string[] = [];\n\t\tfor (const warning of warnings) {\n\t\t\tif (hints.length >= 3) break;\n\n\t\t\tconst lastFired = cooldowns.get(warning.pattern);\n\t\t\tif (lastFired !== undefined && currentThoughtNumber - lastFired < 3) {\n\t\t\t\tcontinue; // Still in cooldown\n\t\t\t}\n\n\t\t\thints.push(warning.message);\n\t\t\tcooldowns.set(warning.pattern, currentThoughtNumber);\n\t\t}\n\n\t\treturn hints;\n\t}\n\n\t/**\n\t * Processes a thought through the sequential thinking pipeline.\n\t *\n\t * This method validates the input, adds it to history, formats the output,\n\t * computes quality signals via the ThoughtEvaluator, and returns\n\t * a structured response with metadata about the current state.\n\t *\n\t * @param input - The thought data to process\n\t * @returns A Promise resolving to the formatted tool result containing:\n\t * - `thought_number` — Current thought index\n\t * - `total_thoughts` — Estimated total thoughts\n\t * - `next_thought_needed` — Whether to continue\n\t * - `branches` — Active branch IDs\n\t * - `thought_history_length` — Number of thoughts in history\n\t * - `available_mcp_tools` — MCP tools available for recommendation\n\t * - `available_skills` — Skills available for recommendation\n\t * - `current_step` — Current step recommendation\n\t * - `previous_steps` — Previously recommended steps\n\t * - `remaining_steps` — Upcoming step descriptions\n\t * - `thought_type` — Classification of thought purpose (optional)\n\t * - `quality_score` — Self-assessed quality score 0-1 (optional)\n\t * - `confidence` — Self-assessed confidence 0-1 (optional)\n\t * - `hypothesis_id` — Hypothesis link for verification chains (optional)\n * - `confidence_signals` — Computed reasoning quality signals (includes structural_quality and quality_components)\n * - `reasoning_stats` — Aggregated reasoning analytics\n * - `reasoning_hints` — (Conditional) Actionable hints from pattern analysis, max 3, warning-severity only (optional)\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await processor.process({\n\t * thought: 'I should read the README file',\n\t * thought_number: 1,\n\t * total_thoughts: 3,\n\t * next_thought_needed: true\n\t * });\n\t *\n\t * console.log(result.content[0].text);\n\t * // Output includes: thought_number, total_thoughts, next_thought_needed,\n\t * // branches, thought_history_length, and any recommendations\n\t * ```\n\t */\n\tpublic async process(input: ThoughtData): Promise<CallToolResult> {\n\t\ttry {\n\t\t\t// Normalize input to handle common LLM field name mistakes\n\t\t\tconst normalizedInput = normalizeInput(input);\n\t\t\tconst sessionId = normalizedInput.session_id;\n\n\t\t\t// Handle reset_state: clear session before processing\n\t\t\tif (normalizedInput.reset_state) {\n\t\t\t\tthis.historyManager.clear(sessionId);\n\t\t\t\tthis.log('State reset for session', { sessionId: sessionId ?? '__global__' });\n\t\t\t}\n\n\t\t\t// Persist available_mcp_tools/available_skills across calls within a session.\n\t\t\t// If the caller omits these, reuse the last-seen values from the session.\n\t\t\tif (!normalizedInput.available_mcp_tools) {\n\t\t\t\tnormalizedInput.available_mcp_tools = this.historyManager.getAvailableMcpTools(sessionId);\n\t\t\t}\n\t\t\tif (!normalizedInput.available_skills) {\n\t\t\t\tnormalizedInput.available_skills = this.historyManager.getAvailableSkills(sessionId);\n\t\t\t}\n\n\t\t\tconst { result: validatedInput, warnings: validateWarnings } =\n\t\t\t\tthis.validateInput(normalizedInput);\n\t\t\tconst { result: checkedInput, warnings: refWarnings } =\n\t\t\t\tthis._validateCrossReferences(validatedInput, sessionId);\n\t\t\tconst allWarnings = [...validateWarnings, ...refWarnings];\n\n\t\t\t// Validate new thought types and tool-interleave invariants.\n\t\t\tthis._validateNewTypes(checkedInput, sessionId);\n\n\t\t\t// Tool-interleave suspend path: persist the tool_call thought, then return\n\t\t\t// a `suspended` envelope without running strategy/evaluator.\n\t\t\tif (checkedInput.thought_type === 'tool_call' && this._suspensionStore) {\n\t\t\t\treturn this._handleToolCall(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\t// Tool-interleave resume path: consume the suspension and continue the\n\t\t\t// normal pipeline (addThought → format → evaluate → strategy).\n\t\t\tif (checkedInput.thought_type === 'tool_observation' && this._suspensionStore) {\n\t\t\t\tthis._handleToolObservation(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\tthis.historyManager.addThought(checkedInput);\n\n\t\t\tconst formattedThought = this.thoughtFormatter.formatThought(checkedInput);\n\t\t\tthis.log(formattedThought, { sessionId: sessionId ?? '__global__' });\n\n\t\t\t// Compute quality signals — fetch history/branches once\n\t\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\t\tconst branches = this.historyManager.getBranches(sessionId);\n\n\t\t\tconst confidenceSignals = this._thoughtEvaluator.computeConfidenceSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningStats = this._thoughtEvaluator.computeReasoningStats(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\n\t\t\t// Detect reasoning patterns and generate hints\n\t\t\tconst patternSignals = this._thoughtEvaluator.computePatternSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningHints = this._generateHints(\n\t\t\t\tpatternSignals,\n\t\t\t\tcheckedInput.thought_number,\n\t\t\t\tsessionId\n\t\t\t);\n\n\t\t\t// Strategy decision — pluggable reasoning policy hook.\n\t\t\t// Built after history/stats so strategies see the latest state.\n\t\t\tconst decision = this._runStrategy(checkedInput, history, reasoningStats, sessionId);\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthought_number: checkedInput.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: checkedInput.total_thoughts,\n\t\t\t\t\t\t\tnext_thought_needed: checkedInput.next_thought_needed ?? true,\n\t\t\t\t\t\t\tbranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t\t\t\tthought_history_length: this.historyManager.getHistoryLength(sessionId),\n\t\t\t\t\t\t\tavailable_mcp_tools: checkedInput.available_mcp_tools,\n\t\t\t\t\t\t\tavailable_skills: checkedInput.available_skills,\n\t\t\t\t\t\t\tcurrent_step: checkedInput.current_step,\n\t\t\t\t\t\t\tprevious_steps: checkedInput.previous_steps,\n\t\t\t\t\t\t\tremaining_steps: checkedInput.remaining_steps,\n\t\t\t\t\t\t\t// Reasoning enrichment fields\n\t\t\t\t\t\t\tthought_type: checkedInput.thought_type,\n\t\t\t\t\t\t\tquality_score: checkedInput.quality_score,\n\t\t\t\t\t\t\tconfidence: checkedInput.confidence,\n\t\t\t\t\t\t\thypothesis_id: checkedInput.hypothesis_id,\n\t\t\t\t\t\t\tconfidence_signals: confidenceSignals,\n\t\t\t\t\t\t\treasoning_stats: reasoningStats,\n\t\t\t\t\t\t\t...(reasoningHints.length > 0 && { reasoning_hints: reasoningHints }),\n\t\t\t\t\t\t\t...(decision.action !== 'continue' && { strategy_hint: decision }),\n\t\t\t\t\t\t\t...(allWarnings.length > 0 && { warnings: allWarnings.slice(0, 3) }),\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Run the configured reasoning strategy and return its decision.\n\t * Strategy errors degrade to `{ action: 'continue' }`.\n\t * @private\n\t */\n\tprivate _runStrategy(\n\t\tcurrentThought: ThoughtData,\n\t\thistory: ThoughtData[],\n\t\tstats: ReturnType<ThoughtEvaluator['computeReasoningStats']>,\n\t\tsessionId?: string\n\t): StrategyDecision {\n\t\tlet decision: StrategyDecision;\n\t\ttry {\n\t\t\tconst edgeStore = this._getEdgeStore();\n\t\t\tconst graph = edgeStore ? new GraphView(edgeStore) : undefined;\n\t\t\tdecision = this.strategy.decide({\n\t\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\t\thistory,\n\t\t\t\tgraph,\n\t\t\t\tstats,\n\t\t\t\tcurrentThought,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._logger.warn('Reasoning strategy threw — defaulting to continue', {\n\t\t\t\tstrategy: this.strategy.name,\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t\tdecision = { action: 'continue' };\n\t\t}\n\n\t\t// Auto-compression trigger: when strategy terminates a branch and\n\t\t// compression is enabled, summarize the branch subtree. Compression\n\t\t// failures must NEVER break the thought pipeline.\n\t\tif (\n\t\t\tdecision.action === 'terminate' &&\n\t\t\tthis._compressionService &&\n\t\t\tcurrentThought.branch_id\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst sid = sessionId ?? '__global__';\n\t\t\t\tconst branchRoot = this._findBranchRoot(sid, currentThought.branch_id);\n\t\t\t\tif (branchRoot) {\n\t\t\t\t\tthis._compressionService.compressBranch(sid, currentThought.branch_id, branchRoot);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis._logger.debug('Compression auto-trigger failed', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn decision;\n\t}\n\n\t/**\n\t * Locate the root thought id for a branch.\n\t * Prefers GraphView.branchThoughts() when an EdgeStore is available;\n\t * falls back to historyManager.getBranches()[branchId][0].id.\n\t * @private\n\t */\n\tprivate _findBranchRoot(sessionId: string, branchId: string): string | undefined {\n\t\tconst edgeStore = this._getEdgeStore();\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tconst branchList = branches[branchId];\n\t\tconst firstId = branchList?.[0]?.id;\n\t\tif (edgeStore && firstId) {\n\t\t\tconst graph = new GraphView(edgeStore);\n\t\t\tconst ids = graph.branchThoughts(sessionId, firstId);\n\t\t\tif (ids.length > 0) return ids[0];\n\t\t}\n\t\treturn firstId;\n\t}\n\n\t/** Access the EdgeStore via IHistoryManager. @private */\n\tprivate _getEdgeStore(): IEdgeStore | undefined {\n\t\treturn this.historyManager.getEdgeStore();\n\t}\n\n\t/**\n\t * Validates and normalizes thought input.\n\t *\n\t * Ensures that thought numbers are consistent and within valid ranges.\n\t * If `thought_number` exceeds `total_thoughts`, `total_thoughts` is\n\t * automatically adjusted to match and a warning is emitted.\n\t *\n\t * @param input - The input to validate\n\t * @returns Object with validated input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // Auto-adjusts total_thoughts when thought_number exceeds it\n\t * const { result, warnings } = this.validateInput(input);\n\t * // result.total_thoughts === 10 (auto-adjusted from 5)\n\t * // warnings === ['Auto-adjusted total_thoughts from 5 to 10 to match thought_number']\n\t * ```\n\t */\n\tprivate validateInput(input: ThoughtData): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tif (input.thought_number > input.total_thoughts) {\n\t\t\tconst originalTotal = input.total_thoughts;\n\t\t\twarnings.push(\n\t\t\t\t`Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`\n\t\t\t);\n\t\t\tthis._logger.warn('Auto-adjusted total_thoughts to match thought_number', {\n\t\t\t\tthought_number: input.thought_number,\n\t\t\t\toriginal_total_thoughts: originalTotal,\n\t\t\t\tadjusted_total_thoughts: input.thought_number,\n\t\t\t});\n\t\t\tinput.total_thoughts = input.thought_number;\n\t\t}\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validates cross-field references against actual thought history.\n\t * Drops invalid references with a warning log — never rejects.\n\t * LLMs frequently send optimistic references to thoughts that don't exist yet.\n\t *\n\t * @param input - The thought data to validate\n\t * @returns Object with cleaned input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // verification_target=999 with only 3 thoughts in history\n\t * const { result, warnings } = this._validateCrossReferences(input);\n\t * // result.verification_target === undefined\n\t * // warnings === ['Dropped dangling verification_target: 999 (history has 3 thoughts)']\n\t * ```\n\t */\n\tprivate _validateCrossReferences(input: ThoughtData, sessionId?: string): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tconst historyLength = this.historyManager.getHistoryLength(sessionId);\n\n\t\t// verification_target: must reference existing thought\n\t\tif (input.verification_target !== undefined && input.verification_target > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling verification_target: ${input.verification_target} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling verification_target', {\n\t\t\t\tverification_target: input.verification_target,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.verification_target = undefined;\n\t\t}\n\n\t\t// revises_thought: must reference existing thought\n\t\tif (input.revises_thought !== undefined && input.revises_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling revises_thought: ${input.revises_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling revises_thought', {\n\t\t\t\trevises_thought: input.revises_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.revises_thought = undefined;\n\t\t}\n\n\t\t// branch_from_thought: must reference existing thought\n\t\tif (input.branch_from_thought !== undefined && input.branch_from_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling branch_from_thought: ${input.branch_from_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling branch_from_thought', {\n\t\t\t\tbranch_from_thought: input.branch_from_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.branch_from_thought = undefined;\n\t\t}\n\n\t\t// synthesis_sources: filter to existing thoughts only\n\t\tif (input.synthesis_sources?.length) {\n\t\t\tconst valid = input.synthesis_sources.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.synthesis_sources.length) {\n\t\t\t\tconst dropped = input.synthesis_sources.filter((n: number) => n > historyLength);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling synthesis_sources: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling synthesis_sources', {\n\t\t\t\t\toriginal: input.synthesis_sources,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.synthesis_sources = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_from_thoughts: filter to existing thoughts only\n\t\tif (input.merge_from_thoughts?.length) {\n\t\t\tconst valid = input.merge_from_thoughts.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.merge_from_thoughts.length) {\n\t\t\t\tconst dropped = input.merge_from_thoughts.filter(\n\t\t\t\t\t(n: number) => n > historyLength\n\t\t\t\t);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling merge_from_thoughts: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_from_thoughts', {\n\t\t\t\t\toriginal: input.merge_from_thoughts,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_from_thoughts = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_branch_ids: filter to existing branches only (includes pre-registered)\n\t\tif (input.merge_branch_ids?.length) {\n\t\t\tconst valid = input.merge_branch_ids.filter((id: string) =>\n\t\t\t\tthis.historyManager.branchExists(sessionId, id)\n\t\t\t);\n\t\t\tif (valid.length < input.merge_branch_ids.length) {\n\t\t\t\tconst dropped = input.merge_branch_ids.filter(\n\t\t\t\t\t(id: string) => !this.historyManager.branchExists(sessionId, id)\n\t\t\t\t);\n\t\t\t\twarnings.push(`Filtered dangling merge_branch_ids: [${dropped.join(', ')}]`);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_branch_ids', {\n\t\t\t\t\toriginal: input.merge_branch_ids,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\texistingBranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_branch_ids = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validate new thought-type invariants behind feature flags.\n\t * @private\n\t */\n\tprivate _validateNewTypes(input: ThoughtData, sessionId?: string): void {\n\t\tconst t = input.thought_type;\n\t\tif ((t === 'tool_call' || t === 'tool_observation') && !this._features.toolInterleave) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the toolInterleave feature flag. Set TRACELATTICE_FEATURES_TOOL_INTERLEAVE=true to enable it.`\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\t(t === 'assumption' || t === 'decomposition' || t === 'backtrack') &&\n\t\t\t!this._features.newThoughtTypes\n\t\t) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the newThoughtTypes feature flag. Set TRACELATTICE_FEATURES_NEW_THOUGHT_TYPES=true to enable it, or use '${ThoughtProcessor._getWorkaroundType(t)}' type as a workaround.`\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_call' && !input.tool_name) {\n\t\t\tthrow new InvalidToolCallError(\n\t\t\t\t'tool_call thought ' + input.thought_number + ' missing required tool_name'\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_observation' && !input.continuation_token) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'continuation_token',\n\t\t\t\t'tool_observation thought ' + input.thought_number + ' missing continuation_token'\n\t\t\t);\n\t\t}\n\t\tif (t === 'backtrack') {\n\t\t\tif (input.backtrack_target === undefined) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t'backtrack_target',\n\t\t\t\t\t'backtrack thought ' + input.thought_number + ' requires backtrack_target'\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (input.backtrack_target > input.thought_number) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' must be <= thought_number ' + input.thought_number\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this._thoughtNumberExists(input.backtrack_target, sessionId)) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' does not exist in session history'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether a given thought_number exists in the session history or any branch.\n\t * @private\n\t */\n\tprivate _thoughtNumberExists(thoughtNumber: number, sessionId?: string): boolean {\n\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\tfor (const t of history) {\n\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t}\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tfor (const branchThoughts of Object.values(branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a workaround thought type for a feature-flagged type.\n\t * @private\n\t */\n\tprivate static _getWorkaroundType(t: 'assumption' | 'decomposition' | 'backtrack'): string {\n\t\tswitch (t) {\n\t\t\tcase 'assumption':\n\t\t\t\treturn 'regular';\n\t\t\tcase 'decomposition':\n\t\t\t\treturn 'hypothesis';\n\t\t\tcase 'backtrack':\n\t\t\t\treturn 'regular';\n\t\t\tdefault: {\n\t\t\t\tconst _exhaust: never = t;\n\t\t\t\tthrow new Error(`Unhandled type: ${_exhaust as string}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Persist a tool_call thought and return a `suspended` envelope.\n\t * Strategy/evaluator are intentionally skipped.\n\t * @private\n\t */\n\tprivate _handleToolCall(input: ThoughtData, sessionId?: string): CallToolResult {\n\t\tthis.historyManager.addThought(input);\n\t\tconst record: SuspensionRecord = this._suspensionStore!.suspend({\n\t\t\tsessionId: sessionId ? asSessionId(sessionId) : GLOBAL_SESSION_ID,\n\t\t\ttoolCallThoughtNumber: input.thought_number,\n\t\t\ttoolName: input.tool_name!,\n\t\t\ttoolArguments: input.tool_arguments ?? {},\n\t\t\tttlMs: 5 * 60_000,\n\t\t\texpiresAt: 0,\n\t\t});\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'suspended',\n\t\t\t\t\t\t\tcontinuation_token: record.token,\n\t\t\t\t\t\t\ttool_name: record.toolName,\n\t\t\t\t\t\t\ttool_arguments: record.toolArguments,\n\t\t\t\t\t\t\texpires_at: record.expiresAt,\n\t\t\t\t\t\t\tthought_number: input.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: input.total_thoughts,\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Resume from a tool_observation, consuming the suspension record.\n\t * Distinguishes missing vs expired via peek().\n\t * @private\n\t */\n\tprivate _handleToolObservation(input: ThoughtData, _sessionId?: string): void {\n\t\tconst token = input.continuation_token!;\n\t\tconst peeked = this._suspensionStore!.peek(token);\n\t\tif (peeked && peeked.expiresAt <= Date.now()) {\n\t\t\tthrow new SuspensionExpiredError('Suspension token expired: ' + token);\n\t\t}\n\t\tconst record = this._suspensionStore!.resume(token);\n\t\tif (!record) {\n\t\t\tthrow new SuspensionNotFoundError('Suspension token not found: ' + token);\n\t\t}\n\t\t(input as ResumableThought)._resumedFrom = record.toolCallThoughtNumber;\n\t}\n}\n"],"names":["DEFAULT_FEATURES","ThoughtProcessor","Map","historyManager","thoughtFormatter","thoughtEvaluator","logger","strategy","SequentialStrategy","_compressionService","_suspensionStore","_features","NullLogger","message","meta","patterns","currentThoughtNumber","sessionId","warnings","p","a","b","pa","pb","sessionKey","cooldowns","hints","warning","lastFired","undefined","input","normalizedInput","normalizeInput","validatedInput","validateWarnings","checkedInput","refWarnings","allWarnings","formattedThought","history","branches","confidenceSignals","reasoningStats","patternSignals","reasoningHints","decision","JSON","error","getErrorMessage","currentThought","stats","edgeStore","graph","GraphView","sid","branchRoot","err","branchId","branchList","firstId","ids","originalTotal","historyLength","valid","n","dropped","id","t","ValidationError","InvalidToolCallError","InvalidBacktrackError","thoughtNumber","branchThoughts","Object","_exhaust","Error","record","asSessionId","GLOBAL_SESSION_ID","_sessionId","token","peeked","Date","SuspensionExpiredError","SuspensionNotFoundError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/ThoughtProcessor.js","sources":["../../src/core/ThoughtProcessor.ts"],"sourcesContent":["/**\n * Core thought processing logic and validation.\n *\n * This module provides the `ThoughtProcessor` class which handles the main\n * sequential thinking request processing pipeline, including input validation,\n * history management, and response formatting.\n *\n * @module processor\n */\n\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { asSessionId, GLOBAL_SESSION_ID } from '../contracts/ids.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISuspensionStore, SuspensionRecord } from '../contracts/suspension.js';\nimport type { IReasoningStrategy, StrategyDecision } from '../contracts/strategy.js';\nimport { DEFAULT_FLAGS, type FeatureFlags } from '../contracts/features.js';\nimport type { ISessionLock, IToolRegistry } from '../contracts/interfaces.js';\nimport {\n\tInvalidBacktrackError,\n\tInvalidToolCallError,\n\tSequentialThinkingError,\n\tSuspensionExpiredError,\n\tSuspensionNotFoundError,\n\tUnknownToolError,\n\tValidationError,\n} from '../errors.js';\nimport { getErrorMessage, WARNING_CODES } from '../errors.js';\nimport { enforceJsonShape, JsonShapeError } from '../sanitize.js';\nimport { GraphView } from './graph/GraphView.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport { normalizeInput } from './InputNormalizer.js';\nimport type { ThoughtData } from './thought.js';\nimport type { ThoughtEvaluator } from './ThoughtEvaluator.js';\nimport { ThoughtFormatter } from './ThoughtFormatter.js';\nimport type { PatternSignal } from './reasoning.js';\nimport { SequentialStrategy } from './reasoning/strategies/SequentialStrategy.js';\nimport type { CompressionService } from './compression/CompressionService.js';\n\n/**\n * Internal extension to ThoughtData carrying resume metadata.\n * Attached by `_handleToolObservation` so downstream consumers (e.g. edge\n * emission) can correlate the observation with the suspended `tool_call`.\n */\ntype ResumableThought = ThoughtData & { _resumedFrom?: number };\n\n\n/**\n * The return type expected by MCP tool invocations.\n *\n * This structure matches the MCP protocol for tool results,\n * supporting both success and error responses.\n *\n * @example\n * ```typescript\n * const successResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"status\":\"success\"}' }]\n * };\n *\n * const errorResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"error\":\"Something went wrong\"}' }],\n * isError: true\n * };\n * ```\n */\nexport interface CallToolResult {\n\t/** Array of content blocks (typically text) to return to the client. */\n\tcontent: Array<{\n\t\ttype: 'text';\n\t\ttext: string;\n\t}>;\n\n\t/** Whether this result represents an error condition. */\n\tisError?: boolean;\n}\n\n/**\n * Core processor for sequential thinking requests.\n *\n * Pipeline: validate → normalize → add to history → format → evaluate signals\n * → run reasoning strategy → return structured response.\n *\n * Auto-adjusts `total_thoughts` if `thought_number` exceeds it. All errors are\n * caught and returned as formatted error responses with `isError: true`.\n *\n * @example\n * ```typescript\n * const processor = new ThoughtProcessor(historyManager, formatter, new ThoughtEvaluator());\n * const result = await processor.process({ thought: '...', thought_number: 1, total_thoughts: 5, next_thought_needed: true });\n * ```\n */\nexport class ThoughtProcessor {\n\t/** Logger for debugging and monitoring. */\n\tprivate _logger: Logger;\n\n\t/** Evaluator for quality signal computation. */\n\tprivate readonly _thoughtEvaluator: ThoughtEvaluator;\n\n\t/**\n\t * Per-session cooldown tracker: session_id → pattern → last_fired_thought_number.\n\t * Prevents re-firing the same pattern hint within 3 thoughts.\n\t */\n\tprivate _hintCooldowns = new Map<string, Map<string, number>>();\n\n\t/**\n\t * Creates a new ThoughtProcessor instance.\n\t *\n\t * @param historyManager - History manager for storing thoughts\n\t * @param thoughtFormatter - Formatter for output formatting\n\t * @param thoughtEvaluator - Evaluator for quality signal computation\n\t * @param logger - Optional logger for diagnostics (defaults to NullLogger)\n\t * @param strategy - Reasoning strategy controlling next-action decisions (defaults to SequentialStrategy)\n\t * @param compressionService - Optional compression service for auto-compression on terminate\n\t * @param suspensionStore - Optional suspension store enabling tool interleave\n\t * @param toolRegistry - Optional tool registry for tool_name allowlist validation (required when toolInterleave is enabled)\n\t * @param features - Optional feature flags (defaults to DEFAULT_FLAGS — all opt-in flags off)\n\t * @param sessionLock - Optional per-session async lock; when provided, `process()` runs under it\n\t */\n\tconstructor(\n\t\tprivate historyManager: IHistoryManager,\n\t\tprivate thoughtFormatter: ThoughtFormatter,\n\t\tthoughtEvaluator: ThoughtEvaluator,\n\t\tlogger?: Logger,\n\t\tprivate readonly strategy: IReasoningStrategy = new SequentialStrategy(),\n\t\tprivate readonly _compressionService?: CompressionService,\n\t\tprivate readonly _suspensionStore?: ISuspensionStore,\n\t\tprivate readonly _toolRegistry?: IToolRegistry,\n\t\tprivate readonly _features: FeatureFlags = DEFAULT_FLAGS,\n\t\tprivate readonly _sessionLock?: ISessionLock,\n\t) {\n\t\tthis._thoughtEvaluator = thoughtEvaluator;\n\t\tthis._logger = logger ?? new NullLogger();\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t * @private\n\t */\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Priority ordering for warning patterns (lower = higher priority).\n\t * Ensures the most actionable patterns fill the hint cap first.\n\t */\n\tprivate static readonly _HINT_PRIORITY: Readonly<Record<string, number>> = {\n\t\tconfidence_drift: 1, // Most actionable — degrading confidence\n\t\tunverified_hypothesis: 2, // Important for quality\n\t\tno_alternatives_explored: 3, // Breadth gap\n\t\tconsecutive_without_verification: 4, // Routine pattern\n\t};\n\n\t/**\n\t * Generate actionable hints from pattern signals.\n\t * Rules: max 3 hints, warning-severity only, cooldown of 3 thoughts per pattern per session.\n\t *\n\t * Warning patterns are sorted by priority before selection (see _HINT_PRIORITY).\n\t * Higher-priority patterns (lower number) fill the hint cap first.\n\t *\n\t * @param patterns - Detected pattern signals\n\t * @param currentThoughtNumber - The current thought number being processed\n\t * @param sessionId - Session identifier for cooldown scoping\n\t * @returns Array of hint strings (max 3), empty if no warnings\n\t */\n\tprivate _generateHints(\n\t\tpatterns: PatternSignal[],\n\t\tcurrentThoughtNumber: number,\n\t\tsessionId?: string\n\t): string[] {\n\t\tconst warnings = patterns.filter((p) => p.severity === 'warning');\n\t\tif (warnings.length === 0) return [];\n\n\t\t// Sort by priority (lower number = higher priority)\n\t\twarnings.sort((a, b) => {\n\t\t\tconst pa = ThoughtProcessor._HINT_PRIORITY[a.pattern] ?? 99;\n\t\t\tconst pb = ThoughtProcessor._HINT_PRIORITY[b.pattern] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\t\tconst sessionKey = sessionId ?? '__global__';\n\t\tif (!this._hintCooldowns.has(sessionKey)) {\n\t\t\tthis._hintCooldowns.set(sessionKey, new Map());\n\t\t}\n\t\tconst cooldowns = this._hintCooldowns.get(sessionKey)!;\n\n\t\tconst hints: string[] = [];\n\t\tfor (const warning of warnings) {\n\t\t\tif (hints.length >= 3) break;\n\n\t\t\tconst lastFired = cooldowns.get(warning.pattern);\n\t\t\tif (lastFired !== undefined && currentThoughtNumber - lastFired < 3) {\n\t\t\t\tcontinue; // Still in cooldown\n\t\t\t}\n\n\t\t\thints.push(warning.message);\n\t\t\tcooldowns.set(warning.pattern, currentThoughtNumber);\n\t\t}\n\n\t\treturn hints;\n\t}\n\n\t/**\n\t * Processes a thought through the sequential thinking pipeline.\n\t *\n\t * This method validates the input, adds it to history, formats the output,\n\t * computes quality signals via the ThoughtEvaluator, and returns\n\t * a structured response with metadata about the current state.\n\t *\n\t * @param input - The thought data to process\n\t * @returns A Promise resolving to the formatted tool result containing:\n\t * - `thought_number` — Current thought index\n\t * - `total_thoughts` — Estimated total thoughts\n\t * - `next_thought_needed` — Whether to continue\n\t * - `branches` — Active branch IDs\n\t * - `thought_history_length` — Number of thoughts in history\n\t * - `available_mcp_tools` — MCP tools available for recommendation\n\t * - `available_skills` — Skills available for recommendation\n\t * - `current_step` — Current step recommendation\n\t * - `previous_steps` — Previously recommended steps\n\t * - `remaining_steps` — Upcoming step descriptions\n\t * - `thought_type` — Classification of thought purpose (optional)\n\t * - `quality_score` — Self-assessed quality score 0-1 (optional)\n\t * - `confidence` — Self-assessed confidence 0-1 (optional)\n\t * - `hypothesis_id` — Hypothesis link for verification chains (optional)\n * - `confidence_signals` — Computed reasoning quality signals (includes structural_quality and quality_components)\n * - `reasoning_stats` — Aggregated reasoning analytics\n * - `reasoning_hints` — (Conditional) Actionable hints from pattern analysis, max 3, warning-severity only (optional)\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await processor.process({\n\t * thought: 'I should read the README file',\n\t * thought_number: 1,\n\t * total_thoughts: 3,\n\t * next_thought_needed: true\n\t * });\n\t *\n\t * console.log(result.content[0].text);\n\t * // Output includes: thought_number, total_thoughts, next_thought_needed,\n\t * // branches, thought_history_length, and any recommendations\n\t * ```\n\t */\n\tpublic async process(input: ThoughtData): Promise<CallToolResult> {\n\t\tconst lock = this._sessionLock;\n\t\tif (lock) {\n\t\t\treturn lock.withLock(input.session_id, () => this._processInner(input));\n\t\t}\n\t\treturn this._processInner(input);\n\t}\n\n\tprivate async _processInner(input: ThoughtData): Promise<CallToolResult> {\n\t\ttry {\n\t\t\t// Normalize input to handle common LLM field name mistakes\n\t\t\tconst normalizedInput = normalizeInput(input);\n\t\t\tconst sessionId = normalizedInput.session_id;\n\n\t\t\t// Handle reset_state: clear session before processing\n\t\t\tif (normalizedInput.reset_state) {\n\t\t\t\tthis.historyManager.clear(sessionId);\n\t\t\t\tthis.log('State reset for session', { sessionId: sessionId ?? '__global__' });\n\t\t\t}\n\n\t\t\t// Persist available_mcp_tools/available_skills across calls within a session.\n\t\t\t// If the caller omits these, reuse the last-seen values from the session.\n\t\t\tif (!normalizedInput.available_mcp_tools) {\n\t\t\t\tnormalizedInput.available_mcp_tools = this.historyManager.getAvailableMcpTools(sessionId);\n\t\t\t}\n\t\t\tif (!normalizedInput.available_skills) {\n\t\t\t\tnormalizedInput.available_skills = this.historyManager.getAvailableSkills(sessionId);\n\t\t\t}\n\n\t\t\tconst { result: validatedInput, warnings: validateWarnings } =\n\t\t\t\tthis.validateInput(normalizedInput);\n\t\t\tconst { result: checkedInput, warnings: refWarnings } =\n\t\t\t\tthis._validateCrossReferences(validatedInput, sessionId);\n\t\t\tconst allWarnings = [...validateWarnings, ...refWarnings];\n\n\t\t\t// Validate new thought types and tool-interleave invariants.\n\t\t\tthis._validateNewTypes(checkedInput, sessionId);\n\n\t\t\t// Tool-interleave suspend path: persist the tool_call thought, then return\n\t\t\t// a `suspended` envelope without running strategy/evaluator.\n\t\t\tif (checkedInput.thought_type === 'tool_call' && this._suspensionStore) {\n\t\t\t\treturn this._handleToolCall(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\t// Tool-interleave resume path: consume the suspension and continue the\n\t\t\t// normal pipeline (addThought → format → evaluate → strategy).\n\t\t\tif (checkedInput.thought_type === 'tool_observation' && this._suspensionStore) {\n\t\t\t\tthis._handleToolObservation(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\tthis.historyManager.addThought(checkedInput);\n\n\t\t\tconst formattedThought = this.thoughtFormatter.formatThought(checkedInput);\n\t\t\tthis.log(formattedThought, { sessionId: sessionId ?? '__global__' });\n\n\t\t\t// Compute quality signals — fetch history/branches once\n\t\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\t\tconst branches = this.historyManager.getBranches(sessionId);\n\n\t\t\tconst confidenceSignals = this._thoughtEvaluator.computeConfidenceSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningStats = this._thoughtEvaluator.computeReasoningStats(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\n\t\t\t// Detect reasoning patterns and generate hints\n\t\t\tconst patternSignals = this._thoughtEvaluator.computePatternSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningHints = this._generateHints(\n\t\t\t\tpatternSignals,\n\t\t\t\tcheckedInput.thought_number,\n\t\t\t\tsessionId\n\t\t\t);\n\n\t\t\t// Strategy decision — pluggable reasoning policy hook.\n\t\t\t// Built after history/stats so strategies see the latest state.\n\t\t\tconst decision = this._runStrategy(checkedInput, history, reasoningStats, sessionId);\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthought_number: checkedInput.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: checkedInput.total_thoughts,\n\t\t\t\t\t\t\tnext_thought_needed: checkedInput.next_thought_needed ?? true,\n\t\t\t\t\t\t\tbranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t\t\t\tthought_history_length: this.historyManager.getHistoryLength(sessionId),\n\t\t\t\t\t\t\tavailable_mcp_tools: checkedInput.available_mcp_tools,\n\t\t\t\t\t\t\tavailable_skills: checkedInput.available_skills,\n\t\t\t\t\t\t\tcurrent_step: checkedInput.current_step,\n\t\t\t\t\t\t\tprevious_steps: checkedInput.previous_steps,\n\t\t\t\t\t\t\tremaining_steps: checkedInput.remaining_steps,\n\t\t\t\t\t\t\t// Reasoning enrichment fields\n\t\t\t\t\t\t\tthought_type: checkedInput.thought_type,\n\t\t\t\t\t\t\tquality_score: checkedInput.quality_score,\n\t\t\t\t\t\t\tconfidence: checkedInput.confidence,\n\t\t\t\t\t\t\thypothesis_id: checkedInput.hypothesis_id,\n\t\t\t\t\t\t\tconfidence_signals: confidenceSignals,\n\t\t\t\t\t\t\treasoning_stats: reasoningStats,\n\t\t\t\t\t\t\t...(reasoningHints.length > 0 && { reasoning_hints: reasoningHints }),\n\t\t\t\t\t\t\t...(decision.action !== 'continue' && { strategy_hint: decision }),\n\t\t\t\t\t\t\t...(allWarnings.length > 0 && { warnings: allWarnings.slice(0, 3) }),\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...(error instanceof SequentialThinkingError && { code: error.code }),\n\t\t\t\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t\t\t\t\tmessage: getErrorMessage(error),\n\t\t\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Run the configured reasoning strategy and return its decision.\n\t * Strategy errors degrade to `{ action: 'continue' }`.\n\t * @private\n\t */\n\tprivate _runStrategy(\n\t\tcurrentThought: ThoughtData,\n\t\thistory: ThoughtData[],\n\t\tstats: ReturnType<ThoughtEvaluator['computeReasoningStats']>,\n\t\tsessionId?: string\n\t): StrategyDecision {\n\t\tlet decision: StrategyDecision;\n\t\ttry {\n\t\t\tconst edgeStore = this._getEdgeStore();\n\t\t\tconst graph = edgeStore ? new GraphView(edgeStore) : undefined;\n\t\t\tdecision = this.strategy.decide({\n\t\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\t\thistory,\n\t\t\t\tgraph,\n\t\t\t\tstats,\n\t\t\t\tcurrentThought,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._logger.warn('Reasoning strategy threw — defaulting to continue', {\n\t\t\t\tstrategy: this.strategy.name,\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t\tdecision = { action: 'continue' };\n\t\t}\n\n\t\t// Auto-compression trigger: when strategy terminates a branch and\n\t\t// compression is enabled, summarize the branch subtree. Compression\n\t\t// failures must NEVER break the thought pipeline.\n\t\tif (\n\t\t\tdecision.action === 'terminate' &&\n\t\t\tthis._compressionService &&\n\t\t\tcurrentThought.branch_id\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst sid = sessionId ?? '__global__';\n\t\t\t\tconst branchRoot = this._findBranchRoot(sid, currentThought.branch_id);\n\t\t\t\tif (branchRoot) {\n\t\t\t\t\tthis._compressionService.compressBranch(sid, currentThought.branch_id, branchRoot);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis._logger.debug('Compression auto-trigger failed', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn decision;\n\t}\n\n\t/**\n\t * Locate the root thought id for a branch.\n\t * Prefers GraphView.branchThoughts() when an EdgeStore is available;\n\t * falls back to historyManager.getBranches()[branchId][0].id.\n\t * @private\n\t */\n\tprivate _findBranchRoot(sessionId: string, branchId: string): string | undefined {\n\t\tconst edgeStore = this._getEdgeStore();\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tconst branchList = branches[branchId];\n\t\tconst firstId = branchList?.[0]?.id;\n\t\tif (edgeStore && firstId) {\n\t\t\tconst graph = new GraphView(edgeStore);\n\t\t\tconst ids = graph.branchThoughts(sessionId, firstId);\n\t\t\tif (ids.length > 0) return ids[0];\n\t\t}\n\t\treturn firstId;\n\t}\n\n\t/** Access the EdgeStore via IHistoryManager. @private */\n\tprivate _getEdgeStore(): IEdgeStore | undefined {\n\t\treturn this.historyManager.getEdgeStore();\n\t}\n\n\t/**\n\t * Validates and normalizes thought input.\n\t *\n\t * Ensures that thought numbers are consistent and within valid ranges.\n\t * If `thought_number` exceeds `total_thoughts`, `total_thoughts` is\n\t * automatically adjusted to match and a warning is emitted.\n\t *\n\t * @param input - The input to validate\n\t * @returns Object with validated input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // Auto-adjusts total_thoughts when thought_number exceeds it\n\t * const { result, warnings } = this.validateInput(input);\n\t * // result.total_thoughts === 10 (auto-adjusted from 5)\n\t * // warnings === ['Auto-adjusted total_thoughts from 5 to 10 to match thought_number']\n\t * ```\n\t */\n\tprivate validateInput(input: ThoughtData): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tif (input.thought_number > input.total_thoughts) {\n\t\t\tconst originalTotal = input.total_thoughts;\n\t\t\twarnings.push(\n\t\t\t\t`[${WARNING_CODES.TOTAL_THOUGHTS_ADJUSTED}] Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`\n\t\t\t);\n\t\t\tthis._logger.warn('Auto-adjusted total_thoughts to match thought_number', {\n\t\t\t\tthought_number: input.thought_number,\n\t\t\t\toriginal_total_thoughts: originalTotal,\n\t\t\t\tadjusted_total_thoughts: input.thought_number,\n\t\t\t});\n\t\t\tinput.total_thoughts = input.thought_number;\n\t\t}\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validates cross-field references against actual thought history.\n\t * Drops invalid references with a warning log — never rejects.\n\t * LLMs frequently send optimistic references to thoughts that don't exist yet.\n\t *\n\t * @param input - The thought data to validate\n\t * @returns Object with cleaned input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // verification_target=999 with only 3 thoughts in history\n\t * const { result, warnings } = this._validateCrossReferences(input);\n\t * // result.verification_target === undefined\n\t * // warnings === ['Dropped dangling verification_target: 999 (history has 3 thoughts)']\n\t * ```\n\t */\n\tprivate _validateCrossReferences(input: ThoughtData, sessionId?: string): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tconst historyLength = this.historyManager.getHistoryLength(sessionId);\n\n\t\t// verification_target: must reference existing thought\n\t\tif (input.verification_target !== undefined && input.verification_target > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling verification_target: ${input.verification_target} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling verification_target', {\n\t\t\t\tverification_target: input.verification_target,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.verification_target = undefined;\n\t\t}\n\n\t\t// revises_thought: must reference existing thought\n\t\tif (input.revises_thought !== undefined && input.revises_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling revises_thought: ${input.revises_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling revises_thought', {\n\t\t\t\trevises_thought: input.revises_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.revises_thought = undefined;\n\t\t}\n\n\t\t// branch_from_thought: must reference existing thought\n\t\tif (input.branch_from_thought !== undefined && input.branch_from_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling branch_from_thought: ${input.branch_from_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling branch_from_thought', {\n\t\t\t\tbranch_from_thought: input.branch_from_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.branch_from_thought = undefined;\n\t\t}\n\n\t\t// synthesis_sources: filter to existing thoughts only\n\t\tif (input.synthesis_sources?.length) {\n\t\t\tconst valid = input.synthesis_sources.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.synthesis_sources.length) {\n\t\t\t\tconst dropped = input.synthesis_sources.filter((n: number) => n > historyLength);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling synthesis_sources: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling synthesis_sources', {\n\t\t\t\t\toriginal: input.synthesis_sources,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.synthesis_sources = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_from_thoughts: filter to existing thoughts only\n\t\tif (input.merge_from_thoughts?.length) {\n\t\t\tconst valid = input.merge_from_thoughts.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.merge_from_thoughts.length) {\n\t\t\t\tconst dropped = input.merge_from_thoughts.filter(\n\t\t\t\t\t(n: number) => n > historyLength\n\t\t\t\t);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling merge_from_thoughts: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_from_thoughts', {\n\t\t\t\t\toriginal: input.merge_from_thoughts,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_from_thoughts = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_branch_ids: filter to existing branches only (includes pre-registered)\n\t\tif (input.merge_branch_ids?.length) {\n\t\t\tconst valid = input.merge_branch_ids.filter((id: string) =>\n\t\t\t\tthis.historyManager.branchExists(sessionId, id)\n\t\t\t);\n\t\t\tif (valid.length < input.merge_branch_ids.length) {\n\t\t\t\tconst dropped = input.merge_branch_ids.filter(\n\t\t\t\t\t(id: string) => !this.historyManager.branchExists(sessionId, id)\n\t\t\t\t);\n\t\t\t\twarnings.push(`Filtered dangling merge_branch_ids: [${dropped.join(', ')}]`);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_branch_ids', {\n\t\t\t\t\toriginal: input.merge_branch_ids,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\texistingBranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_branch_ids = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validate new thought-type invariants behind feature flags.\n\t * @private\n\t */\n\tprivate _validateNewTypes(input: ThoughtData, sessionId?: string): void {\n\t\tconst t = input.thought_type;\n\t\tif ((t === 'tool_call' || t === 'tool_observation') && !this._features.toolInterleave) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the toolInterleave feature flag. Set TRACELATTICE_FEATURES_TOOL_INTERLEAVE=true to enable it.`\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\t(t === 'assumption' || t === 'decomposition' || t === 'backtrack') &&\n\t\t\t!this._features.newThoughtTypes\n\t\t) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the newThoughtTypes feature flag. Set TRACELATTICE_FEATURES_NEW_THOUGHT_TYPES=true to enable it, or use '${ThoughtProcessor._getWorkaroundType(t)}' type as a workaround.`\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_call') {\n\t\t\tif (!input.tool_name) {\n\t\t\t\tthrow new InvalidToolCallError(\n\t\t\t\t\t'tool_call thought ' + input.thought_number + ' missing required tool_name'\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis._validateToolName(input.tool_name);\n\t\t}\n\t\tif (t === 'tool_observation' && !input.continuation_token) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'continuation_token',\n\t\t\t\t'tool_observation thought ' + input.thought_number + ' missing continuation_token'\n\t\t\t);\n\t\t}\n\t\tif (t === 'backtrack') {\n\t\t\tif (input.backtrack_target === undefined) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t'backtrack_target',\n\t\t\t\t\t'backtrack thought ' + input.thought_number + ' requires backtrack_target'\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (input.backtrack_target > input.thought_number) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' must be <= thought_number ' + input.thought_number\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this._thoughtNumberExists(input.backtrack_target, sessionId)) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' does not exist in session history'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate a tool_call's tool_name against the configured allowlist.\n\t *\n\t * Fails closed: if no tool registry was wired, all tool_call invocations are\n\t * rejected. This prevents arbitrary tool name injection through the protocol.\n\t *\n\t * @param toolName - The tool name from the tool_call thought\n\t * @throws {UnknownToolError} When no registry is wired or the tool is not registered\n\t * @private\n\t */\n\tprivate _validateToolName(toolName: string): void {\n\t\tif (!this._toolRegistry) {\n\t\t\tthrow new UnknownToolError(\n\t\t\t\ttoolName,\n\t\t\t\t`Tool '${toolName}' rejected: no tool registry configured. Tool interleave requires a registered allowlist.`\n\t\t\t);\n\t\t}\n\t\tif (!this._toolRegistry.has(toolName)) {\n\t\t\tthrow new UnknownToolError(toolName);\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether a given thought_number exists in the session history or any branch.\n\t * @private\n\t */\n\tprivate _thoughtNumberExists(thoughtNumber: number, sessionId?: string): boolean {\n\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\tfor (const t of history) {\n\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t}\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tfor (const branchThoughts of Object.values(branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a workaround thought type for a feature-flagged type.\n\t * @private\n\t */\n\tprivate static _getWorkaroundType(t: 'assumption' | 'decomposition' | 'backtrack'): string {\n\t\tswitch (t) {\n\t\t\tcase 'assumption':\n\t\t\t\treturn 'regular';\n\t\t\tcase 'decomposition':\n\t\t\t\treturn 'hypothesis';\n\t\t\tcase 'backtrack':\n\t\t\t\treturn 'regular';\n\t\t\tdefault: {\n\t\t\t\tconst _exhaust: never = t;\n\t\t\t\tthrow new Error(`Unhandled type: ${_exhaust as string}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Persist a tool_call thought and return a `suspended` envelope.\n\t * Strategy/evaluator are intentionally skipped.\n\t * @private\n\t */\n\tprivate _handleToolCall(input: ThoughtData, sessionId?: string): CallToolResult {\n\t\tconst args = input.tool_arguments ?? {};\n\t\ttry {\n\t\t\tenforceJsonShape(args);\n\t\t} catch (err) {\n\t\t\tif (err instanceof JsonShapeError) {\n\t\t\t\tthrow new ValidationError('tool_arguments', err.reason);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t\tthis.historyManager.addThought(input);\n\t\tconst record: SuspensionRecord = this._suspensionStore!.suspend({\n\t\t\tsessionId: sessionId ? asSessionId(sessionId) : GLOBAL_SESSION_ID,\n\t\t\ttoolCallThoughtNumber: input.thought_number,\n\t\t\ttoolName: input.tool_name!,\n\t\t\ttoolArguments: input.tool_arguments ?? {},\n\t\t\tttlMs: 5 * 60_000,\n\t\t\texpiresAt: 0,\n\t\t});\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'suspended',\n\t\t\t\t\t\t\tcontinuation_token: record.token,\n\t\t\t\t\t\t\ttool_name: record.toolName,\n\t\t\t\t\t\t\ttool_arguments: record.toolArguments,\n\t\t\t\t\t\t\texpires_at: record.expiresAt,\n\t\t\t\t\t\t\tthought_number: input.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: input.total_thoughts,\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Resume from a tool_observation, consuming the suspension record.\n\t * Distinguishes missing vs expired via peek().\n\t * @private\n\t */\n\tprivate _handleToolObservation(input: ThoughtData, _sessionId?: string): void {\n\t\tconst token = input.continuation_token!;\n\t\tconst peeked = this._suspensionStore!.peek(token);\n\t\tif (peeked && peeked.expiresAt <= Date.now()) {\n\t\t\tthrow new SuspensionExpiredError('Suspension token expired: ' + token);\n\t\t}\n\t\tconst record = this._suspensionStore!.resume(token);\n\t\tif (!record) {\n\t\t\tthrow new SuspensionNotFoundError('Suspension token not found: ' + token);\n\t\t}\n\t\t(input as ResumableThought)._resumedFrom = record.toolCallThoughtNumber;\n\t}\n}\n"],"names":["ThoughtProcessor","Map","historyManager","thoughtFormatter","thoughtEvaluator","logger","strategy","SequentialStrategy","_compressionService","_suspensionStore","_toolRegistry","_features","DEFAULT_FLAGS","_sessionLock","NullLogger","message","meta","patterns","currentThoughtNumber","sessionId","warnings","p","a","b","pa","pb","sessionKey","cooldowns","hints","warning","lastFired","undefined","input","lock","normalizedInput","normalizeInput","validatedInput","validateWarnings","checkedInput","refWarnings","allWarnings","formattedThought","history","branches","confidenceSignals","reasoningStats","patternSignals","reasoningHints","decision","JSON","error","SequentialThinkingError","getErrorMessage","currentThought","stats","edgeStore","graph","GraphView","sid","branchRoot","err","branchId","branchList","firstId","ids","originalTotal","WARNING_CODES","historyLength","valid","n","dropped","id","t","ValidationError","InvalidToolCallError","InvalidBacktrackError","toolName","UnknownToolError","thoughtNumber","branchThoughts","Object","_exhaust","Error","args","enforceJsonShape","JsonShapeError","record","asSessionId","GLOBAL_SESSION_ID","_sessionId","token","peeked","Date","SuspensionExpiredError","SuspensionNotFoundError"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import type { PersistenceBackend } from '../contracts/PersistenceBackend.js';
|
|
|
9
9
|
import type { HistoryManager } from '../core/HistoryManager.js';
|
|
10
10
|
import type { EdgeStore } from '../core/graph/EdgeStore.js';
|
|
11
11
|
import type { ICalibrator } from '../contracts/calibrator.js';
|
|
12
|
-
import type { IOutcomeRecorder } from '../contracts/interfaces.js';
|
|
12
|
+
import type { IOutcomeRecorder, ISessionLock, IToolRegistry } from '../contracts/interfaces.js';
|
|
13
13
|
import type { IReasoningStrategy } from '../contracts/strategy.js';
|
|
14
14
|
import type { ThoughtEvaluator } from '../core/ThoughtEvaluator.js';
|
|
15
15
|
import type { IThoughtFormatter } from '../core/IThoughtFormatter.js';
|
|
@@ -17,7 +17,6 @@ import type { ThoughtProcessor } from '../core/ThoughtProcessor.js';
|
|
|
17
17
|
import type { StructuredLogger } from '../logger/StructuredLogger.js';
|
|
18
18
|
import type { Metrics } from '../metrics/metrics.impl.js';
|
|
19
19
|
import type { SkillRegistry } from '../registry/SkillRegistry.js';
|
|
20
|
-
import type { ToolRegistry } from '../registry/ToolRegistry.js';
|
|
21
20
|
import type { ServerConfig } from '../ServerConfig.js';
|
|
22
21
|
import type { ISummaryStore } from '../contracts/summary.js';
|
|
23
22
|
import type { CompressionService } from '../core/compression/CompressionService.js';
|
|
@@ -31,7 +30,7 @@ export interface ServiceRegistry {
|
|
|
31
30
|
ThoughtFormatter: IThoughtFormatter;
|
|
32
31
|
ThoughtEvaluator: ThoughtEvaluator;
|
|
33
32
|
Persistence: PersistenceBackend | null;
|
|
34
|
-
ToolRegistry:
|
|
33
|
+
ToolRegistry: IToolRegistry;
|
|
35
34
|
SkillRegistry: SkillRegistry;
|
|
36
35
|
Metrics: Metrics;
|
|
37
36
|
EdgeStore: EdgeStore;
|
|
@@ -41,6 +40,7 @@ export interface ServiceRegistry {
|
|
|
41
40
|
summaryStore: ISummaryStore;
|
|
42
41
|
compressionService: CompressionService;
|
|
43
42
|
suspensionStore: ISuspensionStore;
|
|
43
|
+
sessionLock: ISessionLock;
|
|
44
44
|
}
|
|
45
45
|
export type ServiceKey = keyof ServiceRegistry;
|
|
46
46
|
//# sourceMappingURL=ServiceRegistry.d.ts.map
|