@j0hanz/thinkseq-mcp 1.2.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/app.d.ts +2 -1
- package/dist/app.js +5 -2
- package/dist/appConfig/runDependencies.js +12 -2
- package/dist/engine/revision.d.ts +4 -2
- package/dist/engine/revision.js +25 -20
- package/dist/engine/thoughtQueries.js +26 -18
- package/dist/engine/thoughtStore.d.ts +7 -0
- package/dist/engine/thoughtStore.js +53 -24
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +98 -45
- package/dist/instructions.md +22 -16
- package/dist/lib/mcpLogging.d.ts +4 -1
- package/dist/lib/mcpLogging.js +27 -9
- package/dist/lib/types.d.ts +1 -0
- package/dist/schemas/inputs.d.ts +1 -0
- package/dist/schemas/inputs.js +6 -0
- package/dist/schemas/outputs.d.ts +5 -5
- package/dist/schemas/outputs.js +5 -5
- package/dist/tools/thinkseq.js +69 -58
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -12
- package/dist/appConfig.d.ts +0 -4
- package/dist/appConfig.js +0 -2
package/README.md
CHANGED
|
@@ -133,6 +133,7 @@ Record a concise thinking step. Be brief: capture only the essential insight, ca
|
|
|
133
133
|
| Field | Type | Required | Description |
|
|
134
134
|
| :--------------- | :----- | :------: | :----------------------------------------------------------------- |
|
|
135
135
|
| `thought` | string | yes | Current thinking step (1-8000 chars). |
|
|
136
|
+
| `sessionId` | string | no | Optional session identifier to isolate thought histories. |
|
|
136
137
|
| `totalThoughts` | number | no | Estimated total thoughts (1-25, default: 3). |
|
|
137
138
|
| `revisesThought` | number | no | Revise a previous thought by number. Original preserved for audit. |
|
|
138
139
|
|
package/dist/app.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { RunDependencies } from './appConfig/runDependencies.js';
|
|
2
|
+
import type { ProcessLike } from './appConfig/types.js';
|
|
2
3
|
interface ProcessErrorHandlerDeps {
|
|
3
4
|
processLike?: ProcessLike;
|
|
4
5
|
logError?: (message: string) => void;
|
package/dist/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolvePackageIdentity, resolveRunDependencies, } from './appConfig/runDependencies.js';
|
|
2
|
+
import { buildShutdownDependencies } from './appConfig/shutdown.js';
|
|
2
3
|
import { installConsoleBridge, installMcpLogging } from './lib/mcpLogging.js';
|
|
3
4
|
const toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
4
5
|
const createExit = (proc, exit) => exit ?? ((code) => proc.exit(code));
|
|
@@ -21,7 +22,8 @@ export async function run(deps = {}) {
|
|
|
21
22
|
const { name, version } = resolvePackageIdentity(pkg);
|
|
22
23
|
const server = resolved.createServer(name, version);
|
|
23
24
|
installMcpLogging(server);
|
|
24
|
-
installConsoleBridge(server);
|
|
25
|
+
const { flush: flushConsole, restore: restoreConsole } = installConsoleBridge(server);
|
|
26
|
+
process.on('exit', restoreConsole);
|
|
25
27
|
resolved.publishLifecycleEvent({
|
|
26
28
|
type: 'lifecycle.started',
|
|
27
29
|
ts: resolved.now(),
|
|
@@ -29,5 +31,6 @@ export async function run(deps = {}) {
|
|
|
29
31
|
const engine = resolved.engineFactory();
|
|
30
32
|
resolved.registerTool(server, engine);
|
|
31
33
|
const transport = await resolved.connectServer(server);
|
|
34
|
+
flushConsole();
|
|
32
35
|
resolved.installShutdownHandlers(buildShutdownDependencies(resolved, { server, engine, transport }));
|
|
33
36
|
}
|
|
@@ -23,7 +23,17 @@ function loadServerInstructions() {
|
|
|
23
23
|
return trimmed.length > 0 ? trimmed : INSTRUCTIONS_FALLBACK;
|
|
24
24
|
}
|
|
25
25
|
function registerInstructionsResource(server) {
|
|
26
|
-
server.registerResource('instructions', new ResourceTemplate('internal://instructions', {
|
|
26
|
+
server.registerResource('instructions', new ResourceTemplate('internal://instructions', {
|
|
27
|
+
list: () => ({
|
|
28
|
+
resources: [
|
|
29
|
+
{
|
|
30
|
+
uri: 'internal://instructions',
|
|
31
|
+
name: 'Instructions',
|
|
32
|
+
mimeType: 'text/markdown',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
}), { title: 'Instructions', mimeType: 'text/markdown' }, (uri) => ({
|
|
27
37
|
contents: [
|
|
28
38
|
{
|
|
29
39
|
uri: uri.href,
|
|
@@ -40,7 +50,7 @@ function buildServerConfig() {
|
|
|
40
50
|
instructions: SERVER_INSTRUCTIONS,
|
|
41
51
|
capabilities: {
|
|
42
52
|
logging: {},
|
|
43
|
-
tools: { listChanged:
|
|
53
|
+
tools: { listChanged: false },
|
|
44
54
|
resources: { subscribe: false, listChanged: false },
|
|
45
55
|
prompts: { listChanged: false },
|
|
46
56
|
},
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ProcessResult, StoredThought, ThoughtData } from '../lib/types.js';
|
|
2
|
-
export
|
|
2
|
+
export type RevisionTargetResolution = {
|
|
3
3
|
ok: true;
|
|
4
4
|
targetNumber: number;
|
|
5
|
+
target: StoredThought;
|
|
5
6
|
} | {
|
|
6
7
|
ok: false;
|
|
7
|
-
|
|
8
|
+
result: ProcessResult;
|
|
8
9
|
};
|
|
10
|
+
export declare function resolveRevisionTarget(input: ThoughtData, getThoughtByNumber: (thoughtNumber: number) => StoredThought | undefined): RevisionTargetResolution;
|
package/dist/engine/revision.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
+
function missingRevisionError() {
|
|
2
|
+
return {
|
|
3
|
+
ok: false,
|
|
4
|
+
result: buildRevisionError('E_REVISION_MISSING', 'revisesThought is required for revision'),
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
function targetNotFoundError(targetNumber) {
|
|
8
|
+
return {
|
|
9
|
+
ok: false,
|
|
10
|
+
result: buildRevisionError('E_REVISION_TARGET_NOT_FOUND', `Thought ${targetNumber} not found`),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function targetSupersededError(targetNumber) {
|
|
14
|
+
return {
|
|
15
|
+
ok: false,
|
|
16
|
+
result: buildRevisionError('E_REVISION_TARGET_SUPERSEDED', `Thought ${targetNumber} was already superseded`),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
1
19
|
export function resolveRevisionTarget(input, getThoughtByNumber) {
|
|
2
20
|
const targetNumber = input.revisesThought;
|
|
3
|
-
if (targetNumber === undefined)
|
|
4
|
-
return
|
|
5
|
-
ok: false,
|
|
6
|
-
error: buildRevisionError('E_REVISION_MISSING', 'revisesThought is required for revision'),
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
const validationError = validateRevisionTarget(getThoughtByNumber, targetNumber);
|
|
10
|
-
if (validationError) {
|
|
11
|
-
return { ok: false, error: validationError };
|
|
12
|
-
}
|
|
13
|
-
return { ok: true, targetNumber };
|
|
14
|
-
}
|
|
15
|
-
function validateRevisionTarget(getThoughtByNumber, targetNumber) {
|
|
21
|
+
if (targetNumber === undefined)
|
|
22
|
+
return missingRevisionError();
|
|
16
23
|
const target = getThoughtByNumber(targetNumber);
|
|
17
|
-
if (!target)
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
return undefined;
|
|
24
|
+
if (!target)
|
|
25
|
+
return targetNotFoundError(targetNumber);
|
|
26
|
+
if (!target.isActive)
|
|
27
|
+
return targetSupersededError(targetNumber);
|
|
28
|
+
return { ok: true, targetNumber, target };
|
|
24
29
|
}
|
|
25
30
|
function buildRevisionError(code, message) {
|
|
26
31
|
return {
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
+
const MAX_PREVIEW_CHARS = 100;
|
|
2
|
+
const MAX_RECENT_THOUGHTS = 5;
|
|
3
|
+
const RECENT_TAIL_COUNT = 4;
|
|
1
4
|
function selectRecentThoughts(activeThoughts) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
const len = activeThoughts.length;
|
|
6
|
+
const recent = [];
|
|
7
|
+
const stepIndexes = [];
|
|
8
|
+
if (len > MAX_RECENT_THOUGHTS) {
|
|
9
|
+
const anchor = activeThoughts[0];
|
|
10
|
+
if (!anchor)
|
|
11
|
+
throw new Error('Invariant violation: anchor thought missing');
|
|
12
|
+
recent.push(anchor);
|
|
13
|
+
stepIndexes.push(1);
|
|
14
|
+
}
|
|
15
|
+
const start = len <= MAX_RECENT_THOUGHTS ? 0 : Math.max(0, len - RECENT_TAIL_COUNT);
|
|
16
|
+
for (let i = start; i < len; i += 1) {
|
|
17
|
+
const thought = activeThoughts[i];
|
|
18
|
+
if (thought) {
|
|
19
|
+
recent.push(thought);
|
|
20
|
+
stepIndexes.push(i + 1);
|
|
21
|
+
}
|
|
6
22
|
}
|
|
7
|
-
const anchor = activeThoughts[0];
|
|
8
|
-
if (!anchor)
|
|
9
|
-
throw new Error('Invariant violation: anchor thought missing');
|
|
10
|
-
const tail = activeThoughts.slice(-4);
|
|
11
|
-
const recent = [anchor, ...tail];
|
|
12
|
-
const stepIndexes = [
|
|
13
|
-
1,
|
|
14
|
-
...tail.map((_, index) => activeThoughts.length - 4 + index + 1),
|
|
15
|
-
];
|
|
16
23
|
return { recent, stepIndexes };
|
|
17
24
|
}
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
function truncatePreview(input, maxChars) {
|
|
26
|
+
const codepoints = Array.from(input);
|
|
27
|
+
if (codepoints.length <= maxChars)
|
|
28
|
+
return input;
|
|
29
|
+
return `${codepoints.slice(0, maxChars).join('')}...`;
|
|
22
30
|
}
|
|
23
31
|
export function buildContextSummary(activeThoughts, revisionInfo) {
|
|
24
32
|
const { recent, stepIndexes } = selectRecentThoughts(activeThoughts);
|
|
25
33
|
const recentThoughts = recent.map((thought, index) => ({
|
|
26
34
|
stepIndex: stepIndexes[index] ?? index + 1,
|
|
27
35
|
number: thought.thoughtNumber,
|
|
28
|
-
preview:
|
|
36
|
+
preview: truncatePreview(thought.thought, MAX_PREVIEW_CHARS),
|
|
29
37
|
}));
|
|
30
38
|
if (revisionInfo !== undefined) {
|
|
31
39
|
return { recentThoughts, revisionInfo };
|
|
@@ -4,6 +4,12 @@ export interface ThoughtStoreOptions {
|
|
|
4
4
|
maxMemoryBytes: number;
|
|
5
5
|
estimatedThoughtOverheadBytes: number;
|
|
6
6
|
}
|
|
7
|
+
export interface PruneStats {
|
|
8
|
+
truncatedActive: boolean;
|
|
9
|
+
droppedActiveCount: number;
|
|
10
|
+
removedThoughtsCount: number;
|
|
11
|
+
oldestAvailableThoughtNumber: number | null;
|
|
12
|
+
}
|
|
7
13
|
export declare class ThoughtStore {
|
|
8
14
|
#private;
|
|
9
15
|
constructor(options: ThoughtStoreOptions);
|
|
@@ -11,6 +17,7 @@ export declare class ThoughtStore {
|
|
|
11
17
|
thoughtNumber: number;
|
|
12
18
|
totalThoughts: number;
|
|
13
19
|
};
|
|
20
|
+
getLastPruneStats(): PruneStats | null;
|
|
14
21
|
storeThought(stored: StoredThought): void;
|
|
15
22
|
supersedeFrom(targetNumber: number, supersededBy: number, maxSupersedes?: number): {
|
|
16
23
|
supersedes: number[];
|
|
@@ -8,6 +8,7 @@ export class ThoughtStore {
|
|
|
8
8
|
#headIndex = 0;
|
|
9
9
|
#nextThoughtNumber = 1;
|
|
10
10
|
#estimatedBytes = 0;
|
|
11
|
+
#lastPruneStats = null;
|
|
11
12
|
#maxThoughts;
|
|
12
13
|
#maxMemoryBytes;
|
|
13
14
|
#estimatedThoughtOverheadBytes;
|
|
@@ -19,11 +20,11 @@ export class ThoughtStore {
|
|
|
19
20
|
nextThoughtNumbers(totalThoughts) {
|
|
20
21
|
const thoughtNumber = this.#nextThoughtNumber;
|
|
21
22
|
this.#nextThoughtNumber += 1;
|
|
22
|
-
const effectiveTotalThoughts = Math.max(totalThoughts,
|
|
23
|
-
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const effectiveTotalThoughts = Math.max(totalThoughts, this.#activeMaxTotalThoughts);
|
|
24
|
+
return { thoughtNumber, totalThoughts: effectiveTotalThoughts };
|
|
25
|
+
}
|
|
26
|
+
getLastPruneStats() {
|
|
27
|
+
return this.#lastPruneStats;
|
|
27
28
|
}
|
|
28
29
|
storeThought(stored) {
|
|
29
30
|
this.#thoughts.push(stored);
|
|
@@ -42,29 +43,39 @@ export class ThoughtStore {
|
|
|
42
43
|
}
|
|
43
44
|
this.#activeMaxTotalThoughts = maxTotal;
|
|
44
45
|
}
|
|
45
|
-
#
|
|
46
|
+
#findActiveLowerBound(thoughtNumber) {
|
|
46
47
|
const activeThoughtNumbers = this.#activeThoughtNumbers;
|
|
47
48
|
let low = 0;
|
|
48
49
|
let high = activeThoughtNumbers.length;
|
|
49
50
|
while (low < high) {
|
|
50
51
|
const mid = (low + high) >>> 1;
|
|
51
52
|
const midValue = activeThoughtNumbers[mid];
|
|
52
|
-
if (midValue === undefined)
|
|
53
|
+
if (midValue === undefined)
|
|
53
54
|
return -1;
|
|
54
|
-
|
|
55
|
-
if (midValue < thoughtNumber) {
|
|
55
|
+
if (midValue < thoughtNumber)
|
|
56
56
|
low = mid + 1;
|
|
57
|
-
|
|
58
|
-
else {
|
|
57
|
+
else
|
|
59
58
|
high = mid;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (low < activeThoughtNumbers.length &&
|
|
63
|
-
activeThoughtNumbers[low] === thoughtNumber) {
|
|
64
|
-
return low;
|
|
65
59
|
}
|
|
60
|
+
return low;
|
|
61
|
+
}
|
|
62
|
+
#findActiveThoughtIndex(thoughtNumber) {
|
|
63
|
+
const index = this.#findActiveLowerBound(thoughtNumber);
|
|
64
|
+
if (index < 0)
|
|
65
|
+
return -1;
|
|
66
|
+
if (index < this.#activeThoughtNumbers.length &&
|
|
67
|
+
this.#activeThoughtNumbers[index] === thoughtNumber)
|
|
68
|
+
return index;
|
|
66
69
|
return -1;
|
|
67
70
|
}
|
|
71
|
+
#findFirstActiveIndexAfter(thoughtNumber) {
|
|
72
|
+
const index = this.#findActiveLowerBound(thoughtNumber);
|
|
73
|
+
if (index < 0)
|
|
74
|
+
return 0;
|
|
75
|
+
if (this.#activeThoughtNumbers[index] === thoughtNumber)
|
|
76
|
+
return index + 1;
|
|
77
|
+
return index;
|
|
78
|
+
}
|
|
68
79
|
supersedeFrom(targetNumber, supersededBy, maxSupersedes) {
|
|
69
80
|
const startIndex = this.#findActiveThoughtIndex(targetNumber);
|
|
70
81
|
if (startIndex < 0)
|
|
@@ -110,6 +121,18 @@ export class ThoughtStore {
|
|
|
110
121
|
return this.#thoughts.length - this.#headIndex;
|
|
111
122
|
}
|
|
112
123
|
pruneHistoryIfNeeded() {
|
|
124
|
+
const beforeTotal = this.getTotalLength();
|
|
125
|
+
const beforeActive = this.#activeThoughts.length;
|
|
126
|
+
this.#lastPruneStats = {
|
|
127
|
+
truncatedActive: false,
|
|
128
|
+
droppedActiveCount: 0,
|
|
129
|
+
removedThoughtsCount: 0,
|
|
130
|
+
oldestAvailableThoughtNumber: this.#thoughts[this.#headIndex]?.thoughtNumber ?? null,
|
|
131
|
+
};
|
|
132
|
+
this.#performPruning();
|
|
133
|
+
this.#updatePruneStats(beforeTotal, beforeActive);
|
|
134
|
+
}
|
|
135
|
+
#performPruning() {
|
|
113
136
|
const excess = this.getTotalLength() - this.#maxThoughts;
|
|
114
137
|
if (excess > 0) {
|
|
115
138
|
const batch = Math.max(excess, Math.ceil(this.#maxThoughts * 0.1));
|
|
@@ -121,19 +144,24 @@ export class ThoughtStore {
|
|
|
121
144
|
this.#removeOldest(toRemove, { forceCompact: true });
|
|
122
145
|
}
|
|
123
146
|
}
|
|
147
|
+
#updatePruneStats(beforeTotal, beforeActive) {
|
|
148
|
+
if (!this.#lastPruneStats)
|
|
149
|
+
return;
|
|
150
|
+
const afterActive = this.#activeThoughts.length;
|
|
151
|
+
const stats = this.#lastPruneStats;
|
|
152
|
+
stats.removedThoughtsCount = Math.max(0, beforeTotal - this.getTotalLength());
|
|
153
|
+
stats.droppedActiveCount = Math.max(0, beforeActive - afterActive);
|
|
154
|
+
stats.truncatedActive = stats.droppedActiveCount > 0;
|
|
155
|
+
stats.oldestAvailableThoughtNumber =
|
|
156
|
+
this.#thoughts[this.#headIndex]?.thoughtNumber ?? null;
|
|
157
|
+
}
|
|
124
158
|
#estimateThoughtBytes(thought) {
|
|
125
|
-
return thought.
|
|
159
|
+
return thought.byteLength + this.#estimatedThoughtOverheadBytes;
|
|
126
160
|
}
|
|
127
161
|
#dropActiveThoughtsUpTo(thoughtNumber) {
|
|
128
162
|
if (this.#activeThoughts.length === 0)
|
|
129
163
|
return;
|
|
130
|
-
|
|
131
|
-
while (startIndex < this.#activeThoughts.length) {
|
|
132
|
-
const thought = this.#activeThoughts[startIndex];
|
|
133
|
-
if (!thought || thought.thoughtNumber > thoughtNumber)
|
|
134
|
-
break;
|
|
135
|
-
startIndex += 1;
|
|
136
|
-
}
|
|
164
|
+
const startIndex = this.#findFirstActiveIndexAfter(thoughtNumber);
|
|
137
165
|
if (startIndex === 0)
|
|
138
166
|
return;
|
|
139
167
|
this.#activeThoughts = this.#activeThoughts.slice(startIndex);
|
|
@@ -198,5 +226,6 @@ export class ThoughtStore {
|
|
|
198
226
|
this.#headIndex = 0;
|
|
199
227
|
this.#nextThoughtNumber = 1;
|
|
200
228
|
this.#estimatedBytes = 0;
|
|
229
|
+
this.#lastPruneStats = null;
|
|
201
230
|
}
|
|
202
231
|
}
|
package/dist/engine.d.ts
CHANGED
|
@@ -3,10 +3,12 @@ export interface ThinkingEngineOptions {
|
|
|
3
3
|
maxThoughts?: number;
|
|
4
4
|
maxMemoryBytes?: number;
|
|
5
5
|
estimatedThoughtOverheadBytes?: number;
|
|
6
|
+
maxSessions?: number;
|
|
6
7
|
}
|
|
7
8
|
export declare class ThinkingEngine {
|
|
8
9
|
#private;
|
|
9
10
|
static readonly DEFAULT_TOTAL_THOUGHTS = 3;
|
|
10
11
|
constructor(options?: ThinkingEngineOptions);
|
|
11
12
|
processThought(input: ThoughtData): ProcessResult;
|
|
13
|
+
processThoughtWithSession(sessionId: string, input: ThoughtData): ProcessResult;
|
|
12
14
|
}
|
package/dist/engine.js
CHANGED
|
@@ -4,42 +4,88 @@ import { buildContextSummary } from './engine/thoughtQueries.js';
|
|
|
4
4
|
import { ThoughtStore } from './engine/thoughtStore.js';
|
|
5
5
|
import { DEFAULT_MAX_THOUGHTS, ESTIMATED_THOUGHT_OVERHEAD_BYTES, MAX_MEMORY_BYTES, MAX_REVISABLE_THOUGHTS, MAX_SUPERSEDES, MAX_THOUGHTS_CAP, normalizeInt, } from './engineConfig.js';
|
|
6
6
|
export class ThinkingEngine {
|
|
7
|
-
#store;
|
|
8
|
-
#hasRevisions = false;
|
|
9
7
|
static DEFAULT_TOTAL_THOUGHTS = 3;
|
|
8
|
+
#maxThoughts;
|
|
9
|
+
#maxMemoryBytes;
|
|
10
|
+
#estimatedThoughtOverheadBytes;
|
|
11
|
+
#maxSessions;
|
|
12
|
+
#sessions = new Map();
|
|
10
13
|
constructor(options = {}) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
estimatedThoughtOverheadBytes,
|
|
14
|
+
this.#maxThoughts = normalizeInt(options.maxThoughts, DEFAULT_MAX_THOUGHTS, { min: 1, max: MAX_THOUGHTS_CAP });
|
|
15
|
+
this.#maxMemoryBytes = normalizeInt(options.maxMemoryBytes, MAX_MEMORY_BYTES, { min: 1, max: Number.MAX_SAFE_INTEGER });
|
|
16
|
+
this.#estimatedThoughtOverheadBytes = normalizeInt(options.estimatedThoughtOverheadBytes, ESTIMATED_THOUGHT_OVERHEAD_BYTES, { min: 1, max: Number.MAX_SAFE_INTEGER });
|
|
17
|
+
this.#maxSessions = normalizeInt(options.maxSessions, 50, {
|
|
18
|
+
min: 1,
|
|
19
|
+
max: 10_000,
|
|
18
20
|
});
|
|
21
|
+
this.#getSession('default');
|
|
19
22
|
}
|
|
20
23
|
processThought(input) {
|
|
24
|
+
return this.processThoughtWithSession('default', input);
|
|
25
|
+
}
|
|
26
|
+
processThoughtWithSession(sessionId, input) {
|
|
27
|
+
const session = this.#getSession(sessionId);
|
|
21
28
|
if (input.revisesThought !== undefined) {
|
|
22
|
-
return this.#processRevision(input);
|
|
29
|
+
return this.#processRevision(session, input);
|
|
30
|
+
}
|
|
31
|
+
return this.#processNewThought(session, input);
|
|
32
|
+
}
|
|
33
|
+
#getSession(sessionId) {
|
|
34
|
+
const key = sessionId.trim() || 'default';
|
|
35
|
+
const existing = this.#sessions.get(key);
|
|
36
|
+
if (existing) {
|
|
37
|
+
// bump to most-recent for LRU eviction
|
|
38
|
+
this.#sessions.delete(key);
|
|
39
|
+
this.#sessions.set(key, existing);
|
|
40
|
+
return existing;
|
|
41
|
+
}
|
|
42
|
+
const state = {
|
|
43
|
+
store: new ThoughtStore({
|
|
44
|
+
maxThoughts: this.#maxThoughts,
|
|
45
|
+
maxMemoryBytes: this.#maxMemoryBytes,
|
|
46
|
+
estimatedThoughtOverheadBytes: this.#estimatedThoughtOverheadBytes,
|
|
47
|
+
}),
|
|
48
|
+
hasRevisions: false,
|
|
49
|
+
};
|
|
50
|
+
this.#sessions.set(key, state);
|
|
51
|
+
this.#evictSessionsIfNeeded();
|
|
52
|
+
return state;
|
|
53
|
+
}
|
|
54
|
+
#evictSessionsIfNeeded() {
|
|
55
|
+
while (this.#sessions.size > this.#maxSessions) {
|
|
56
|
+
const oldestKey = this.#sessions.keys().next().value;
|
|
57
|
+
if (!oldestKey)
|
|
58
|
+
return;
|
|
59
|
+
if (oldestKey === 'default') {
|
|
60
|
+
const def = this.#sessions.get('default');
|
|
61
|
+
if (!def)
|
|
62
|
+
return;
|
|
63
|
+
this.#sessions.delete('default');
|
|
64
|
+
this.#sessions.set('default', def);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
this.#sessions.delete(oldestKey);
|
|
23
68
|
}
|
|
24
|
-
return this.#processNewThought(input);
|
|
25
69
|
}
|
|
26
|
-
#processNewThought(input) {
|
|
27
|
-
const { stored } = this.#createStoredThought(input);
|
|
28
|
-
this.#commitThought(stored);
|
|
29
|
-
return this.#buildProcessResult(stored);
|
|
70
|
+
#processNewThought(session, input) {
|
|
71
|
+
const { stored } = this.#createStoredThought(session.store, input);
|
|
72
|
+
this.#commitThought(session.store, stored);
|
|
73
|
+
return this.#buildProcessResult(session, stored);
|
|
30
74
|
}
|
|
31
|
-
#processRevision(input) {
|
|
32
|
-
const
|
|
75
|
+
#processRevision(session, input) {
|
|
76
|
+
const { store } = session;
|
|
77
|
+
const resolved = resolveRevisionTarget(input, (thoughtNumber) => store.getThoughtByNumber(thoughtNumber));
|
|
33
78
|
if (!resolved.ok)
|
|
34
|
-
return resolved.
|
|
79
|
+
return resolved.result;
|
|
35
80
|
const { targetNumber } = resolved;
|
|
36
|
-
const { numbers, stored } = this.#createStoredThought(input, {
|
|
81
|
+
const { numbers, stored } = this.#createStoredThought(store, input, {
|
|
37
82
|
revisionOf: targetNumber,
|
|
83
|
+
fallbackTotalThoughts: resolved.target.totalThoughts,
|
|
38
84
|
});
|
|
39
|
-
const { supersedes, supersedesTotal } =
|
|
40
|
-
|
|
41
|
-
this.#commitThought(stored);
|
|
42
|
-
return this.#buildProcessResult(stored, {
|
|
85
|
+
const { supersedes, supersedesTotal } = store.supersedeFrom(targetNumber, numbers.thoughtNumber, MAX_SUPERSEDES);
|
|
86
|
+
session.hasRevisions = true;
|
|
87
|
+
this.#commitThought(store, stored);
|
|
88
|
+
return this.#buildProcessResult(session, stored, {
|
|
43
89
|
revises: targetNumber,
|
|
44
90
|
supersedes,
|
|
45
91
|
supersedesTotal,
|
|
@@ -58,53 +104,60 @@ export class ThinkingEngine {
|
|
|
58
104
|
...(details.revisionOf !== undefined && {
|
|
59
105
|
revisionOf: details.revisionOf,
|
|
60
106
|
}),
|
|
107
|
+
byteLength: Buffer.byteLength(input.thought),
|
|
61
108
|
};
|
|
62
109
|
}
|
|
63
|
-
#createStoredThought(input, extras = {}) {
|
|
64
|
-
const effectiveTotalThoughts = this.#resolveEffectiveTotalThoughts(input);
|
|
65
|
-
const numbers =
|
|
110
|
+
#createStoredThought(store, input, extras = {}) {
|
|
111
|
+
const effectiveTotalThoughts = this.#resolveEffectiveTotalThoughts(store, input, extras.fallbackTotalThoughts);
|
|
112
|
+
const numbers = store.nextThoughtNumbers(effectiveTotalThoughts);
|
|
66
113
|
const stored = this.#buildStoredThought(input, {
|
|
67
114
|
...numbers,
|
|
68
|
-
...extras,
|
|
115
|
+
...(extras.revisionOf !== undefined && { revisionOf: extras.revisionOf }),
|
|
69
116
|
});
|
|
70
117
|
return { stored, numbers };
|
|
71
118
|
}
|
|
72
|
-
#commitThought(stored) {
|
|
73
|
-
|
|
74
|
-
|
|
119
|
+
#commitThought(store, stored) {
|
|
120
|
+
store.storeThought(stored);
|
|
121
|
+
store.pruneHistoryIfNeeded();
|
|
75
122
|
}
|
|
76
|
-
#resolveEffectiveTotalThoughts(input) {
|
|
123
|
+
#resolveEffectiveTotalThoughts(store, input, fallback) {
|
|
77
124
|
if (input.totalThoughts !== undefined) {
|
|
78
125
|
return normalizeInt(input.totalThoughts, _a.DEFAULT_TOTAL_THOUGHTS, { min: 1, max: MAX_THOUGHTS_CAP });
|
|
79
126
|
}
|
|
80
|
-
|
|
127
|
+
if (fallback !== undefined)
|
|
128
|
+
return fallback;
|
|
129
|
+
const activeThoughts = store.getActiveThoughts();
|
|
81
130
|
const lastActive = activeThoughts[activeThoughts.length - 1];
|
|
82
131
|
if (lastActive !== undefined) {
|
|
83
132
|
return lastActive.totalThoughts;
|
|
84
133
|
}
|
|
85
134
|
return _a.DEFAULT_TOTAL_THOUGHTS;
|
|
86
135
|
}
|
|
87
|
-
#buildProcessResult(stored, revisionInfo) {
|
|
88
|
-
const activeThoughts =
|
|
89
|
-
const
|
|
90
|
-
const isComplete =
|
|
91
|
-
const revisableThoughtsTotal = activeThoughts.length;
|
|
92
|
-
const revisableThoughts = this.#store.getActiveThoughtNumbers(MAX_REVISABLE_THOUGHTS);
|
|
136
|
+
#buildProcessResult(session, stored, revisionInfo) {
|
|
137
|
+
const activeThoughts = session.store.getActiveThoughts();
|
|
138
|
+
const activePathLength = activeThoughts.length;
|
|
139
|
+
const { progress, isComplete } = this.#calculateMetrics(activePathLength, stored.totalThoughts);
|
|
93
140
|
return {
|
|
94
141
|
ok: true,
|
|
95
142
|
result: {
|
|
96
143
|
thoughtNumber: stored.thoughtNumber,
|
|
97
144
|
totalThoughts: stored.totalThoughts,
|
|
98
|
-
progress
|
|
145
|
+
progress,
|
|
99
146
|
isComplete,
|
|
100
|
-
thoughtHistoryLength:
|
|
101
|
-
hasRevisions:
|
|
102
|
-
activePathLength
|
|
103
|
-
revisableThoughts,
|
|
104
|
-
revisableThoughtsTotal,
|
|
105
|
-
context,
|
|
147
|
+
thoughtHistoryLength: session.store.getTotalLength(),
|
|
148
|
+
hasRevisions: session.hasRevisions,
|
|
149
|
+
activePathLength,
|
|
150
|
+
revisableThoughts: session.store.getActiveThoughtNumbers(MAX_REVISABLE_THOUGHTS),
|
|
151
|
+
revisableThoughtsTotal: activePathLength,
|
|
152
|
+
context: buildContextSummary(activeThoughts, revisionInfo),
|
|
106
153
|
},
|
|
107
154
|
};
|
|
108
155
|
}
|
|
156
|
+
#calculateMetrics(active, total) {
|
|
157
|
+
return {
|
|
158
|
+
progress: total > 0 ? Math.min(1, active / total) : 1,
|
|
159
|
+
isComplete: active >= total,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
109
162
|
}
|
|
110
163
|
_a = ThinkingEngine;
|
package/dist/instructions.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# ThinkSeq Instructions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- Path: [src/instructions.md](src/instructions.md) -->
|
|
4
|
+
|
|
5
|
+
> Guidance for the Agent: These instructions are available as a resource (`internal://instructions`) or prompt (`get-help`). Load them when you are unsure about tool usage.
|
|
4
6
|
|
|
5
7
|
## 1. Core Capability
|
|
6
8
|
|
|
7
|
-
- **Domain:** In-memory, sequential thinking with revision (destructive rewind).
|
|
9
|
+
- **Domain:** In-memory, sequential thinking with revision (destructive rewind) per session.
|
|
8
10
|
- **Primary Resources:** `Thoughts`, `RevisionChain`, `ProgressContext`.
|
|
11
|
+
- **Tools:**
|
|
12
|
+
- **Read:** none.
|
|
13
|
+
- **Write:** `thinkseq` (records and revises thoughts).
|
|
9
14
|
|
|
10
15
|
## 2. The "Golden Path" Workflows (Critical)
|
|
11
16
|
|
|
@@ -13,30 +18,31 @@ _Follow this order; do not guess indices._
|
|
|
13
18
|
|
|
14
19
|
### Workflow A: Capture a reasoning chain
|
|
15
20
|
|
|
16
|
-
1. Call `thinkseq` with `thought` (
|
|
21
|
+
1. Call `thinkseq` with `thought` (optionally `totalThoughts`, `sessionId`).
|
|
17
22
|
2. Continue calling `thinkseq` for each step.
|
|
18
|
-
3. Read `progress` and `isComplete`
|
|
19
|
-
>
|
|
23
|
+
3. Read `progress` and `isComplete` to know when to stop.
|
|
24
|
+
> Constraint: Keep each step atomic; one decision per call.
|
|
20
25
|
|
|
21
26
|
### Workflow B: Revise a prior step
|
|
22
27
|
|
|
23
28
|
1. Call `thinkseq` to get the latest `revisableThoughts` list.
|
|
24
29
|
2. Call `thinkseq` again with `revisesThought` set to a valid entry.
|
|
25
30
|
3. Continue from the revised step.
|
|
26
|
-
>
|
|
31
|
+
> Constraint: Never guess `revisesThought`; always pick from `revisableThoughts`.
|
|
27
32
|
|
|
28
|
-
## 3. Tool Nuances &
|
|
33
|
+
## 3. Tool Nuances & Gotchas
|
|
29
34
|
|
|
30
|
-
_Do NOT repeat the JSON Schema. Focus on behavior._
|
|
35
|
+
_Do NOT repeat the JSON Schema. Focus on behavior and pitfalls._
|
|
31
36
|
|
|
32
|
-
- **`thinkseq
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
- **
|
|
37
|
+
- **`thinkseq`**
|
|
38
|
+
- **Purpose:** Append or revise a thought in the current session.
|
|
39
|
+
- **Inputs:** `thought` (1–8000 chars), `totalThoughts` (1–25), `sessionId` (1–200), `revisesThought` (int ≥ 1).
|
|
40
|
+
- **Side effects:** Mutates in-memory thought history; revisions supersede the target and later active thoughts.
|
|
41
|
+
- **Defaults:** If `totalThoughts` is omitted, the engine uses 3 or the last active total.
|
|
42
|
+
- **Compatibility:** Set `THINKSEQ_INCLUDE_TEXT_CONTENT=0|false|no|off` to omit JSON string content.
|
|
37
43
|
|
|
38
44
|
## 4. Error Handling Strategy
|
|
39
45
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
46
|
+
- On `E_REVISION_MISSING` or `E_REVISION_TARGET_NOT_FOUND`, fetch `revisableThoughts` and retry.
|
|
47
|
+
- On `E_REVISION_TARGET_SUPERSEDED`, revise the latest active thought instead.
|
|
48
|
+
- On `E_THINK`, verify inputs and retry once.
|
package/dist/lib/mcpLogging.d.ts
CHANGED
|
@@ -3,4 +3,7 @@ export interface LoggingTarget {
|
|
|
3
3
|
sendLoggingMessage: (params: LoggingMessageNotificationParams, sessionId?: string) => Promise<void>;
|
|
4
4
|
}
|
|
5
5
|
export declare function installMcpLogging(target: LoggingTarget): () => void;
|
|
6
|
-
export declare function installConsoleBridge(target: LoggingTarget):
|
|
6
|
+
export declare function installConsoleBridge(target: LoggingTarget): {
|
|
7
|
+
flush: () => void;
|
|
8
|
+
restore: () => void;
|
|
9
|
+
};
|