@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 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 { type ProcessLike, type RunDependencies } from './appConfig.js';
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 { buildShutdownDependencies, resolvePackageIdentity, resolveRunDependencies, } from './appConfig.js';
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', { list: undefined }), { title: 'Instructions', mimeType: 'text/markdown' }, (uri) => ({
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: true },
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 declare function resolveRevisionTarget(input: ThoughtData, getThoughtByNumber: (thoughtNumber: number) => StoredThought | undefined): {
2
+ export type RevisionTargetResolution = {
3
3
  ok: true;
4
4
  targetNumber: number;
5
+ target: StoredThought;
5
6
  } | {
6
7
  ok: false;
7
- error: ProcessResult;
8
+ result: ProcessResult;
8
9
  };
10
+ export declare function resolveRevisionTarget(input: ThoughtData, getThoughtByNumber: (thoughtNumber: number) => StoredThought | undefined): RevisionTargetResolution;
@@ -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 buildRevisionError('E_REVISION_TARGET_NOT_FOUND', `Thought ${targetNumber} not found`);
19
- }
20
- if (!target.isActive) {
21
- return buildRevisionError('E_REVISION_TARGET_SUPERSEDED', `Thought ${targetNumber} was already superseded`);
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
- if (activeThoughts.length <= 5) {
3
- const recent = activeThoughts.slice();
4
- const stepIndexes = recent.map((_, index) => index + 1);
5
- return { recent, stepIndexes };
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 formatThoughtPreview(thought) {
19
- if (thought.length <= 100)
20
- return thought;
21
- return `${thought.slice(0, 100)}...`;
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: formatThoughtPreview(thought.thought),
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, thoughtNumber, this.#activeMaxTotalThoughts);
23
- return {
24
- thoughtNumber,
25
- totalThoughts: effectiveTotalThoughts,
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
- #findActiveThoughtIndex(thoughtNumber) {
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.thought.length * 2 + this.#estimatedThoughtOverheadBytes;
159
+ return thought.byteLength + this.#estimatedThoughtOverheadBytes;
126
160
  }
127
161
  #dropActiveThoughtsUpTo(thoughtNumber) {
128
162
  if (this.#activeThoughts.length === 0)
129
163
  return;
130
- let startIndex = 0;
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
- const maxThoughts = normalizeInt(options.maxThoughts, DEFAULT_MAX_THOUGHTS, { min: 1, max: MAX_THOUGHTS_CAP });
12
- const maxMemoryBytes = normalizeInt(options.maxMemoryBytes, MAX_MEMORY_BYTES, { min: 1, max: Number.MAX_SAFE_INTEGER });
13
- const estimatedThoughtOverheadBytes = normalizeInt(options.estimatedThoughtOverheadBytes, ESTIMATED_THOUGHT_OVERHEAD_BYTES, { min: 1, max: Number.MAX_SAFE_INTEGER });
14
- this.#store = new ThoughtStore({
15
- maxThoughts,
16
- maxMemoryBytes,
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 resolved = resolveRevisionTarget(input, (thoughtNumber) => this.#store.getThoughtByNumber(thoughtNumber));
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.error;
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 } = this.#store.supersedeFrom(targetNumber, numbers.thoughtNumber, MAX_SUPERSEDES);
40
- this.#hasRevisions = true;
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 = this.#store.nextThoughtNumbers(effectiveTotalThoughts);
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
- this.#store.storeThought(stored);
74
- this.#store.pruneHistoryIfNeeded();
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
- const activeThoughts = this.#store.getActiveThoughts();
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 = this.#store.getActiveThoughts();
89
- const context = buildContextSummary(activeThoughts, revisionInfo);
90
- const isComplete = stored.thoughtNumber >= stored.totalThoughts;
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: Math.min(1, stored.thoughtNumber / stored.totalThoughts),
145
+ progress,
99
146
  isComplete,
100
- thoughtHistoryLength: this.#store.getTotalLength(),
101
- hasRevisions: this.#hasRevisions,
102
- activePathLength: activeThoughts.length,
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;
@@ -1,11 +1,16 @@
1
1
  # ThinkSeq Instructions
2
2
 
3
- > **Guidance for the Agent:** These instructions are available as a resource (`internal://instructions`). Load them when you are confused about tool usage.
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` (and optionally `totalThoughts`).
21
+ 1. Call `thinkseq` with `thought` (optionally `totalThoughts`, `sessionId`).
17
22
  2. Continue calling `thinkseq` for each step.
18
- 3. Read `progress` and `isComplete` from the response to know when to stop.
19
- > **Constraint:** Keep each step atomic; one decision per call.
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
- > **Constraint:** Never guess `revisesThought`; always pick from `revisableThoughts`.
31
+ > Constraint: Never guess `revisesThought`; always pick from `revisableThoughts`.
27
32
 
28
- ## 3. Tool Nuances & "Gotchas"
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
- - **Side Effects:** Mutates in-memory thought history (write operation).
34
- - **Limits:** `thought` max 8000 chars; `totalThoughts` max 25.
35
- - **Revisions:** Revising a thought supersedes that step and all later active steps.
36
- - **Compatibility:** Set `THINKSEQ_INCLUDE_TEXT_CONTENT=0|false|no|off` to omit the JSON string in `content`.
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
- - If `E_REVISION_TARGET_NOT_FOUND`, fetch a fresh `revisableThoughts` list and retry.
41
- - If `E_REVISION_TARGET_SUPERSEDED`, revise the latest active thought instead.
42
- - If `E_THINK`, verify inputs and retry once.
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.
@@ -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): void;
6
+ export declare function installConsoleBridge(target: LoggingTarget): {
7
+ flush: () => void;
8
+ restore: () => void;
9
+ };