@iworkforces/tracelattice 2.3.6 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -5
- package/dist/ServerConfig.d.ts +16 -0
- package/dist/ServerConfig.d.ts.map +1 -1
- package/dist/ServerConfig.js +11 -0
- package/dist/ServerConfig.js.map +1 -1
- package/dist/cli.js +234 -18
- package/dist/cli.js.map +1 -1
- package/dist/config/ConfigLoader.d.ts +2 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +2 -0
- package/dist/config/ConfigLoader.js.map +1 -1
- package/dist/contracts/PersistenceBackend.d.ts +26 -0
- package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
- package/dist/contracts/persistence-backlog.d.ts +21 -0
- package/dist/contracts/persistence-backlog.d.ts.map +1 -0
- package/dist/contracts/persistence-backlog.js +1 -0
- package/dist/core/HistoryManager.d.ts +15 -1
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +12 -2
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/IHistoryManager.d.ts +2 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.d.ts +25 -0
- package/dist/core/PersistenceBuffer.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.js +27 -5
- package/dist/core/PersistenceBuffer.js.map +1 -1
- package/dist/core/PersistenceBufferErrors.d.ts +10 -0
- package/dist/core/PersistenceBufferErrors.d.ts.map +1 -1
- package/dist/core/PersistenceBufferErrors.js +13 -1
- package/dist/core/PersistenceBufferErrors.js.map +1 -1
- package/dist/core/PersistenceRestore.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.js +25 -30
- package/dist/core/PersistenceRestore.js.map +1 -1
- package/dist/core/PersistenceWorkQueue.d.ts +9 -0
- package/dist/core/PersistenceWorkQueue.d.ts.map +1 -1
- package/dist/core/PersistenceWorkQueue.js +18 -0
- package/dist/core/PersistenceWorkQueue.js.map +1 -1
- package/dist/core/ThoughtEvaluator.d.ts +6 -0
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js +15 -1
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +3 -10
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/evaluator/VerificationLinks.d.ts +5 -0
- package/dist/core/evaluator/VerificationLinks.d.ts.map +1 -1
- package/dist/core/evaluator/VerificationLinks.js +47 -53
- package/dist/core/evaluator/VerificationLinks.js.map +1 -1
- package/dist/core/reasoning/ActiveEvidenceProjection.d.ts.map +1 -1
- package/dist/core/reasoning/ActiveEvidenceProjection.js +4 -3
- package/dist/core/reasoning/ActiveEvidenceProjection.js.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -1
- package/dist/errors.js.map +1 -1
- package/dist/health/HealthChecker.d.ts +42 -1
- package/dist/health/HealthChecker.d.ts.map +1 -1
- package/dist/health/HealthChecker.js +60 -8
- package/dist/health/HealthChecker.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +1 -1
- package/dist/metrics/metrics.impl.js.map +1 -1
- package/dist/persistence/FilePersistence.d.ts +2 -1
- package/dist/persistence/FilePersistence.d.ts.map +1 -1
- package/dist/persistence/FilePersistence.js +6 -1
- package/dist/persistence/FilePersistence.js.map +1 -1
- package/dist/persistence/FileSnapshotV2.d.ts +2 -0
- package/dist/persistence/FileSnapshotV2.d.ts.map +1 -1
- package/dist/persistence/FileSnapshotV2.js +39 -1
- package/dist/persistence/FileSnapshotV2.js.map +1 -1
- package/dist/persistence/MemoryPersistence.d.ts +4 -1
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
- package/dist/persistence/MemoryPersistence.js +30 -0
- package/dist/persistence/MemoryPersistence.js.map +1 -1
- package/dist/persistence/SqlitePersistence.d.ts +6 -1
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +31 -4
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/dist/transport/StreamableHttpTransport.d.ts +12 -0
- package/dist/transport/StreamableHttpTransport.d.ts.map +1 -1
- package/dist/transport/StreamableHttpTransport.js +19 -5
- package/dist/transport/StreamableHttpTransport.js.map +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -151,6 +151,7 @@ persistence:
|
|
|
151
151
|
persistenceBufferSize: 7 # example file-only override; default 100
|
|
152
152
|
persistenceFlushInterval: 750 # milliseconds, file-only; default 1000
|
|
153
153
|
persistenceMaxRetries: 2 # example file-only override; default 3
|
|
154
|
+
persistenceMaxPendingThoughtsPerSession: 500 # example file-only override; default and maximum 10000
|
|
154
155
|
features:
|
|
155
156
|
dagEdges: true
|
|
156
157
|
reasoningStrategy: sequential # sequential or tot
|
|
@@ -210,10 +211,18 @@ environment value is not expanded by `ConfigLoader`. `TRACELATTICE_DISCOVERY_CAC
|
|
|
210
211
|
300 seconds and becomes `discoveryCache.ttl: 300000` milliseconds. The interleave environment
|
|
211
212
|
variables ending in `_MS` and the corresponding YAML fields use milliseconds.
|
|
212
213
|
|
|
213
|
-
`persistenceBufferSize` (default 100 thoughts), `persistenceFlushInterval` (default 1000 ms),
|
|
214
|
-
`persistenceMaxRetries` (default 3)
|
|
215
|
-
|
|
216
|
-
`
|
|
214
|
+
`persistenceBufferSize` (default 100 thoughts), `persistenceFlushInterval` (default 1000 ms),
|
|
215
|
+
`persistenceMaxRetries` (default 3), and `persistenceMaxPendingThoughtsPerSession` (default 10000,
|
|
216
|
+
allowed 1 to 10000) are YAML/JSON settings, not environment variables. There are no
|
|
217
|
+
`TRACELATTICE_PERSISTENCE_BUFFER_SIZE`, `TRACELATTICE_PERSISTENCE_FLUSH_INTERVAL`,
|
|
218
|
+
`TRACELATTICE_PERSISTENCE_MAX_RETRIES`, or `TRACELATTICE_PERSISTENCE_MAX_PENDING_THOUGHTS_PER_SESSION`
|
|
219
|
+
overrides.
|
|
220
|
+
|
|
221
|
+
`persistenceMaxPendingThoughtsPerSession` limits the thought and backtrack writes one session may hold
|
|
222
|
+
unacknowledged, counting writes that failed terminally. At the limit a thought is rejected with
|
|
223
|
+
`PERSISTENCE_BACKLOG_FULL` before any history, branch, edge, outcome or suspension state changes, so
|
|
224
|
+
the same call can be retried with the same thought id. Other sessions are unaffected. Each rejection
|
|
225
|
+
also retries the retained failures in the background, so a retry succeeds once the backend is back.
|
|
217
226
|
|
|
218
227
|
## Transports
|
|
219
228
|
|
|
@@ -226,6 +235,8 @@ Set `TRACELATTICE_TRANSPORT_TYPE` to pick one:
|
|
|
226
235
|
|
|
227
236
|
The Streamable HTTP endpoint defaults to `POST /mcp` for JSON-RPC requests and supports stateful sessions via the `Mcp-Session-Id` header. `GET /mcp` is not allowed. The library also exposes `HttpTransport` for stateless JSON-RPC over HTTP, but the CLI does not select it with `TRACELATTICE_TRANSPORT_TYPE`.
|
|
228
237
|
|
|
238
|
+
Streamable HTTP also serves `GET /health` (liveness) and `GET /ready` (readiness). Both answer after Host validation and the shutting-down 503 but ahead of the per-client rate limiter, so an orchestrator polling them neither spends nor is rejected by the client budget. They still apply CORS origin validation and headers. With persistence disabled, `/ready` is `ok` with no components. With persistence enabled it probes the backend under a one second deadline: concurrent polls share one probe, a probe that timed out is joined until it settles, and a settled result is reused for one second. `/ready` answers 503 when the probe fails or times out, or when persistence is globally quarantined after a failed global reset. Otherwise it answers 200 and lists the quarantined session count, retained write failure count and pending thought count in the `persistenceBacklog` component details. Those counts do not make the instance unready, because nothing retries retained failures until an explicit drain.
|
|
239
|
+
|
|
229
240
|
Set all three transport-session retention variables or leave all three unset. Each must be a positive safe integer; the two timing values use milliseconds. With none set, transport sessions remain unbounded and live until shutdown. Retention settings are rejected with `TRACELATTICE_STREAMABLE_HTTP_STATEFUL=false`. At capacity, a request that would create a new session receives HTTP 503 with JSON-RPC code `-32000`. After an idle session is swept, a request using its header receives HTTP 404 with code `-32001`; the client must re-initialize without the old `Mcp-Session-Id`. Sweeps skip sessions with accepted POSTs still in flight and don't change thought sessions (`session_id`), their history, ownership, or TTL handling.
|
|
230
241
|
|
|
231
242
|
In Streamable HTTP mode, the port and retention values accept only unsigned decimal integers without leading zeros (except `0`), signs, whitespace, or trailing characters, and must be safe integers. `TRACELATTICE_STREAMABLE_HTTP_PORT` accepts 0 through 65535 and defaults to 9007. `TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS` must be at most 2147483647, the largest timer delay Node and Bun honor. Invalid values, a partial retention set, or an unknown `TRACELATTICE_TRANSPORT_TYPE` stop CLI startup with exit code 1. Stdio ignores HTTP-only variables.
|
|
@@ -296,7 +307,7 @@ The checked evaluation report separates deterministic structural validation and
|
|
|
296
307
|
|
|
297
308
|
### Persistence, recovery, and backtracking
|
|
298
309
|
|
|
299
|
-
Library callers can supply their own backend with `createServer({ persistenceBackend })`. This skips the persistence factory and the configured backend, whether or not configured persistence is enabled. The package root exports the implementation types `PersistenceBackend`, `ThoughtData`, `Edge`, `Summary`, `SessionId`, `ThoughtId`, and `BranchId` as types only; deep imports aren't needed. Startup restore reads the injected backend unless `loadFromPersistence: false` is set.
|
|
310
|
+
Library callers can supply their own backend with `createServer({ persistenceBackend })`. This skips the persistence factory and the configured backend, whether or not configured persistence is enabled. The package root exports the implementation types `PersistenceBackend`, `ThoughtData`, `Edge`, `Summary`, `SessionId`, `ThoughtId`, and `BranchId`, plus the restore-read types `PersistenceRestoreRead`, `PersistedSessionRecord`, and `PersistedBranchRecord`, as types only; deep imports aren't needed. Startup restore reads the injected backend unless `loadFromPersistence: false` is set.
|
|
300
311
|
|
|
301
312
|
An injected backend belongs to the caller. The server drains buffered writes before `stop()` or `dispose()` resolves, but never calls the backend's `close()`, including during startup-failure cleanup. The caller must close it after the awaited shutdown resolves. Factory-created backends are server-owned and closed exactly once.
|
|
302
313
|
|
|
@@ -304,6 +315,8 @@ SQLite selects its driver by runtime. Bun, including the packed `tracelattice` C
|
|
|
304
315
|
|
|
305
316
|
`PersistenceBackend` requires `saveBacktrackForSession(sessionId, thought, targetThoughtId)`. Every custom backend must implement it. The operation atomically corrects every retained stable-ID copy of the target, appends the backtrack thought, and applies retention. There is no capability fallback; the ordinary persistence methods retain their existing contracts.
|
|
306
317
|
|
|
318
|
+
`PersistenceBackend` also requires `loadAllSessionsForRestore()`, which returns a `PersistenceRestoreRead`. Every custom backend must implement it. Startup restore calls `healthy()` and then this one method: no `listSessions`, per-session, or per-branch load. The result must come from one consistent view of storage and hold each session exactly once in ascending id order, with its history, branches (ascending by branch id), edges, and summaries. Its sessions are the same set `listSessions()` reports, including sessions that exist only through edges or summaries; with branch persistence disabled the branches are empty. Duplicate session or branch records are rejected as incompatible storage. The per-item loads remain on the interface for other callers, and restore does not fall back to them.
|
|
319
|
+
|
|
307
320
|
Startup File/SQLite restore reads supported retained durable records with provenance restrictions; it doesn't restore all transient auxiliary state. It repairs retained backtrack copies without writing the snapshot, ignores an absent retained target, and rejects an ambiguous retained numeric target. TTL and capacity eviction remove live state without deleting durable records, but ordinary access doesn't reload them. Evidence pruned from durable storage by retention or deleted by explicit reset cannot be recovered. With persistence disabled, state cannot resume across restart; process-local expiration markers are also lost on restart.
|
|
308
321
|
|
|
309
322
|
Branches and revisions may legally reuse thought numbers. A numeric `verification_target` must uniquely identify a retained thought identity in the same session, so avoid reusing numbers when you intend unique numeric verification references. Targets may have any thought type. Recording a verification outcome requires an explicit result of `0` or `1` and a non-retracted target with confidence.
|
package/dist/ServerConfig.d.ts
CHANGED
|
@@ -88,6 +88,13 @@ export interface ServerConfigOptions {
|
|
|
88
88
|
* @default 3
|
|
89
89
|
*/
|
|
90
90
|
persistenceMaxRetries?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Maximum unacknowledged thought and backtrack writes per session, counting writes
|
|
93
|
+
* that failed terminally. At the limit new thoughts are rejected before any state changes.
|
|
94
|
+
* YAML/JSON only; no environment override.
|
|
95
|
+
* @default 10000
|
|
96
|
+
*/
|
|
97
|
+
persistenceMaxPendingThoughtsPerSession?: number;
|
|
91
98
|
/**
|
|
92
99
|
* Feature flag overrides. Missing fields are filled with defaults (all ON,
|
|
93
100
|
* reasoningStrategy='sequential').
|
|
@@ -162,6 +169,8 @@ export declare class ServerConfig {
|
|
|
162
169
|
persistenceFlushInterval: number;
|
|
163
170
|
/** Maximum number of retries for failed persistence flushes. */
|
|
164
171
|
persistenceMaxRetries: number;
|
|
172
|
+
/** Maximum unacknowledged thought and backtrack writes per session. */
|
|
173
|
+
persistenceMaxPendingThoughtsPerSession: number;
|
|
165
174
|
/** Feature flag toggles. */
|
|
166
175
|
features: FeatureFlags;
|
|
167
176
|
/** TTL in milliseconds for suspended tool-interleave entries. */
|
|
@@ -251,6 +260,13 @@ export declare class ServerConfig {
|
|
|
251
260
|
* @private
|
|
252
261
|
*/
|
|
253
262
|
private validatePersistenceMaxRetries;
|
|
263
|
+
/**
|
|
264
|
+
* Validates the per-session pending thought limit.
|
|
265
|
+
* @param value - The value to validate
|
|
266
|
+
* @returns The validated value or default (10000)
|
|
267
|
+
* @private
|
|
268
|
+
*/
|
|
269
|
+
private validatePersistenceMaxPendingThoughts;
|
|
254
270
|
/**
|
|
255
271
|
* Validates feature flags and fills defaults for missing fields.
|
|
256
272
|
* @param value - Partial feature flag overrides
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerConfig.d.ts","sourceRoot":"","sources":["../src/ServerConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE;QAChB;;;WAGG;QACH,GAAG,CAAC,EAAE,MAAM,CAAC;QACb;;;WAGG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;OAEG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAEhC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAElC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAEjC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,YAAY;IACxB,qDAAqD;IAC9C,cAAc,EAAE,MAAM,CAAC;IAE9B,8CAA8C;IACvC,WAAW,EAAE,MAAM,CAAC;IAE3B,mCAAmC;IAC5B,aAAa,EAAE,MAAM,CAAC;IAE7B,4CAA4C;IACrC,SAAS,EAAE,MAAM,EAAE,CAAC;IAE3B,2CAA2C;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAE1B,qCAAqC;IAC9B,cAAc,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAExD,iCAAiC;IAC1B,WAAW,EAAE,iBAAiB,CAAC;IAEtC,2EAA2E;IACpE,qBAAqB,EAAE,MAAM,CAAC;IAErC,qEAAqE;IAC9D,wBAAwB,EAAE,MAAM,CAAC;IAExC,gEAAgE;IACzD,qBAAqB,EAAE,MAAM,CAAC;IAErC,4BAA4B;IACrB,QAAQ,EAAE,YAAY,CAAC;IAE9B,iEAAiE;IAC1D,mBAAmB,EAAE,MAAM,CAAC;IAEnC,6EAA6E;IACtE,qBAAqB,EAAE,MAAM,CAAC;IAErC,yDAAyD;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAEnC;;;;;;;;;;;;;;;OAeG;gBACS,OAAO,GAAE,mBAAwB;
|
|
1
|
+
{"version":3,"file":"ServerConfig.d.ts","sourceRoot":"","sources":["../src/ServerConfig.ts"],"names":[],"mappings":"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"}
|
package/dist/ServerConfig.js
CHANGED
|
@@ -12,6 +12,7 @@ class ServerConfig {
|
|
|
12
12
|
persistenceBufferSize;
|
|
13
13
|
persistenceFlushInterval;
|
|
14
14
|
persistenceMaxRetries;
|
|
15
|
+
persistenceMaxPendingThoughtsPerSession;
|
|
15
16
|
features;
|
|
16
17
|
toolInterleaveTtlMs;
|
|
17
18
|
toolInterleaveSweepMs;
|
|
@@ -27,6 +28,7 @@ class ServerConfig {
|
|
|
27
28
|
this.persistenceBufferSize = this.validatePersistenceBufferSize(options.persistenceBufferSize);
|
|
28
29
|
this.persistenceFlushInterval = this.validatePersistenceFlushInterval(options.persistenceFlushInterval);
|
|
29
30
|
this.persistenceMaxRetries = this.validatePersistenceMaxRetries(options.persistenceMaxRetries);
|
|
31
|
+
this.persistenceMaxPendingThoughtsPerSession = this.validatePersistenceMaxPendingThoughts(options.persistenceMaxPendingThoughtsPerSession);
|
|
30
32
|
this.features = this.validateFeatures(options.features);
|
|
31
33
|
this.toolInterleaveTtlMs = this.validatePositiveMs(options.toolInterleaveTtlMs, 60000, 'toolInterleaveTtlMs');
|
|
32
34
|
this.toolInterleaveSweepMs = this.validatePositiveMs(options.toolInterleaveSweepMs, 60000, 'toolInterleaveSweepMs');
|
|
@@ -124,6 +126,14 @@ class ServerConfig {
|
|
|
124
126
|
if (value > 10) throw new ConfigurationError(`persistenceMaxRetries must not exceed 10, got ${value}`);
|
|
125
127
|
return value;
|
|
126
128
|
}
|
|
129
|
+
validatePersistenceMaxPendingThoughts(value) {
|
|
130
|
+
const defaultValue = 10000;
|
|
131
|
+
if (null == value) return defaultValue;
|
|
132
|
+
if ('number' != typeof value || !Number.isFinite(value)) throw new ConfigurationError(`persistenceMaxPendingThoughtsPerSession must be a finite number, got ${value}`);
|
|
133
|
+
if (value < 1) throw new ConfigurationError(`persistenceMaxPendingThoughtsPerSession must be at least 1, got ${value}`);
|
|
134
|
+
if (value > 10000) throw new ConfigurationError(`persistenceMaxPendingThoughtsPerSession must not exceed 10000, got ${value}`);
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
127
137
|
validateFeatures(value) {
|
|
128
138
|
const reasoningStrategy = value?.reasoningStrategy ?? 'sequential';
|
|
129
139
|
if ('sequential' !== reasoningStrategy && 'tot' !== reasoningStrategy) throw new ConfigurationError(`features.reasoningStrategy must be one of sequential, tot, got ${reasoningStrategy}`);
|
|
@@ -163,6 +173,7 @@ class ServerConfig {
|
|
|
163
173
|
persistenceBufferSize: this.persistenceBufferSize,
|
|
164
174
|
persistenceFlushInterval: this.persistenceFlushInterval,
|
|
165
175
|
persistenceMaxRetries: this.persistenceMaxRetries,
|
|
176
|
+
persistenceMaxPendingThoughtsPerSession: this.persistenceMaxPendingThoughtsPerSession,
|
|
166
177
|
features: this.features,
|
|
167
178
|
toolInterleaveTtlMs: this.toolInterleaveTtlMs,
|
|
168
179
|
toolInterleaveSweepMs: this.toolInterleaveSweepMs,
|
package/dist/ServerConfig.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerConfig.js","sources":["../src/ServerConfig.ts"],"sourcesContent":["/**\n * Server configuration management with validation.\n *\n * This module provides the `ServerConfig` class which handles all server configuration\n * with built-in validation and sensible defaults. Configuration values are validated\n * on construction and warnings are emitted for out-of-range values.\n *\n * @module ServerConfig\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ConfigurationError } from './errors.js';\nimport type { PersistenceConfig } from './contracts/PersistenceBackend.js';\n\n/**\n * Configuration options for creating a `ServerConfig` instance.\n *\n * All properties are optional with sensible defaults applied during validation.\n *\n * @example\n * ```typescript\n * const options: ServerConfigOptions = {\n * maxHistorySize: 500,\n * maxBranches: 25,\n * skillDirs: ['./custom-skills'],\n * toolDirs: ['./custom-tools'],\n * persistence: { enabled: true, backend: 'sqlite' }\n * };\n * ```\n */\n\nimport type { FeatureFlags } from './contracts/features.js';\nexport interface ServerConfigOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * @default 10000\n\t */\n\tmaxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * @default 50\n\t */\n\tmaxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * @default 100\n\t */\n\tmaxBranchSize?: number;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default Project .claude/skills and .agents/skills, plus both paths under the home directory\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Directory paths to search for tools.\n\t * @default ['.claude/tools', '~/.claude/tools']\n\t */\n\ttoolDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t */\n\tdiscoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * @default 300000 (5 minutes)\n\t\t */\n\t\tttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t * @default 100\n\t\t */\n\t\tmaxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\tpersistence?: PersistenceConfig;\n\n\t/**\n\t * Maximum number of thoughts to buffer before flushing to persistence.\n\t * YAML/JSON only; no environment override.\n\t * @default 100\n\t */\n\tpersistenceBufferSize?: number;\n\n\t/**\n\t * Interval in milliseconds between periodic persistence flushes.\n\t * YAML/JSON only; no environment override.\n\t * @default 1000\n\t */\n\tpersistenceFlushInterval?: number;\n\n\t/**\n\t * Maximum number of retries for failed persistence flushes.\n\t * YAML/JSON only; no environment override.\n\t * @default 3\n\t */\n\tpersistenceMaxRetries?: number;\n\n\t/**\n\t * Feature flag overrides. Missing fields are filled with defaults (all ON,\n\t * reasoningStrategy='sequential').\n\t */\n\tfeatures?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries in SuspensionStore.\n\t * @default 60000\n\t */\n\ttoolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * @default 60000\n\t */\n\ttoolInterleaveSweepMs?: number;\n\n\t/**\n\t * Maximum sessions per owner. Prevents one user from consuming all session slots.\n\t * @default 50\n\t */\n\tmaxSessionsPerOwner?: number;\n}\n\n/**\n * Server configuration with validation and defaults.\n *\n * This class manages all server configuration including history limits,\n * branch limits, discovery directories, cache settings, and persistence.\n * All values are validated on construction with appropriate defaults applied.\n *\n * @remarks\n * - Values outside recommended ranges trigger warnings but are still applied\n * - Supported environment variables override file-based configuration through ConfigLoader\n * - Logging uses file/environment input separately; ServerConfig does not hold log settings\n * - The `toJSON()` method provides a plain object representation\n *\n * @example\n * ```typescript\n * // Using defaults\n * const config1 = new ServerConfig();\n * console.log(config1.maxHistorySize); // 10000\n *\n * // With custom options\n * const config2 = new ServerConfig({\n * maxHistorySize: 500,\n * persistence: { enabled: true, backend: 'file', options: { dataDir: './data' } }\n * });\n *\n * // Export as plain object\n * const json = config2.toJSON();\n * ```\n */\nexport class ServerConfig {\n\t/** Maximum number of thoughts to keep in history. */\n\tpublic maxHistorySize: number;\n\n\t/** Maximum number of branches to maintain. */\n\tpublic maxBranches: number;\n\n\t/** Maximum size of each branch. */\n\tpublic maxBranchSize: number;\n\n\t/** Directory paths to search for skills. */\n\tpublic skillDirs: string[];\n\n\t/** Directory paths to search for tools. */\n\tpublic toolDirs: string[];\n\n\t/** Discovery cache configuration. */\n\tpublic discoveryCache: { ttl: number; maxSize: number };\n\n\t/** Persistence configuration. */\n\tpublic persistence: PersistenceConfig;\n\n\t/** Maximum number of thoughts to buffer before flushing to persistence. */\n\tpublic persistenceBufferSize: number;\n\n\t/** Interval in milliseconds between periodic persistence flushes. */\n\tpublic persistenceFlushInterval: number;\n\n\t/** Maximum number of retries for failed persistence flushes. */\n\tpublic persistenceMaxRetries: number;\n\n\t/** Feature flag toggles. */\n\tpublic features: FeatureFlags;\n\n\t/** TTL in milliseconds for suspended tool-interleave entries. */\n\tpublic toolInterleaveTtlMs: number;\n\n\t/** Sweep interval in milliseconds for SuspensionStore expiration cleanup. */\n\tpublic toolInterleaveSweepMs: number;\n\n\t/** Maximum sessions per owner (LRU per-owner bucket). */\n\tpublic maxSessionsPerOwner: number;\n\n\t/**\n\t * Creates a new ServerConfig instance with validation.\n\t *\n\t * All values are validated and defaults are applied for undefined options.\n\t * Warnings are emitted to console for values outside recommended ranges.\n\t *\n\t * @param options - Optional configuration overrides\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({\n\t * maxHistorySize: 500,\n\t * skillDirs: ['./my-skills']\n\t * });\n\t * ```\n\t */\n\tconstructor(options: ServerConfigOptions = {}) {\n\t\tthis.maxHistorySize = this.validateMaxHistorySize(options.maxHistorySize);\n\t\tthis.maxBranches = this.validateMaxBranches(options.maxBranches);\n\t\tthis.maxBranchSize = this.validateMaxBranchSize(options.maxBranchSize);\n\t\tthis.skillDirs = this.validateSkillDirs(options.skillDirs);\n\t\tthis.toolDirs = this.validateToolDirs(options.toolDirs);\n\t\tthis.discoveryCache = this.validateDiscoveryCache(options.discoveryCache);\n\t\tthis.persistence = this.validatePersistence(options.persistence);\n\t\tthis.persistenceBufferSize = this.validatePersistenceBufferSize(options.persistenceBufferSize);\n\t\tthis.persistenceFlushInterval = this.validatePersistenceFlushInterval(\n\t\t\toptions.persistenceFlushInterval\n\t\t);\n\t\tthis.persistenceMaxRetries = this.validatePersistenceMaxRetries(options.persistenceMaxRetries);\n\t\tthis.features = this.validateFeatures(options.features);\n\t\tthis.toolInterleaveTtlMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveTtlMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveTtlMs'\n\t\t);\n\t\tthis.toolInterleaveSweepMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveSweepMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveSweepMs'\n\t\t);\n\t\tthis.maxSessionsPerOwner = this.validateMaxSessionsPerOwner(options.maxSessionsPerOwner);\n\t}\n\n\t/**\n\t * Validates the max history size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (10000)\n\t * @private\n\t */\n\tprivate validateMaxHistorySize(value?: number): number {\n\t\tconst defaultValue = 10000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branches value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxBranches(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranches must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branch size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validateMaxBranchSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the skill directories value.\n\t * @param value - The value to validate\n\t * @returns The validated value or project-local and home-directory .claude/.agents skill paths\n\t * @private\n\t */\n\tprivate validateSkillDirs(value?: string[]): string[] {\n\t\tconst defaultValue = [\n\t\t\t'.claude/skills',\n\t\t\tjoin(homedir(), '.claude/skills'),\n\t\t\t'.agents/skills',\n\t\t\tjoin(homedir(), '.agents/skills'),\n\t\t];\n\t\treturn value === undefined ? defaultValue : [...value];\n\t}\n\n\tprivate validateToolDirs(value?: string[]): string[] {\n\t\tconst defaultValue = ['.claude/tools', join(homedir(), '.claude/tools')];\n\t\treturn value === undefined ? defaultValue : [...value];\n\t}\n\n\t/**\n\t * Validates the discovery cache configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validateDiscoveryCache(value?: { ttl?: number; maxSize?: number }): {\n\t\tttl: number;\n\t\tmaxSize: number;\n\t} {\n\t\treturn {\n\t\t\tttl: value?.ttl ?? 300000,\n\t\t\tmaxSize: value?.maxSize ?? 100,\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validatePersistence(value?: PersistenceConfig): PersistenceConfig {\n\t\tif (!value) {\n\t\t\t// Default to in-memory (no actual persistence, just consistency)\n\t\t\treturn {\n\t\t\t\tenabled: false,\n\t\t\t\tbackend: 'memory',\n\t\t\t};\n\t\t}\n\n\t\t// Validate backend type\n\t\tconst validBackends = ['file', 'sqlite', 'memory'];\n\t\tconst backend = value.backend ?? 'memory';\n\t\tif (!validBackends.includes(backend)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistence.backend must be one of ${validBackends.join(', ')}, got ${backend}`\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tenabled: value.enabled ?? false,\n\t\t\tbackend,\n\t\t\toptions: value.options ?? {},\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence buffer size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validatePersistenceBufferSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence flush interval value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validatePersistenceFlushInterval(value?: number): number {\n\t\tconst defaultValue = 1000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceFlushInterval must be a finite number, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value < 100) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must be at least 100, got ${value}`);\n\t\t}\n\t\tif (value > 60000) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must not exceed 60000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence max retries value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (3)\n\t * @private\n\t */\n\tprivate validatePersistenceMaxRetries(value?: number): number {\n\t\tconst defaultValue = 3;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 10) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must not exceed 10, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates feature flags and fills defaults for missing fields.\n\t * @param value - Partial feature flag overrides\n\t * @returns Fully populated FeatureFlags with defaults applied\n\t * @private\n\t */\n\tprivate validateFeatures(value?: Partial<FeatureFlags>): FeatureFlags {\n\t\tconst reasoningStrategy = value?.reasoningStrategy ?? 'sequential';\n\t\tif (reasoningStrategy !== 'sequential' && reasoningStrategy !== 'tot') {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`features.reasoningStrategy must be one of sequential, tot, got ${reasoningStrategy}`\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tdagEdges: value?.dagEdges ?? true,\n\t\t\treasoningStrategy,\n\t\t\tcalibration: value?.calibration ?? true,\n\t\t\tcompression: value?.compression ?? true,\n\t\t\ttoolInterleave: value?.toolInterleave ?? true,\n\t\t\tnewThoughtTypes: value?.newThoughtTypes ?? true,\n\t\t\toutcomeRecording: value?.outcomeRecording ?? true,\n\t\t};\n\t}\n\n\t/**\n\t * Validates a positive millisecond value with a default fallback.\n\t * @param value - The value to validate\n\t * @param defaultValue - Default applied if value is undefined/null\n\t * @param fieldName - Field name for error messages\n\t * @returns The validated millisecond value\n\t * @private\n\t */\n\tprivate validatePositiveMs(\n\t\tvalue: number | undefined,\n\t\tdefaultValue: number,\n\t\tfieldName: string\n\t): number {\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be at least 1, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max sessions per owner value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxSessionsPerOwner(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Converts the configuration to a plain object.\n\t *\n\t * Useful for serialization, logging, or when a plain object representation\n\t * is preferred over the ServerConfig instance.\n\t *\n\t * @returns A plain object representation of the configuration\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({ maxHistorySize: 500 });\n\t * const json = config.toJSON();\n\t * console.log(JSON.stringify(json, null, 2));\n\t * ```\n\t */\n\tpublic toJSON(): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: this.maxHistorySize,\n\t\t\tmaxBranches: this.maxBranches,\n\t\t\tmaxBranchSize: this.maxBranchSize,\n\t\t\tskillDirs: this.skillDirs,\n\t\t\ttoolDirs: this.toolDirs,\n\t\t\tdiscoveryCache: this.discoveryCache,\n\t\t\tpersistence: this.persistence,\n\t\t\tpersistenceBufferSize: this.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: this.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: this.persistenceMaxRetries,\n\t\t\tfeatures: this.features,\n\t\t\ttoolInterleaveTtlMs: this.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: this.toolInterleaveSweepMs,\n\t\t\tmaxSessionsPerOwner: this.maxSessionsPerOwner,\n\t\t};\n\t}\n}\n"],"names":["ServerConfig","options","value","defaultValue","Number","ConfigurationError","join","homedir","undefined","validBackends","backend","reasoningStrategy","fieldName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ServerConfig.js","sources":["../src/ServerConfig.ts"],"sourcesContent":["/**\n * Server configuration management with validation.\n *\n * This module provides the `ServerConfig` class which handles all server configuration\n * with built-in validation and sensible defaults. Configuration values are validated\n * on construction and warnings are emitted for out-of-range values.\n *\n * @module ServerConfig\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ConfigurationError } from './errors.js';\nimport type { PersistenceConfig } from './contracts/PersistenceBackend.js';\n\n/**\n * Configuration options for creating a `ServerConfig` instance.\n *\n * All properties are optional with sensible defaults applied during validation.\n *\n * @example\n * ```typescript\n * const options: ServerConfigOptions = {\n * maxHistorySize: 500,\n * maxBranches: 25,\n * skillDirs: ['./custom-skills'],\n * toolDirs: ['./custom-tools'],\n * persistence: { enabled: true, backend: 'sqlite' }\n * };\n * ```\n */\n\nimport type { FeatureFlags } from './contracts/features.js';\nexport interface ServerConfigOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * @default 10000\n\t */\n\tmaxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * @default 50\n\t */\n\tmaxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * @default 100\n\t */\n\tmaxBranchSize?: number;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default Project .claude/skills and .agents/skills, plus both paths under the home directory\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Directory paths to search for tools.\n\t * @default ['.claude/tools', '~/.claude/tools']\n\t */\n\ttoolDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t */\n\tdiscoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * @default 300000 (5 minutes)\n\t\t */\n\t\tttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t * @default 100\n\t\t */\n\t\tmaxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\tpersistence?: PersistenceConfig;\n\n\t/**\n\t * Maximum number of thoughts to buffer before flushing to persistence.\n\t * YAML/JSON only; no environment override.\n\t * @default 100\n\t */\n\tpersistenceBufferSize?: number;\n\n\t/**\n\t * Interval in milliseconds between periodic persistence flushes.\n\t * YAML/JSON only; no environment override.\n\t * @default 1000\n\t */\n\tpersistenceFlushInterval?: number;\n\n\t/**\n\t * Maximum number of retries for failed persistence flushes.\n\t * YAML/JSON only; no environment override.\n\t * @default 3\n\t */\n\tpersistenceMaxRetries?: number;\n\n\t/**\n\t * Maximum unacknowledged thought and backtrack writes per session, counting writes\n\t * that failed terminally. At the limit new thoughts are rejected before any state changes.\n\t * YAML/JSON only; no environment override.\n\t * @default 10000\n\t */\n\tpersistenceMaxPendingThoughtsPerSession?: number;\n\n\t/**\n\t * Feature flag overrides. Missing fields are filled with defaults (all ON,\n\t * reasoningStrategy='sequential').\n\t */\n\tfeatures?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries in SuspensionStore.\n\t * @default 60000\n\t */\n\ttoolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * @default 60000\n\t */\n\ttoolInterleaveSweepMs?: number;\n\n\t/**\n\t * Maximum sessions per owner. Prevents one user from consuming all session slots.\n\t * @default 50\n\t */\n\tmaxSessionsPerOwner?: number;\n}\n\n/**\n * Server configuration with validation and defaults.\n *\n * This class manages all server configuration including history limits,\n * branch limits, discovery directories, cache settings, and persistence.\n * All values are validated on construction with appropriate defaults applied.\n *\n * @remarks\n * - Values outside recommended ranges trigger warnings but are still applied\n * - Supported environment variables override file-based configuration through ConfigLoader\n * - Logging uses file/environment input separately; ServerConfig does not hold log settings\n * - The `toJSON()` method provides a plain object representation\n *\n * @example\n * ```typescript\n * // Using defaults\n * const config1 = new ServerConfig();\n * console.log(config1.maxHistorySize); // 10000\n *\n * // With custom options\n * const config2 = new ServerConfig({\n * maxHistorySize: 500,\n * persistence: { enabled: true, backend: 'file', options: { dataDir: './data' } }\n * });\n *\n * // Export as plain object\n * const json = config2.toJSON();\n * ```\n */\nexport class ServerConfig {\n\t/** Maximum number of thoughts to keep in history. */\n\tpublic maxHistorySize: number;\n\n\t/** Maximum number of branches to maintain. */\n\tpublic maxBranches: number;\n\n\t/** Maximum size of each branch. */\n\tpublic maxBranchSize: number;\n\n\t/** Directory paths to search for skills. */\n\tpublic skillDirs: string[];\n\n\t/** Directory paths to search for tools. */\n\tpublic toolDirs: string[];\n\n\t/** Discovery cache configuration. */\n\tpublic discoveryCache: { ttl: number; maxSize: number };\n\n\t/** Persistence configuration. */\n\tpublic persistence: PersistenceConfig;\n\n\t/** Maximum number of thoughts to buffer before flushing to persistence. */\n\tpublic persistenceBufferSize: number;\n\n\t/** Interval in milliseconds between periodic persistence flushes. */\n\tpublic persistenceFlushInterval: number;\n\n\t/** Maximum number of retries for failed persistence flushes. */\n\tpublic persistenceMaxRetries: number;\n\n\t/** Maximum unacknowledged thought and backtrack writes per session. */\n\tpublic persistenceMaxPendingThoughtsPerSession: number;\n\n\t/** Feature flag toggles. */\n\tpublic features: FeatureFlags;\n\n\t/** TTL in milliseconds for suspended tool-interleave entries. */\n\tpublic toolInterleaveTtlMs: number;\n\n\t/** Sweep interval in milliseconds for SuspensionStore expiration cleanup. */\n\tpublic toolInterleaveSweepMs: number;\n\n\t/** Maximum sessions per owner (LRU per-owner bucket). */\n\tpublic maxSessionsPerOwner: number;\n\n\t/**\n\t * Creates a new ServerConfig instance with validation.\n\t *\n\t * All values are validated and defaults are applied for undefined options.\n\t * Warnings are emitted to console for values outside recommended ranges.\n\t *\n\t * @param options - Optional configuration overrides\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({\n\t * maxHistorySize: 500,\n\t * skillDirs: ['./my-skills']\n\t * });\n\t * ```\n\t */\n\tconstructor(options: ServerConfigOptions = {}) {\n\t\tthis.maxHistorySize = this.validateMaxHistorySize(options.maxHistorySize);\n\t\tthis.maxBranches = this.validateMaxBranches(options.maxBranches);\n\t\tthis.maxBranchSize = this.validateMaxBranchSize(options.maxBranchSize);\n\t\tthis.skillDirs = this.validateSkillDirs(options.skillDirs);\n\t\tthis.toolDirs = this.validateToolDirs(options.toolDirs);\n\t\tthis.discoveryCache = this.validateDiscoveryCache(options.discoveryCache);\n\t\tthis.persistence = this.validatePersistence(options.persistence);\n\t\tthis.persistenceBufferSize = this.validatePersistenceBufferSize(options.persistenceBufferSize);\n\t\tthis.persistenceFlushInterval = this.validatePersistenceFlushInterval(\n\t\t\toptions.persistenceFlushInterval\n\t\t);\n\t\tthis.persistenceMaxRetries = this.validatePersistenceMaxRetries(options.persistenceMaxRetries);\n\t\tthis.persistenceMaxPendingThoughtsPerSession = this.validatePersistenceMaxPendingThoughts(\n\t\t\toptions.persistenceMaxPendingThoughtsPerSession\n\t\t);\n\t\tthis.features = this.validateFeatures(options.features);\n\t\tthis.toolInterleaveTtlMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveTtlMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveTtlMs'\n\t\t);\n\t\tthis.toolInterleaveSweepMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveSweepMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveSweepMs'\n\t\t);\n\t\tthis.maxSessionsPerOwner = this.validateMaxSessionsPerOwner(options.maxSessionsPerOwner);\n\t}\n\n\t/**\n\t * Validates the max history size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (10000)\n\t * @private\n\t */\n\tprivate validateMaxHistorySize(value?: number): number {\n\t\tconst defaultValue = 10000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branches value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxBranches(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranches must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branch size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validateMaxBranchSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the skill directories value.\n\t * @param value - The value to validate\n\t * @returns The validated value or project-local and home-directory .claude/.agents skill paths\n\t * @private\n\t */\n\tprivate validateSkillDirs(value?: string[]): string[] {\n\t\tconst defaultValue = [\n\t\t\t'.claude/skills',\n\t\t\tjoin(homedir(), '.claude/skills'),\n\t\t\t'.agents/skills',\n\t\t\tjoin(homedir(), '.agents/skills'),\n\t\t];\n\t\treturn value === undefined ? defaultValue : [...value];\n\t}\n\n\tprivate validateToolDirs(value?: string[]): string[] {\n\t\tconst defaultValue = ['.claude/tools', join(homedir(), '.claude/tools')];\n\t\treturn value === undefined ? defaultValue : [...value];\n\t}\n\n\t/**\n\t * Validates the discovery cache configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validateDiscoveryCache(value?: { ttl?: number; maxSize?: number }): {\n\t\tttl: number;\n\t\tmaxSize: number;\n\t} {\n\t\treturn {\n\t\t\tttl: value?.ttl ?? 300000,\n\t\t\tmaxSize: value?.maxSize ?? 100,\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validatePersistence(value?: PersistenceConfig): PersistenceConfig {\n\t\tif (!value) {\n\t\t\t// Default to in-memory (no actual persistence, just consistency)\n\t\t\treturn {\n\t\t\t\tenabled: false,\n\t\t\t\tbackend: 'memory',\n\t\t\t};\n\t\t}\n\n\t\t// Validate backend type\n\t\tconst validBackends = ['file', 'sqlite', 'memory'];\n\t\tconst backend = value.backend ?? 'memory';\n\t\tif (!validBackends.includes(backend)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistence.backend must be one of ${validBackends.join(', ')}, got ${backend}`\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tenabled: value.enabled ?? false,\n\t\t\tbackend,\n\t\t\toptions: value.options ?? {},\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence buffer size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validatePersistenceBufferSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence flush interval value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validatePersistenceFlushInterval(value?: number): number {\n\t\tconst defaultValue = 1000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceFlushInterval must be a finite number, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value < 100) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must be at least 100, got ${value}`);\n\t\t}\n\t\tif (value > 60000) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must not exceed 60000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence max retries value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (3)\n\t * @private\n\t */\n\tprivate validatePersistenceMaxRetries(value?: number): number {\n\t\tconst defaultValue = 3;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 10) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must not exceed 10, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the per-session pending thought limit.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (10000)\n\t * @private\n\t */\n\tprivate validatePersistenceMaxPendingThoughts(value?: number): number {\n\t\tconst defaultValue = 10000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceMaxPendingThoughtsPerSession must be a finite number, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceMaxPendingThoughtsPerSession must be at least 1, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceMaxPendingThoughtsPerSession must not exceed 10000, got ${value}`\n\t\t\t);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates feature flags and fills defaults for missing fields.\n\t * @param value - Partial feature flag overrides\n\t * @returns Fully populated FeatureFlags with defaults applied\n\t * @private\n\t */\n\tprivate validateFeatures(value?: Partial<FeatureFlags>): FeatureFlags {\n\t\tconst reasoningStrategy = value?.reasoningStrategy ?? 'sequential';\n\t\tif (reasoningStrategy !== 'sequential' && reasoningStrategy !== 'tot') {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`features.reasoningStrategy must be one of sequential, tot, got ${reasoningStrategy}`\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tdagEdges: value?.dagEdges ?? true,\n\t\t\treasoningStrategy,\n\t\t\tcalibration: value?.calibration ?? true,\n\t\t\tcompression: value?.compression ?? true,\n\t\t\ttoolInterleave: value?.toolInterleave ?? true,\n\t\t\tnewThoughtTypes: value?.newThoughtTypes ?? true,\n\t\t\toutcomeRecording: value?.outcomeRecording ?? true,\n\t\t};\n\t}\n\n\t/**\n\t * Validates a positive millisecond value with a default fallback.\n\t * @param value - The value to validate\n\t * @param defaultValue - Default applied if value is undefined/null\n\t * @param fieldName - Field name for error messages\n\t * @returns The validated millisecond value\n\t * @private\n\t */\n\tprivate validatePositiveMs(\n\t\tvalue: number | undefined,\n\t\tdefaultValue: number,\n\t\tfieldName: string\n\t): number {\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be at least 1, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max sessions per owner value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxSessionsPerOwner(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxSessionsPerOwner must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Converts the configuration to a plain object.\n\t *\n\t * Useful for serialization, logging, or when a plain object representation\n\t * is preferred over the ServerConfig instance.\n\t *\n\t * @returns A plain object representation of the configuration\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({ maxHistorySize: 500 });\n\t * const json = config.toJSON();\n\t * console.log(JSON.stringify(json, null, 2));\n\t * ```\n\t */\n\tpublic toJSON(): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: this.maxHistorySize,\n\t\t\tmaxBranches: this.maxBranches,\n\t\t\tmaxBranchSize: this.maxBranchSize,\n\t\t\tskillDirs: this.skillDirs,\n\t\t\ttoolDirs: this.toolDirs,\n\t\t\tdiscoveryCache: this.discoveryCache,\n\t\t\tpersistence: this.persistence,\n\t\t\tpersistenceBufferSize: this.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: this.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: this.persistenceMaxRetries,\n\t\t\tpersistenceMaxPendingThoughtsPerSession: this.persistenceMaxPendingThoughtsPerSession,\n\t\t\tfeatures: this.features,\n\t\t\ttoolInterleaveTtlMs: this.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: this.toolInterleaveSweepMs,\n\t\t\tmaxSessionsPerOwner: this.maxSessionsPerOwner,\n\t\t};\n\t}\n}\n"],"names":["ServerConfig","options","value","defaultValue","Number","ConfigurationError","join","homedir","undefined","validBackends","backend","reasoningStrategy","fieldName"],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -136,7 +136,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
136
136
|
PERSISTENCE_SESSION_BARRIER_REENTRANCY: 'PERSISTENCE_SESSION_BARRIER_REENTRANCY',
|
|
137
137
|
PERSISTENCE_SCOPE_MISMATCH: 'PERSISTENCE_SCOPE_MISMATCH',
|
|
138
138
|
PERSISTENCE_UNAVAILABLE: 'PERSISTENCE_UNAVAILABLE',
|
|
139
|
-
PERSISTENCE_COMPATIBILITY: 'PERSISTENCE_COMPATIBILITY'
|
|
139
|
+
PERSISTENCE_COMPATIBILITY: 'PERSISTENCE_COMPATIBILITY',
|
|
140
|
+
PERSISTENCE_BACKLOG_FULL: 'PERSISTENCE_BACKLOG_FULL'
|
|
140
141
|
};
|
|
141
142
|
/**
|
|
142
143
|
* All known warning codes as a const object.
|
|
@@ -2209,6 +2210,34 @@ function parseSessionRetentionOptions(options) {
|
|
|
2209
2210
|
this._metrics?.histogram('streamable_http_request_duration_seconds', durationSeconds, {});
|
|
2210
2211
|
});
|
|
2211
2212
|
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Validate the request origin and attach CORS headers.
|
|
2215
|
+
*
|
|
2216
|
+
* @returns false when the origin is rejected and the 403 has been sent
|
|
2217
|
+
*/ _admitCors(req, res) {
|
|
2218
|
+
if (!this.validateCorsOrigin(req)) {
|
|
2219
|
+
this._sendJsonRpcError(res, 403, -32000, 'Forbidden - invalid origin');
|
|
2220
|
+
return false;
|
|
2221
|
+
}
|
|
2222
|
+
this.setCorsHeaders(res);
|
|
2223
|
+
return true;
|
|
2224
|
+
}
|
|
2225
|
+
_isProbeRoute(req, urlPath) {
|
|
2226
|
+
return req.method === 'GET' && (urlPath === '/health' || urlPath === '/ready');
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Serve GET /health and GET /ready. These run after Host validation and the shutdown check
|
|
2230
|
+
* but ahead of the rate limiter; they keep CORS origin validation and headers.
|
|
2231
|
+
*/ async _serveProbe(req, res, urlPath) {
|
|
2232
|
+
if (!this._admitCors(req, res)) {
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
if (urlPath === '/health') {
|
|
2236
|
+
this._handleHealthCheck(res);
|
|
2237
|
+
} else {
|
|
2238
|
+
await this._handleReadinessCheck(res);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2212
2241
|
/**
|
|
2213
2242
|
* Route and handle incoming HTTP requests.
|
|
2214
2243
|
*/ async _handleRequest(req, res) {
|
|
@@ -2223,6 +2252,13 @@ function parseSessionRetentionOptions(options) {
|
|
|
2223
2252
|
this._sendJsonRpcError(res, 503, -32603, 'Server is shutting down');
|
|
2224
2253
|
return;
|
|
2225
2254
|
}
|
|
2255
|
+
// Parse URL path
|
|
2256
|
+
const urlPath = req.url?.split('?')[0] ?? '/';
|
|
2257
|
+
// Probes (/health, /ready) precede rate limiting so polling never spends the client budget
|
|
2258
|
+
if (this._isProbeRoute(req, urlPath)) {
|
|
2259
|
+
await this._serveProbe(req, res, urlPath);
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2226
2262
|
// Rate limiting
|
|
2227
2263
|
const clientIp = this.getClientIp(req);
|
|
2228
2264
|
if (this.checkRateLimit(clientIp)) {
|
|
@@ -2231,11 +2267,9 @@ function parseSessionRetentionOptions(options) {
|
|
|
2231
2267
|
return;
|
|
2232
2268
|
}
|
|
2233
2269
|
// CORS validation
|
|
2234
|
-
if (!this.
|
|
2235
|
-
this._sendJsonRpcError(res, 403, -32000, 'Forbidden - invalid origin');
|
|
2270
|
+
if (!this._admitCors(req, res)) {
|
|
2236
2271
|
return;
|
|
2237
2272
|
}
|
|
2238
|
-
this.setCorsHeaders(res);
|
|
2239
2273
|
// CORS preflight
|
|
2240
2274
|
if (req.method === 'OPTIONS') {
|
|
2241
2275
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id');
|
|
@@ -2243,23 +2277,11 @@ function parseSessionRetentionOptions(options) {
|
|
|
2243
2277
|
res.end();
|
|
2244
2278
|
return;
|
|
2245
2279
|
}
|
|
2246
|
-
// Parse URL path
|
|
2247
|
-
const urlPath = req.url?.split('?')[0] ?? '/';
|
|
2248
2280
|
// Metrics endpoint
|
|
2249
2281
|
if (req.method === 'GET' && urlPath === '/metrics') {
|
|
2250
2282
|
this._handleMetrics(res);
|
|
2251
2283
|
return;
|
|
2252
2284
|
}
|
|
2253
|
-
// Health check (liveness)
|
|
2254
|
-
if (req.method === 'GET' && urlPath === '/health') {
|
|
2255
|
-
this._handleHealthCheck(res);
|
|
2256
|
-
return;
|
|
2257
|
-
}
|
|
2258
|
-
// Readiness check
|
|
2259
|
-
if (req.method === 'GET' && urlPath === '/ready') {
|
|
2260
|
-
await this._handleReadinessCheck(res);
|
|
2261
|
-
return;
|
|
2262
|
-
}
|
|
2263
2285
|
// MCP endpoint routing
|
|
2264
2286
|
if (urlPath === this._path) {
|
|
2265
2287
|
if (req.method === 'POST') {
|
|
@@ -3159,6 +3181,191 @@ var RequestContext = __webpack_require__("i");
|
|
|
3159
3181
|
}
|
|
3160
3182
|
}
|
|
3161
3183
|
|
|
3184
|
+
;// CONCATENATED MODULE: ./src/health/HealthChecker.ts
|
|
3185
|
+
/**
|
|
3186
|
+
* Aggregate health checking system.
|
|
3187
|
+
*
|
|
3188
|
+
* Provides liveness and readiness checks by aggregating the health
|
|
3189
|
+
* status of registered backend components (persistence, pool, etc.).
|
|
3190
|
+
*
|
|
3191
|
+
* @module health
|
|
3192
|
+
*/
|
|
3193
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 1000;
|
|
3194
|
+
function describeBacklog(view) {
|
|
3195
|
+
const counts = `quarantined sessions: ${view.quarantinedSessionCount}, ` + `retained failures: ${view.retainedFailureCount}, ` + `pending thoughts: ${view.pendingThoughtCount}`;
|
|
3196
|
+
return view.globalQuarantined ? `Persistence is globally quarantined; ${counts}` : counts;
|
|
3197
|
+
}
|
|
3198
|
+
/**
|
|
3199
|
+
* No-op logger used when none is provided.
|
|
3200
|
+
*/ class NoopLogger {
|
|
3201
|
+
info() {}
|
|
3202
|
+
warn() {}
|
|
3203
|
+
error() {}
|
|
3204
|
+
debug() {}
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Aggregates component health into liveness and readiness checks.
|
|
3208
|
+
*
|
|
3209
|
+
* - `checkLiveness()` always returns 'ok' if the process is running.
|
|
3210
|
+
* - `checkReadiness()` checks all registered backends and returns an aggregate status.
|
|
3211
|
+
*
|
|
3212
|
+
* @example
|
|
3213
|
+
* ```typescript
|
|
3214
|
+
* const checker = new HealthChecker({ persistence: myBackend });
|
|
3215
|
+
* const readiness = await checker.checkReadiness();
|
|
3216
|
+
* // { status: 'ok', timestamp: '...', components: { persistence: { ... } } }
|
|
3217
|
+
* ```
|
|
3218
|
+
*/ class HealthChecker {
|
|
3219
|
+
_persistence;
|
|
3220
|
+
_backlog;
|
|
3221
|
+
_logger;
|
|
3222
|
+
_probeTimeoutMs;
|
|
3223
|
+
_probeFreshnessMs;
|
|
3224
|
+
/** Verdict of the backend probe that has not settled yet, shared by every caller. */ _inFlight = null;
|
|
3225
|
+
_settled = null;
|
|
3226
|
+
constructor(options = {}){
|
|
3227
|
+
this._persistence = options.persistence ?? null;
|
|
3228
|
+
this._backlog = options.persistenceBacklog ?? null;
|
|
3229
|
+
this._logger = options.logger ?? new NoopLogger();
|
|
3230
|
+
this._probeTimeoutMs = options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
3231
|
+
this._probeFreshnessMs = options.probeFreshnessMs ?? 0;
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Liveness check — returns 'ok' if the process is alive.
|
|
3235
|
+
*
|
|
3236
|
+
* This is a synchronous check that always succeeds; if this code
|
|
3237
|
+
* can execute, the process is alive.
|
|
3238
|
+
*/ checkLiveness() {
|
|
3239
|
+
return {
|
|
3240
|
+
status: 'ok',
|
|
3241
|
+
timestamp: new Date().toISOString(),
|
|
3242
|
+
components: {}
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
/**
|
|
3246
|
+
* Readiness check — aggregates health of all registered backends.
|
|
3247
|
+
*
|
|
3248
|
+
* Checks each component and returns:
|
|
3249
|
+
* - 'ok' if all components are healthy (or no components registered)
|
|
3250
|
+
* - 'degraded' if some components are unhealthy
|
|
3251
|
+
* - 'unhealthy' if all components are unhealthy
|
|
3252
|
+
*
|
|
3253
|
+
* The persistence probe is bounded by `probeTimeoutMs`, shared between concurrent calls, and
|
|
3254
|
+
* reused for `probeFreshnessMs` after it settles. The backlog component is read on every call.
|
|
3255
|
+
*/ async checkReadiness() {
|
|
3256
|
+
const components = {};
|
|
3257
|
+
if (this._persistence) {
|
|
3258
|
+
components['persistence'] = {
|
|
3259
|
+
...await this._checkPersistence()
|
|
3260
|
+
};
|
|
3261
|
+
}
|
|
3262
|
+
const backlog = this._checkBacklog();
|
|
3263
|
+
if (backlog) {
|
|
3264
|
+
components['persistenceBacklog'] = backlog;
|
|
3265
|
+
}
|
|
3266
|
+
const totalCount = Object.keys(components).length;
|
|
3267
|
+
const healthyCount = Object.values(components).filter((component)=>component.healthy).length;
|
|
3268
|
+
// Determine aggregate status
|
|
3269
|
+
let status;
|
|
3270
|
+
if (totalCount === 0 || healthyCount === totalCount) {
|
|
3271
|
+
status = 'ok';
|
|
3272
|
+
} else if (healthyCount === 0) {
|
|
3273
|
+
status = 'unhealthy';
|
|
3274
|
+
} else {
|
|
3275
|
+
status = 'degraded';
|
|
3276
|
+
}
|
|
3277
|
+
const result = {
|
|
3278
|
+
status,
|
|
3279
|
+
timestamp: new Date().toISOString(),
|
|
3280
|
+
components
|
|
3281
|
+
};
|
|
3282
|
+
if (status !== 'ok') {
|
|
3283
|
+
this._logger.warn('Readiness check returned non-ok status', {
|
|
3284
|
+
status,
|
|
3285
|
+
components
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
return result;
|
|
3289
|
+
}
|
|
3290
|
+
/**
|
|
3291
|
+
* Persistence component from the freshest source available: a settled result still inside
|
|
3292
|
+
* the freshness window, else the probe already running (even past its deadline), else a new
|
|
3293
|
+
* probe.
|
|
3294
|
+
*/ async _checkPersistence() {
|
|
3295
|
+
const settled = this._settled;
|
|
3296
|
+
if (settled !== null && Date.now() - settled.settledAt < this._probeFreshnessMs) {
|
|
3297
|
+
return settled.component;
|
|
3298
|
+
}
|
|
3299
|
+
this._inFlight ??= this._startProbe();
|
|
3300
|
+
return this._inFlight;
|
|
3301
|
+
}
|
|
3302
|
+
/**
|
|
3303
|
+
* Start one backend probe. The backend call is never abandoned: it stays tracked until it
|
|
3304
|
+
* settles, while the returned verdict resolves at that point or at the deadline, whichever
|
|
3305
|
+
* comes first.
|
|
3306
|
+
*/ _startProbe() {
|
|
3307
|
+
const settled = this._probeBackend().then((component)=>{
|
|
3308
|
+
this._inFlight = null;
|
|
3309
|
+
this._settled = {
|
|
3310
|
+
component,
|
|
3311
|
+
settledAt: Date.now()
|
|
3312
|
+
};
|
|
3313
|
+
return component;
|
|
3314
|
+
});
|
|
3315
|
+
return new Promise((resolve)=>{
|
|
3316
|
+
const timer = setTimeout(()=>{
|
|
3317
|
+
resolve({
|
|
3318
|
+
name: 'persistence',
|
|
3319
|
+
healthy: false,
|
|
3320
|
+
latencyMs: this._probeTimeoutMs,
|
|
3321
|
+
details: `Health check timed out after ${this._probeTimeoutMs}ms`
|
|
3322
|
+
});
|
|
3323
|
+
}, this._probeTimeoutMs);
|
|
3324
|
+
timer.unref();
|
|
3325
|
+
void settled.then((component)=>{
|
|
3326
|
+
clearTimeout(timer);
|
|
3327
|
+
resolve(component);
|
|
3328
|
+
});
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
_checkBacklog() {
|
|
3332
|
+
const view = this._backlog?.getPersistenceBacklogView() ?? null;
|
|
3333
|
+
if (view === null) {
|
|
3334
|
+
return null;
|
|
3335
|
+
}
|
|
3336
|
+
return {
|
|
3337
|
+
name: 'persistenceBacklog',
|
|
3338
|
+
healthy: !view.globalQuarantined,
|
|
3339
|
+
details: describeBacklog(view)
|
|
3340
|
+
};
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Probe the persistence backend with latency measurement. Never rejects: every failure,
|
|
3344
|
+
* including a rejection that arrives after the deadline, becomes an unhealthy component.
|
|
3345
|
+
*/ async _probeBackend() {
|
|
3346
|
+
const start = Date.now();
|
|
3347
|
+
try {
|
|
3348
|
+
const healthy = await this._persistence.healthy();
|
|
3349
|
+
return {
|
|
3350
|
+
name: 'persistence',
|
|
3351
|
+
healthy,
|
|
3352
|
+
latencyMs: Date.now() - start,
|
|
3353
|
+
details: healthy ? 'Backend is responsive' : 'Backend reported unhealthy'
|
|
3354
|
+
};
|
|
3355
|
+
} catch (error) {
|
|
3356
|
+
this._logger.error('Persistence health check failed', {
|
|
3357
|
+
error: (0,errors/* .getErrorMessage */.u1)(error)
|
|
3358
|
+
});
|
|
3359
|
+
return {
|
|
3360
|
+
name: 'persistence',
|
|
3361
|
+
healthy: false,
|
|
3362
|
+
latencyMs: Date.now() - start,
|
|
3363
|
+
details: `Health check error: ${(0,errors/* .getErrorMessage */.u1)(error)}`
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3162
3369
|
// EXTERNAL MODULE: ./src/schema.ts
|
|
3163
3370
|
var schema = __webpack_require__("R");
|
|
3164
3371
|
;// CONCATENATED MODULE: ./src/cli.ts
|
|
@@ -3178,12 +3385,15 @@ var schema = __webpack_require__("R");
|
|
|
3178
3385
|
|
|
3179
3386
|
|
|
3180
3387
|
|
|
3388
|
+
|
|
3181
3389
|
// Get version from package.json
|
|
3182
3390
|
const CLI_NAME = 'tracelattice';
|
|
3183
3391
|
const cli_filename = (0,__rspack_external_node_url_3991086a.fileURLToPath)(import.meta.url);
|
|
3184
3392
|
const cli_dirname = (0,__rspack_external_node_path_806ed179.dirname)(cli_filename);
|
|
3185
3393
|
const package_json = JSON.parse((0,__rspack_external_node_fs_1b05aee1.readFileSync)((0,__rspack_external_node_path_806ed179.join)(cli_dirname, '../package.json'), 'utf-8'));
|
|
3186
3394
|
const { version } = package_json;
|
|
3395
|
+
// Orchestrators poll /ready often; one settled probe serves every poll inside this window.
|
|
3396
|
+
const READINESS_PROBE_FRESHNESS_MS = 1000;
|
|
3187
3397
|
// Handle CLI arguments
|
|
3188
3398
|
const args = process.argv.slice(2);
|
|
3189
3399
|
const shouldShowVersion = args.includes('--version') || args.includes('-v');
|
|
@@ -3233,14 +3443,20 @@ async function main() {
|
|
|
3233
3443
|
const { StreamableHttpTransport } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "q"));
|
|
3234
3444
|
const { lifecycle, config } = options;
|
|
3235
3445
|
const { port, host } = config;
|
|
3236
|
-
const
|
|
3446
|
+
const container = thinkingServer.getContainer();
|
|
3447
|
+
const healthChecker = new HealthChecker({
|
|
3448
|
+
persistence: container.resolve('Persistence'),
|
|
3449
|
+
persistenceBacklog: container.resolve('HistoryManager'),
|
|
3450
|
+
probeFreshnessMs: READINESS_PROBE_FRESHNESS_MS
|
|
3451
|
+
});
|
|
3237
3452
|
const streamableTransport = new StreamableHttpTransport({
|
|
3238
3453
|
port,
|
|
3239
3454
|
host,
|
|
3240
3455
|
corsOrigin: config.corsOrigin,
|
|
3241
3456
|
enableCors: config.enableCors,
|
|
3242
3457
|
allowedHosts: config.allowedHosts,
|
|
3243
|
-
metrics:
|
|
3458
|
+
metrics: container.resolve('Metrics'),
|
|
3459
|
+
healthChecker,
|
|
3244
3460
|
stateful: config.stateful,
|
|
3245
3461
|
...config.retention
|
|
3246
3462
|
});
|