@iworkforces/tracelattice 2.3.1 → 2.3.6
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 +13 -0
- package/dist/cli.js +94 -15
- package/dist/cli.js.map +1 -1
- package/dist/config/CliTransportConfig.d.ts +22 -0
- package/dist/config/CliTransportConfig.d.ts.map +1 -0
- package/dist/config/CliTransportConfig.js +52 -0
- package/dist/config/CliTransportConfig.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +0 -1
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +9 -14
- package/dist/config/ConfigLoader.js.map +1 -1
- package/dist/config/EnvironmentInteger.d.ts +2 -0
- package/dist/config/EnvironmentInteger.d.ts.map +1 -0
- package/dist/config/EnvironmentInteger.js +10 -0
- package/dist/config/EnvironmentInteger.js.map +1 -0
- package/dist/lib.d.ts +12 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +9 -4
- package/dist/lib.js.map +1 -1
- package/dist/persistence/BunSqliteDriver.d.ts +11 -0
- package/dist/persistence/BunSqliteDriver.d.ts.map +1 -0
- package/dist/persistence/BunSqliteDriver.js +36 -0
- package/dist/persistence/BunSqliteDriver.js.map +1 -0
- package/dist/persistence/SqliteDriverLoader.d.ts +11 -0
- package/dist/persistence/SqliteDriverLoader.d.ts.map +1 -0
- package/dist/persistence/SqliteDriverLoader.js +19 -0
- package/dist/persistence/SqliteDriverLoader.js.map +1 -0
- package/dist/persistence/SqlitePersistence.d.ts +2 -1
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +4 -9
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -186,6 +186,9 @@ All feature flags default to enabled in `ServerConfig`. Set a boolean flag to `f
|
|
|
186
186
|
| `TRACELATTICE_STREAMABLE_HTTP_PORT` | `9007` | Port for Streamable HTTP server |
|
|
187
187
|
| `TRACELATTICE_STREAMABLE_HTTP_HOST` | `localhost` | Host for Streamable HTTP server |
|
|
188
188
|
| `TRACELATTICE_STREAMABLE_HTTP_STATEFUL` | `true` | Enable stateful session tracking |
|
|
189
|
+
| `TRACELATTICE_STREAMABLE_HTTP_MAX_SESSIONS` | unset | Maximum retained transport sessions |
|
|
190
|
+
| `TRACELATTICE_STREAMABLE_HTTP_SESSION_IDLE_TIMEOUT_MS` | unset | Transport-session idle timeout in milliseconds |
|
|
191
|
+
| `TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS` | unset | Idle-session sweep interval in milliseconds |
|
|
189
192
|
| `TRACELATTICE_CORS_ORIGIN` | `*` | CORS origin |
|
|
190
193
|
| `TRACELATTICE_ENABLE_CORS` | `true` | Enable CORS preflight |
|
|
191
194
|
| `TRACELATTICE_ALLOWED_HOSTS` | derived from bound host | Comma-separated allowed `Host` header values |
|
|
@@ -223,6 +226,10 @@ Set `TRACELATTICE_TRANSPORT_TYPE` to pick one:
|
|
|
223
226
|
|
|
224
227
|
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`.
|
|
225
228
|
|
|
229
|
+
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
|
+
|
|
231
|
+
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.
|
|
232
|
+
|
|
226
233
|
### Session model
|
|
227
234
|
|
|
228
235
|
TraceLattice uses separate session concepts with separate identifiers:
|
|
@@ -289,6 +296,12 @@ The checked evaluation report separates deterministic structural validation and
|
|
|
289
296
|
|
|
290
297
|
### Persistence, recovery, and backtracking
|
|
291
298
|
|
|
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.
|
|
300
|
+
|
|
301
|
+
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
|
+
|
|
303
|
+
SQLite selects its driver by runtime. Bun, including the packed `tracelattice` CLI, uses the built-in `bun:sqlite` module. Node uses the optional `better-sqlite3` package, which you must install yourself (`npm install better-sqlite3`). Neither runtime falls back to the other driver. A missing or failing driver stops startup with a `PersistenceCompatibilityError` naming the required driver.
|
|
304
|
+
|
|
292
305
|
`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.
|
|
293
306
|
|
|
294
307
|
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.
|
package/dist/cli.js
CHANGED
|
@@ -777,9 +777,11 @@ class PersistenceClosedError extends SequentialThinkingError {
|
|
|
777
777
|
}
|
|
778
778
|
|
|
779
779
|
__webpack_require__.d(__webpack_exports__, {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
780
|
+
}, {
|
|
781
|
+
Qt: CliShutdownTimeoutError,
|
|
782
|
+
j1: ConfigurationError,
|
|
783
|
+
u1: getErrorMessage,
|
|
784
|
+
yI: ValidationError
|
|
783
785
|
});
|
|
784
786
|
|
|
785
787
|
|
|
@@ -2825,6 +2827,79 @@ function createCliShutdownHandler(lifecycle, boundary) {
|
|
|
2825
2827
|
};
|
|
2826
2828
|
}
|
|
2827
2829
|
|
|
2830
|
+
;// CONCATENATED MODULE: ./src/config/EnvironmentInteger.ts
|
|
2831
|
+
|
|
2832
|
+
function parseEnvironmentInteger(environmentName, raw) {
|
|
2833
|
+
if (!/^(0|[1-9]\d*)$/.test(raw)) {
|
|
2834
|
+
throw new errors/* .ConfigurationError */.j1(`${environmentName} must be an integer, got ${raw}`);
|
|
2835
|
+
}
|
|
2836
|
+
const parsed = Number(raw);
|
|
2837
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
2838
|
+
throw new errors/* .ConfigurationError */.j1(`${environmentName} must be a safe integer, got ${raw}`);
|
|
2839
|
+
}
|
|
2840
|
+
return parsed;
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
;// CONCATENATED MODULE: ./src/config/CliTransportConfig.ts
|
|
2844
|
+
|
|
2845
|
+
|
|
2846
|
+
const DEFAULT_STREAMABLE_HTTP_PORT = 9007;
|
|
2847
|
+
// Node and Bun clamp larger setInterval delays to 1 ms.
|
|
2848
|
+
const MAX_SESSION_SWEEP_INTERVAL_MS = 2147483647;
|
|
2849
|
+
const retentionNames = [
|
|
2850
|
+
'TRACELATTICE_STREAMABLE_HTTP_MAX_SESSIONS',
|
|
2851
|
+
'TRACELATTICE_STREAMABLE_HTTP_SESSION_IDLE_TIMEOUT_MS',
|
|
2852
|
+
'TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS'
|
|
2853
|
+
];
|
|
2854
|
+
function positiveRetentionInteger(env, name) {
|
|
2855
|
+
const raw = env[name];
|
|
2856
|
+
if (raw === undefined) throw new errors/* .ConfigurationError */.j1(`Missing ${name}`);
|
|
2857
|
+
const value = parseEnvironmentInteger(name, raw);
|
|
2858
|
+
if (value === 0) throw new errors/* .ConfigurationError */.j1(`${name} must be greater than zero`);
|
|
2859
|
+
return value;
|
|
2860
|
+
}
|
|
2861
|
+
function parseRetention(env, stateful) {
|
|
2862
|
+
const missing = retentionNames.filter((name)=>env[name] === undefined);
|
|
2863
|
+
if (missing.length === retentionNames.length) return undefined;
|
|
2864
|
+
if (missing.length > 0) {
|
|
2865
|
+
throw new errors/* .ConfigurationError */.j1(`Missing Streamable HTTP retention variables: ${missing.join(', ')}`);
|
|
2866
|
+
}
|
|
2867
|
+
if (!stateful) {
|
|
2868
|
+
throw new errors/* .ConfigurationError */.j1('Streamable HTTP retention requires TRACELATTICE_STREAMABLE_HTTP_STATEFUL to not be false');
|
|
2869
|
+
}
|
|
2870
|
+
const retention = {
|
|
2871
|
+
maxSessions: positiveRetentionInteger(env, retentionNames[0]),
|
|
2872
|
+
sessionIdleTimeoutMs: positiveRetentionInteger(env, retentionNames[1]),
|
|
2873
|
+
sessionSweepIntervalMs: positiveRetentionInteger(env, retentionNames[2])
|
|
2874
|
+
};
|
|
2875
|
+
if (retention.sessionSweepIntervalMs > MAX_SESSION_SWEEP_INTERVAL_MS) {
|
|
2876
|
+
throw new errors/* .ConfigurationError */.j1(`${retentionNames[2]} must be at most ${MAX_SESSION_SWEEP_INTERVAL_MS}`);
|
|
2877
|
+
}
|
|
2878
|
+
return retention;
|
|
2879
|
+
}
|
|
2880
|
+
function parseCliTransportConfig(env) {
|
|
2881
|
+
const kind = env.TRACELATTICE_TRANSPORT_TYPE ?? 'stdio';
|
|
2882
|
+
if (kind === 'stdio') return {
|
|
2883
|
+
kind
|
|
2884
|
+
};
|
|
2885
|
+
if (kind !== 'streamable-http') {
|
|
2886
|
+
throw new errors/* .ConfigurationError */.j1(`TRACELATTICE_TRANSPORT_TYPE must be one of stdio, streamable-http, got ${kind}`);
|
|
2887
|
+
}
|
|
2888
|
+
const port = parseEnvironmentInteger('TRACELATTICE_STREAMABLE_HTTP_PORT', env.TRACELATTICE_STREAMABLE_HTTP_PORT ?? String(DEFAULT_STREAMABLE_HTTP_PORT));
|
|
2889
|
+
if (port > 65535) throw new errors/* .ConfigurationError */.j1('TRACELATTICE_STREAMABLE_HTTP_PORT must be between 0 and 65535');
|
|
2890
|
+
const stateful = env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';
|
|
2891
|
+
return {
|
|
2892
|
+
kind,
|
|
2893
|
+
port,
|
|
2894
|
+
host: env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost',
|
|
2895
|
+
corsOrigin: env.TRACELATTICE_CORS_ORIGIN || '*',
|
|
2896
|
+
enableCors: env.TRACELATTICE_ENABLE_CORS !== 'false',
|
|
2897
|
+
allowedHosts: env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue)=>hostValue.trim()),
|
|
2898
|
+
stateful,
|
|
2899
|
+
retention: parseRetention(env, stateful)
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2828
2903
|
;// CONCATENATED MODULE: external "./lib.js"
|
|
2829
2904
|
|
|
2830
2905
|
// EXTERNAL MODULE: ./src/context/RequestContext.ts + 1 modules
|
|
@@ -3102,6 +3177,7 @@ var schema = __webpack_require__("R");
|
|
|
3102
3177
|
|
|
3103
3178
|
|
|
3104
3179
|
|
|
3180
|
+
|
|
3105
3181
|
// Get version from package.json
|
|
3106
3182
|
const CLI_NAME = 'tracelattice';
|
|
3107
3183
|
const cli_filename = (0,__rspack_external_node_url_3991086a.fileURLToPath)(import.meta.url);
|
|
@@ -3116,7 +3192,6 @@ if (shouldShowVersion) {
|
|
|
3116
3192
|
process.exit(0);
|
|
3117
3193
|
}
|
|
3118
3194
|
async function main() {
|
|
3119
|
-
const transportType = process.env.TRACELATTICE_TRANSPORT_TYPE || 'stdio';
|
|
3120
3195
|
const adapter = new __rspack_external__tmcp_adapter_valibot_0faddfa6.ValibotJsonSchemaAdapter();
|
|
3121
3196
|
const server = new __rspack_external_tmcp.McpServer({
|
|
3122
3197
|
name: CLI_NAME,
|
|
@@ -3133,37 +3208,41 @@ async function main() {
|
|
|
3133
3208
|
const thinkingServer = await (0,__rspack_external__lib_js_1645bc55.initializeServer)();
|
|
3134
3209
|
const lifecycle = new CliLifecycle(thinkingServer);
|
|
3135
3210
|
try {
|
|
3211
|
+
const transportConfig = parseCliTransportConfig(process.env);
|
|
3136
3212
|
server.tool({
|
|
3137
3213
|
name: 'sequentialthinking_tools',
|
|
3138
3214
|
description: schema/* .SEQUENTIAL_THINKING_TOOL.description */.iV.description,
|
|
3139
3215
|
schema: schema/* .SequentialThinkingSchema */.ZK
|
|
3140
3216
|
}, async (input)=>thinkingServer.processThought(input));
|
|
3141
|
-
if (
|
|
3142
|
-
await startStreamableHttpTransport(server, thinkingServer,
|
|
3217
|
+
if (transportConfig.kind === 'streamable-http') {
|
|
3218
|
+
await startStreamableHttpTransport(server, thinkingServer, {
|
|
3219
|
+
lifecycle,
|
|
3220
|
+
config: transportConfig
|
|
3221
|
+
});
|
|
3143
3222
|
} else {
|
|
3144
3223
|
await startStdioTransport(server, thinkingServer, lifecycle);
|
|
3145
3224
|
}
|
|
3146
|
-
registerShutdownHandlers(lifecycle, thinkingServer,
|
|
3225
|
+
registerShutdownHandlers(lifecycle, thinkingServer, transportConfig.kind === 'stdio');
|
|
3147
3226
|
} catch (error) {
|
|
3148
3227
|
await lifecycle.rollbackStartup(error);
|
|
3149
3228
|
}
|
|
3150
3229
|
}
|
|
3151
3230
|
/**
|
|
3152
3231
|
* Start Streamable HTTP transport (MCP spec recommended)
|
|
3153
|
-
*/ async function startStreamableHttpTransport(server, thinkingServer,
|
|
3232
|
+
*/ async function startStreamableHttpTransport(server, thinkingServer, options) {
|
|
3154
3233
|
const { StreamableHttpTransport } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "q"));
|
|
3155
|
-
const
|
|
3156
|
-
const host =
|
|
3234
|
+
const { lifecycle, config } = options;
|
|
3235
|
+
const { port, host } = config;
|
|
3157
3236
|
const transportMetrics = thinkingServer.getContainer().resolve('Metrics');
|
|
3158
|
-
const stateful = process.env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';
|
|
3159
3237
|
const streamableTransport = new StreamableHttpTransport({
|
|
3160
3238
|
port,
|
|
3161
3239
|
host,
|
|
3162
|
-
corsOrigin:
|
|
3163
|
-
enableCors:
|
|
3164
|
-
allowedHosts:
|
|
3240
|
+
corsOrigin: config.corsOrigin,
|
|
3241
|
+
enableCors: config.enableCors,
|
|
3242
|
+
allowedHosts: config.allowedHosts,
|
|
3165
3243
|
metrics: transportMetrics,
|
|
3166
|
-
stateful
|
|
3244
|
+
stateful: config.stateful,
|
|
3245
|
+
...config.retention
|
|
3167
3246
|
});
|
|
3168
3247
|
lifecycle.attachTransport(streamableTransport);
|
|
3169
3248
|
// Connect the Streamable HTTP transport
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env bun\n\n// CLI entry point for tracelattice MCP server.\n// This file handles CLI argument parsing, transport selection, and signal handlers.\n// For library usage, import from './lib.js' or './index.js' instead.\n\nimport { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';\nimport { StdioTransport } from '@tmcp/transport-stdio';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer } from 'tmcp';\nimport { CliLifecycle, createCliShutdownHandler } from './CliLifecycle.js';\nimport type { ToolAwareSequentialThinkingServer } from './lib.js';\nimport { initializeServer } from './lib.js';\nimport { StructuredLogger } from './logger/StructuredLogger.js';\nimport { getErrorMessage } from './errors.js';\nimport { SEQUENTIAL_THINKING_TOOL, SequentialThinkingSchema } from './schema.js';\n\n// Get version from package.json\nconst CLI_NAME = 'tracelattice' as const;\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst package_json = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\nconst { version } = package_json;\n// Handle CLI arguments\nconst args = process.argv.slice(2);\nconst shouldShowVersion = args.includes('--version') || args.includes('-v');\n\nif (shouldShowVersion) {\n\tconsole.log(`${CLI_NAME} v${version}`);\n\tprocess.exit(0);\n}\nasync function main() {\n\tconst transportType = process.env.TRACELATTICE_TRANSPORT_TYPE || 'stdio';\n\tconst adapter = new ValibotJsonSchemaAdapter();\n\tconst server = new McpServer(\n\t\t{\n\t\t\tname: CLI_NAME,\n\t\t\tversion,\n\t\t\tdescription: 'Semantic Sequential Thinking MCP Server',\n\t\t},\n\t\t{\n\t\t\tadapter,\n\t\t\tcapabilities: {\n\t\t\t\ttools: { listChanged: true },\n\t\t\t},\n\t\t}\n\t);\n\n\tconst thinkingServer = await initializeServer();\n\tconst lifecycle = new CliLifecycle(thinkingServer);\n\ttry {\n\t\tserver.tool(\n\t\t\t{\n\t\t\t\tname: 'sequentialthinking_tools',\n\t\t\t\tdescription: SEQUENTIAL_THINKING_TOOL.description,\n\t\t\t\tschema: SequentialThinkingSchema,\n\t\t\t},\n\t\t\tasync (input) => thinkingServer.processThought(input)\n\t\t);\n\t\tif (transportType === 'streamable-http') {\n\t\t\tawait startStreamableHttpTransport(server, thinkingServer, lifecycle);\n\t\t} else {\n\t\t\tawait startStdioTransport(server, thinkingServer, lifecycle);\n\t\t}\n\t\tregisterShutdownHandlers(lifecycle, thinkingServer, transportType === 'stdio');\n\t} catch (error) {\n\t\tawait lifecycle.rollbackStartup(error);\n\t}\n}\n/**\n * Start Streamable HTTP transport (MCP spec recommended)\n */\nasync function startStreamableHttpTransport(\n\tserver: McpServer,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\tlifecycle: CliLifecycle\n): Promise<void> {\n\tconst { StreamableHttpTransport } = await import('./transport/StreamableHttpTransport.js');\n\tconst port = parseInt(process.env.TRACELATTICE_STREAMABLE_HTTP_PORT || '9007', 10);\n\tconst host = process.env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost';\n\tconst transportMetrics = thinkingServer.getContainer().resolve('Metrics');\n\tconst stateful = process.env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';\n\tconst streamableTransport = new StreamableHttpTransport({\n\t\tport,\n\t\thost,\n\t\tcorsOrigin: process.env.TRACELATTICE_CORS_ORIGIN || '*',\n\t\tenableCors: process.env.TRACELATTICE_ENABLE_CORS !== 'false',\n\t\tallowedHosts: process.env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue) =>\n\t\t\thostValue.trim()\n\t\t),\n\t\tmetrics: transportMetrics,\n\t\tstateful,\n\t});\n\tlifecycle.attachTransport(streamableTransport);\n\t// Connect the Streamable HTTP transport\n\tawait streamableTransport.connect(server);\n\tthinkingServer['_logger'].info(\n\t\t`Sequential Thinking MCP Server running on Streamable HTTP transport at http://${host}:${port}`\n\t);\n}\n/**\n * Start stdio transport (default, single-user)\n */\nasync function startStdioTransport(\n\tserver: McpServer,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\tlifecycle: CliLifecycle\n): Promise<void> {\n\tconst transport = new StdioTransport(server);\n\tconst processListeners = {\n\t\tsigint: new Set(process.listeners('SIGINT')),\n\t\tsigterm: new Set(process.listeners('SIGTERM')),\n\t\tstdinEnd: new Set(process.stdin.listeners('end')),\n\t};\n\tlifecycle.attachTransport({ stop: () => transport.close() });\n\ttransport.listen();\n\tfor (const listener of process.listeners('SIGINT')) {\n\t\tif (!processListeners.sigint.has(listener)) process.off('SIGINT', listener);\n\t}\n\tfor (const listener of process.listeners('SIGTERM')) {\n\t\tif (!processListeners.sigterm.has(listener)) process.off('SIGTERM', listener);\n\t}\n\tfor (const listener of process.stdin.listeners('end')) {\n\t\tif (!processListeners.stdinEnd.has(listener)) process.stdin.off('end', listener);\n\t}\n\tthinkingServer['_logger'].info('Sequential Thinking MCP Server running on stdio');\n}\n/**\n * Register shutdown signal handlers for a common pattern\n */\nfunction registerShutdownHandlers(\n\tlifecycle: CliLifecycle,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\tshutdownOnStdinEnd: boolean\n): void {\n\tconst shutdown = createCliShutdownHandler(lifecycle, {\n\t\treportFailure: (error) => {\n\t\t\tthinkingServer['_logger'].error('CLI shutdown failed', failureMetadata(error));\n\t\t},\n\t\texit: (code) => process.exit(code),\n\t});\n\tconst requestShutdown = (): void => {\n\t\tvoid shutdown();\n\t};\n\tprocess.on('SIGINT', requestShutdown);\n\tprocess.on('SIGTERM', requestShutdown);\n\tif (shutdownOnStdinEnd) process.stdin.on('end', requestShutdown);\n}\n\nfunction failureMetadata(error: unknown): Record<string, unknown> {\n\tif (!(error instanceof AggregateError)) return { error: getErrorMessage(error) };\n\treturn {\n\t\terror: error.message,\n\t\tcauses: error.errors.map((cause) => getErrorMessage(cause)),\n\t};\n}\nmain().catch((error) => {\n\tconst logger = new StructuredLogger({\n\t\tlevel: 'error',\n\t\tcontext: 'SequentialThinking',\n\t\tpretty: true,\n\t});\n\tlogger.error('Fatal error running server', failureMetadata(error));\n\tprocess.exit(1);\n});\n"],"names":["CLI_NAME","__filename","fileURLToPath","__dirname","dirname","package_json","JSON","readFileSync","join","version","args","process","shouldShowVersion","console","main","transportType","adapter","ValibotJsonSchemaAdapter","server","McpServer","thinkingServer","initializeServer","lifecycle","CliLifecycle","SEQUENTIAL_THINKING_TOOL","SequentialThinkingSchema","input","startStreamableHttpTransport","startStdioTransport","registerShutdownHandlers","error","StreamableHttpTransport","port","parseInt","host","transportMetrics","stateful","streamableTransport","hostValue","transport","StdioTransport","processListeners","Set","listener","shutdownOnStdinEnd","shutdown","createCliShutdownHandler","failureMetadata","code","requestShutdown","AggregateError","getErrorMessage","cause","logger","StructuredLogger"],"mappings":";;;;;;;;;;;;AAoBA,MAAMA,WAAW;AACjB,MAAMC,eAAaC,cAAc,YAAY,GAAG;AAChD,MAAMC,cAAYC,QAAQH;AAC1B,MAAMI,eAAeC,KAAK,KAAK,CAACC,aAAaC,KAAKL,aAAW,oBAAoB;AACjF,MAAM,EAAEM,SAAAA,OAAO,EAAE,GAAGJ;AAEpB,MAAMK,OAAOC,QAAQ,IAAI,CAAC,KAAK,CAAC;AAChC,MAAMC,oBAAoBF,KAAK,QAAQ,CAAC,gBAAgBA,KAAK,QAAQ,CAAC;AAEtE,IAAIE,mBAAmB;IACtBC,QAAQ,GAAG,CAAC,GAAGb,SAAS,EAAE,EAAES,SAAS;IACrCE,QAAQ,IAAI,CAAC;AACd;AACA,eAAeG;IACd,MAAMC,gBAAgBJ,QAAQ,GAAG,CAAC,2BAA2B,IAAI;IACjE,MAAMK,UAAU,IAAIC;IACpB,MAAMC,SAAS,IAAIC,UAClB;QACC,MAAMnB;QACNS,SAAAA;QACA,aAAa;IACd,GACA;QACCO;QACA,cAAc;YACb,OAAO;gBAAE,aAAa;YAAK;QAC5B;IACD;IAGD,MAAMI,iBAAiB,MAAMC;IAC7B,MAAMC,YAAY,IAAIC,aAAaH;IACnC,IAAI;QACHF,OAAO,IAAI,CACV;YACC,MAAM;YACN,aAAaM,yBAAyB,WAAW;YACjD,QAAQC;QACT,GACA,OAAOC,QAAUN,eAAe,cAAc,CAACM;QAEhD,IAAIX,AAAkB,sBAAlBA,eACH,MAAMY,6BAA6BT,QAAQE,gBAAgBE;aAE3D,MAAMM,oBAAoBV,QAAQE,gBAAgBE;QAEnDO,yBAAyBP,WAAWF,gBAAgBL,AAAkB,YAAlBA;IACrD,EAAE,OAAOe,OAAO;QACf,MAAMR,UAAU,eAAe,CAACQ;IACjC;AACD;AAIA,eAAeH,6BACdT,MAAiB,EACjBE,cAAiD,EACjDE,SAAuB;IAEvB,MAAM,EAAES,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC;IACjD,MAAMC,OAAOC,SAAStB,QAAQ,GAAG,CAAC,iCAAiC,IAAI,QAAQ;IAC/E,MAAMuB,OAAOvB,QAAQ,GAAG,CAAC,iCAAiC,IAAI;IAC9D,MAAMwB,mBAAmBf,eAAe,YAAY,GAAG,OAAO,CAAC;IAC/D,MAAMgB,WAAWzB,AAAsD,YAAtDA,QAAQ,GAAG,CAAC,qCAAqC;IAClE,MAAM0B,sBAAsB,IAAIN,wBAAwB;QACvDC;QACAE;QACA,YAAYvB,QAAQ,GAAG,CAAC,wBAAwB,IAAI;QACpD,YAAYA,AAAyC,YAAzCA,QAAQ,GAAG,CAAC,wBAAwB;QAChD,cAAcA,QAAQ,GAAG,CAAC,0BAA0B,EAAE,MAAM,KAAK,IAAI,CAAC2B,YACrEA,UAAU,IAAI;QAEf,SAASH;QACTC;IACD;IACAd,UAAU,eAAe,CAACe;IAE1B,MAAMA,oBAAoB,OAAO,CAACnB;IAClCE,cAAc,CAAC,UAAU,CAAC,IAAI,CAC7B,CAAC,8EAA8E,EAAEc,KAAK,CAAC,EAAEF,MAAM;AAEjG;AAIA,eAAeJ,oBACdV,MAAiB,EACjBE,cAAiD,EACjDE,SAAuB;IAEvB,MAAMiB,YAAY,IAAIC,eAAetB;IACrC,MAAMuB,mBAAmB;QACxB,QAAQ,IAAIC,IAAI/B,QAAQ,SAAS,CAAC;QAClC,SAAS,IAAI+B,IAAI/B,QAAQ,SAAS,CAAC;QACnC,UAAU,IAAI+B,IAAI/B,QAAQ,KAAK,CAAC,SAAS,CAAC;IAC3C;IACAW,UAAU,eAAe,CAAC;QAAE,MAAM,IAAMiB,UAAU,KAAK;IAAG;IAC1DA,UAAU,MAAM;IAChB,KAAK,MAAMI,YAAYhC,QAAQ,SAAS,CAAC,UACxC,IAAI,CAAC8B,iBAAiB,MAAM,CAAC,GAAG,CAACE,WAAWhC,QAAQ,GAAG,CAAC,UAAUgC;IAEnE,KAAK,MAAMA,YAAYhC,QAAQ,SAAS,CAAC,WACxC,IAAI,CAAC8B,iBAAiB,OAAO,CAAC,GAAG,CAACE,WAAWhC,QAAQ,GAAG,CAAC,WAAWgC;IAErE,KAAK,MAAMA,YAAYhC,QAAQ,KAAK,CAAC,SAAS,CAAC,OAC9C,IAAI,CAAC8B,iBAAiB,QAAQ,CAAC,GAAG,CAACE,WAAWhC,QAAQ,KAAK,CAAC,GAAG,CAAC,OAAOgC;IAExEvB,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC;AAChC;AAIA,SAASS,yBACRP,SAAuB,EACvBF,cAAiD,EACjDwB,kBAA2B;IAE3B,MAAMC,WAAWC,yBAAyBxB,WAAW;QACpD,eAAe,CAACQ;YACfV,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,uBAAuB2B,gBAAgBjB;QACxE;QACA,MAAM,CAACkB,OAASrC,QAAQ,IAAI,CAACqC;IAC9B;IACA,MAAMC,kBAAkB;QAClBJ;IACN;IACAlC,QAAQ,EAAE,CAAC,UAAUsC;IACrBtC,QAAQ,EAAE,CAAC,WAAWsC;IACtB,IAAIL,oBAAoBjC,QAAQ,KAAK,CAAC,EAAE,CAAC,OAAOsC;AACjD;AAEA,SAASF,gBAAgBjB,KAAc;IACtC,IAAI,CAAEA,CAAAA,iBAAiBoB,cAAa,GAAI,OAAO;QAAE,OAAOC,gBAAgBrB;IAAO;IAC/E,OAAO;QACN,OAAOA,MAAM,OAAO;QACpB,QAAQA,MAAM,MAAM,CAAC,GAAG,CAAC,CAACsB,QAAUD,gBAAgBC;IACrD;AACD;AACAtC,OAAO,KAAK,CAAC,CAACgB;IACb,MAAMuB,SAAS,IAAIC,iBAAiB;QACnC,OAAO;QACP,SAAS;QACT,QAAQ;IACT;IACAD,OAAO,KAAK,CAAC,8BAA8BN,gBAAgBjB;IAC3DnB,QAAQ,IAAI,CAAC;AACd"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env bun\n\n// CLI entry point for tracelattice MCP server.\n// This file handles CLI argument parsing, transport selection, and signal handlers.\n// For library usage, import from './lib.js' or './index.js' instead.\n\nimport { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';\nimport { StdioTransport } from '@tmcp/transport-stdio';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { McpServer } from 'tmcp';\nimport { CliLifecycle, createCliShutdownHandler } from './CliLifecycle.js';\nimport { parseCliTransportConfig } from './config/CliTransportConfig.js';\nimport type { CliHttpTransportConfig } from './config/CliTransportConfig.js';\nimport type { ToolAwareSequentialThinkingServer } from './lib.js';\nimport { initializeServer } from './lib.js';\nimport { StructuredLogger } from './logger/StructuredLogger.js';\nimport { getErrorMessage } from './errors.js';\nimport { SEQUENTIAL_THINKING_TOOL, SequentialThinkingSchema } from './schema.js';\n\n// Get version from package.json\nconst CLI_NAME = 'tracelattice' as const;\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst package_json = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\nconst { version } = package_json;\n// Handle CLI arguments\nconst args = process.argv.slice(2);\nconst shouldShowVersion = args.includes('--version') || args.includes('-v');\n\nif (shouldShowVersion) {\n\tconsole.log(`${CLI_NAME} v${version}`);\n\tprocess.exit(0);\n}\nasync function main() {\n\tconst adapter = new ValibotJsonSchemaAdapter();\n\tconst server = new McpServer(\n\t\t{\n\t\t\tname: CLI_NAME,\n\t\t\tversion,\n\t\t\tdescription: 'Semantic Sequential Thinking MCP Server',\n\t\t},\n\t\t{\n\t\t\tadapter,\n\t\t\tcapabilities: {\n\t\t\t\ttools: { listChanged: true },\n\t\t\t},\n\t\t}\n\t);\n\n\tconst thinkingServer = await initializeServer();\n\tconst lifecycle = new CliLifecycle(thinkingServer);\n\ttry {\n\t\tconst transportConfig = parseCliTransportConfig(process.env);\n\t\tserver.tool(\n\t\t\t{\n\t\t\t\tname: 'sequentialthinking_tools',\n\t\t\t\tdescription: SEQUENTIAL_THINKING_TOOL.description,\n\t\t\t\tschema: SequentialThinkingSchema,\n\t\t\t},\n\t\t\tasync (input) => thinkingServer.processThought(input)\n\t\t);\n\t\tif (transportConfig.kind === 'streamable-http') {\n\t\t\tawait startStreamableHttpTransport(server, thinkingServer, {\n\t\t\t\tlifecycle,\n\t\t\t\tconfig: transportConfig,\n\t\t\t});\n\t\t} else {\n\t\t\tawait startStdioTransport(server, thinkingServer, lifecycle);\n\t\t}\n\t\tregisterShutdownHandlers(lifecycle, thinkingServer, transportConfig.kind === 'stdio');\n\t} catch (error) {\n\t\tawait lifecycle.rollbackStartup(error);\n\t}\n}\n/**\n * Start Streamable HTTP transport (MCP spec recommended)\n */\nasync function startStreamableHttpTransport(\n\tserver: McpServer,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\toptions: { readonly lifecycle: CliLifecycle; readonly config: CliHttpTransportConfig }\n): Promise<void> {\n\tconst { StreamableHttpTransport } = await import('./transport/StreamableHttpTransport.js');\n\tconst { lifecycle, config } = options;\n\tconst { port, host } = config;\n\tconst transportMetrics = thinkingServer.getContainer().resolve('Metrics');\n\tconst streamableTransport = new StreamableHttpTransport({\n\t\tport,\n\t\thost,\n\t\tcorsOrigin: config.corsOrigin,\n\t\tenableCors: config.enableCors,\n\t\tallowedHosts: config.allowedHosts,\n\t\tmetrics: transportMetrics,\n\t\tstateful: config.stateful,\n\t\t...config.retention,\n\t});\n\tlifecycle.attachTransport(streamableTransport);\n\t// Connect the Streamable HTTP transport\n\tawait streamableTransport.connect(server);\n\tthinkingServer['_logger'].info(\n\t\t`Sequential Thinking MCP Server running on Streamable HTTP transport at http://${host}:${port}`\n\t);\n}\n/**\n * Start stdio transport (default, single-user)\n */\nasync function startStdioTransport(\n\tserver: McpServer,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\tlifecycle: CliLifecycle\n): Promise<void> {\n\tconst transport = new StdioTransport(server);\n\tconst processListeners = {\n\t\tsigint: new Set(process.listeners('SIGINT')),\n\t\tsigterm: new Set(process.listeners('SIGTERM')),\n\t\tstdinEnd: new Set(process.stdin.listeners('end')),\n\t};\n\tlifecycle.attachTransport({ stop: () => transport.close() });\n\ttransport.listen();\n\tfor (const listener of process.listeners('SIGINT')) {\n\t\tif (!processListeners.sigint.has(listener)) process.off('SIGINT', listener);\n\t}\n\tfor (const listener of process.listeners('SIGTERM')) {\n\t\tif (!processListeners.sigterm.has(listener)) process.off('SIGTERM', listener);\n\t}\n\tfor (const listener of process.stdin.listeners('end')) {\n\t\tif (!processListeners.stdinEnd.has(listener)) process.stdin.off('end', listener);\n\t}\n\tthinkingServer['_logger'].info('Sequential Thinking MCP Server running on stdio');\n}\n/**\n * Register shutdown signal handlers for a common pattern\n */\nfunction registerShutdownHandlers(\n\tlifecycle: CliLifecycle,\n\tthinkingServer: ToolAwareSequentialThinkingServer,\n\tshutdownOnStdinEnd: boolean\n): void {\n\tconst shutdown = createCliShutdownHandler(lifecycle, {\n\t\treportFailure: (error) => {\n\t\t\tthinkingServer['_logger'].error('CLI shutdown failed', failureMetadata(error));\n\t\t},\n\t\texit: (code) => process.exit(code),\n\t});\n\tconst requestShutdown = (): void => {\n\t\tvoid shutdown();\n\t};\n\tprocess.on('SIGINT', requestShutdown);\n\tprocess.on('SIGTERM', requestShutdown);\n\tif (shutdownOnStdinEnd) process.stdin.on('end', requestShutdown);\n}\n\nfunction failureMetadata(error: unknown): Record<string, unknown> {\n\tif (!(error instanceof AggregateError)) return { error: getErrorMessage(error) };\n\treturn {\n\t\terror: error.message,\n\t\tcauses: error.errors.map((cause) => getErrorMessage(cause)),\n\t};\n}\nmain().catch((error) => {\n\tconst logger = new StructuredLogger({\n\t\tlevel: 'error',\n\t\tcontext: 'SequentialThinking',\n\t\tpretty: true,\n\t});\n\tlogger.error('Fatal error running server', failureMetadata(error));\n\tprocess.exit(1);\n});\n"],"names":["CLI_NAME","__filename","fileURLToPath","__dirname","dirname","package_json","JSON","readFileSync","join","version","args","process","shouldShowVersion","console","main","adapter","ValibotJsonSchemaAdapter","server","McpServer","thinkingServer","initializeServer","lifecycle","CliLifecycle","transportConfig","parseCliTransportConfig","SEQUENTIAL_THINKING_TOOL","SequentialThinkingSchema","input","startStreamableHttpTransport","startStdioTransport","registerShutdownHandlers","error","options","StreamableHttpTransport","config","port","host","transportMetrics","streamableTransport","transport","StdioTransport","processListeners","Set","listener","shutdownOnStdinEnd","shutdown","createCliShutdownHandler","failureMetadata","code","requestShutdown","AggregateError","getErrorMessage","cause","logger","StructuredLogger"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const DEFAULT_STREAMABLE_HTTP_PORT = 9007;
|
|
2
|
+
export declare const MAX_SESSION_SWEEP_INTERVAL_MS = 2147483647;
|
|
3
|
+
export type CliSessionRetention = {
|
|
4
|
+
readonly maxSessions: number;
|
|
5
|
+
readonly sessionIdleTimeoutMs: number;
|
|
6
|
+
readonly sessionSweepIntervalMs: number;
|
|
7
|
+
};
|
|
8
|
+
export type CliHttpTransportConfig = {
|
|
9
|
+
readonly kind: 'streamable-http';
|
|
10
|
+
readonly port: number;
|
|
11
|
+
readonly host: string;
|
|
12
|
+
readonly corsOrigin: string;
|
|
13
|
+
readonly enableCors: boolean;
|
|
14
|
+
readonly allowedHosts: string[] | undefined;
|
|
15
|
+
readonly stateful: boolean;
|
|
16
|
+
readonly retention: CliSessionRetention | undefined;
|
|
17
|
+
};
|
|
18
|
+
export type CliTransportConfig = {
|
|
19
|
+
readonly kind: 'stdio';
|
|
20
|
+
} | CliHttpTransportConfig;
|
|
21
|
+
export declare function parseCliTransportConfig(env: NodeJS.ProcessEnv): CliTransportConfig;
|
|
22
|
+
//# sourceMappingURL=CliTransportConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CliTransportConfig.d.ts","sourceRoot":"","sources":["../../src/config/CliTransportConfig.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,eAAO,MAAM,6BAA6B,aAAgB,CAAC;AAE3D,MAAM,MAAM,mBAAmB,GAAG;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,sBAAsB,CAAC;AA6CrF,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,kBAAkB,CAyBlF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ConfigurationError } from "../errors.js";
|
|
2
|
+
import { parseEnvironmentInteger } from "./EnvironmentInteger.js";
|
|
3
|
+
const DEFAULT_STREAMABLE_HTTP_PORT = 9007;
|
|
4
|
+
const MAX_SESSION_SWEEP_INTERVAL_MS = 2147483647;
|
|
5
|
+
const retentionNames = [
|
|
6
|
+
'TRACELATTICE_STREAMABLE_HTTP_MAX_SESSIONS',
|
|
7
|
+
'TRACELATTICE_STREAMABLE_HTTP_SESSION_IDLE_TIMEOUT_MS',
|
|
8
|
+
'TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS'
|
|
9
|
+
];
|
|
10
|
+
function positiveRetentionInteger(env, name) {
|
|
11
|
+
const raw = env[name];
|
|
12
|
+
if (void 0 === raw) throw new ConfigurationError(`Missing ${name}`);
|
|
13
|
+
const value = parseEnvironmentInteger(name, raw);
|
|
14
|
+
if (0 === value) throw new ConfigurationError(`${name} must be greater than zero`);
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function parseRetention(env, stateful) {
|
|
18
|
+
const missing = retentionNames.filter((name)=>void 0 === env[name]);
|
|
19
|
+
if (missing.length === retentionNames.length) return;
|
|
20
|
+
if (missing.length > 0) throw new ConfigurationError(`Missing Streamable HTTP retention variables: ${missing.join(', ')}`);
|
|
21
|
+
if (!stateful) throw new ConfigurationError('Streamable HTTP retention requires TRACELATTICE_STREAMABLE_HTTP_STATEFUL to not be false');
|
|
22
|
+
const retention = {
|
|
23
|
+
maxSessions: positiveRetentionInteger(env, retentionNames[0]),
|
|
24
|
+
sessionIdleTimeoutMs: positiveRetentionInteger(env, retentionNames[1]),
|
|
25
|
+
sessionSweepIntervalMs: positiveRetentionInteger(env, retentionNames[2])
|
|
26
|
+
};
|
|
27
|
+
if (retention.sessionSweepIntervalMs > MAX_SESSION_SWEEP_INTERVAL_MS) throw new ConfigurationError(`${retentionNames[2]} must be at most ${MAX_SESSION_SWEEP_INTERVAL_MS}`);
|
|
28
|
+
return retention;
|
|
29
|
+
}
|
|
30
|
+
function parseCliTransportConfig(env) {
|
|
31
|
+
const kind = env.TRACELATTICE_TRANSPORT_TYPE ?? 'stdio';
|
|
32
|
+
if ('stdio' === kind) return {
|
|
33
|
+
kind
|
|
34
|
+
};
|
|
35
|
+
if ('streamable-http' !== kind) throw new ConfigurationError(`TRACELATTICE_TRANSPORT_TYPE must be one of stdio, streamable-http, got ${kind}`);
|
|
36
|
+
const port = parseEnvironmentInteger('TRACELATTICE_STREAMABLE_HTTP_PORT', env.TRACELATTICE_STREAMABLE_HTTP_PORT ?? String(DEFAULT_STREAMABLE_HTTP_PORT));
|
|
37
|
+
if (port > 65535) throw new ConfigurationError('TRACELATTICE_STREAMABLE_HTTP_PORT must be between 0 and 65535');
|
|
38
|
+
const stateful = 'false' !== env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL;
|
|
39
|
+
return {
|
|
40
|
+
kind,
|
|
41
|
+
port,
|
|
42
|
+
host: env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost',
|
|
43
|
+
corsOrigin: env.TRACELATTICE_CORS_ORIGIN || '*',
|
|
44
|
+
enableCors: 'false' !== env.TRACELATTICE_ENABLE_CORS,
|
|
45
|
+
allowedHosts: env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue)=>hostValue.trim()),
|
|
46
|
+
stateful,
|
|
47
|
+
retention: parseRetention(env, stateful)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export { DEFAULT_STREAMABLE_HTTP_PORT, MAX_SESSION_SWEEP_INTERVAL_MS, parseCliTransportConfig };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=CliTransportConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config/CliTransportConfig.js","sources":["../../src/config/CliTransportConfig.ts"],"sourcesContent":["import { ConfigurationError } from '../errors.js';\nimport { parseEnvironmentInteger } from './EnvironmentInteger.js';\n\nexport const DEFAULT_STREAMABLE_HTTP_PORT = 9007;\n// Node and Bun clamp larger setInterval delays to 1 ms.\nexport const MAX_SESSION_SWEEP_INTERVAL_MS = 2_147_483_647;\n\nexport type CliSessionRetention = {\n\treadonly maxSessions: number;\n\treadonly sessionIdleTimeoutMs: number;\n\treadonly sessionSweepIntervalMs: number;\n};\n\nexport type CliHttpTransportConfig = {\n\treadonly kind: 'streamable-http';\n\treadonly port: number;\n\treadonly host: string;\n\treadonly corsOrigin: string;\n\treadonly enableCors: boolean;\n\treadonly allowedHosts: string[] | undefined;\n\treadonly stateful: boolean;\n\treadonly retention: CliSessionRetention | undefined;\n};\n\nexport type CliTransportConfig = { readonly kind: 'stdio' } | CliHttpTransportConfig;\n\nconst retentionNames = [\n\t'TRACELATTICE_STREAMABLE_HTTP_MAX_SESSIONS',\n\t'TRACELATTICE_STREAMABLE_HTTP_SESSION_IDLE_TIMEOUT_MS',\n\t'TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS',\n] as const;\n\nfunction positiveRetentionInteger(env: NodeJS.ProcessEnv, name: string): number {\n\tconst raw = env[name];\n\tif (raw === undefined) throw new ConfigurationError(`Missing ${name}`);\n\tconst value = parseEnvironmentInteger(name, raw);\n\tif (value === 0) throw new ConfigurationError(`${name} must be greater than zero`);\n\treturn value;\n}\n\nfunction parseRetention(\n\tenv: NodeJS.ProcessEnv,\n\tstateful: boolean\n): CliSessionRetention | undefined {\n\tconst missing = retentionNames.filter((name) => env[name] === undefined);\n\tif (missing.length === retentionNames.length) return undefined;\n\tif (missing.length > 0) {\n\t\tthrow new ConfigurationError(\n\t\t\t`Missing Streamable HTTP retention variables: ${missing.join(', ')}`\n\t\t);\n\t}\n\tif (!stateful) {\n\t\tthrow new ConfigurationError(\n\t\t\t'Streamable HTTP retention requires TRACELATTICE_STREAMABLE_HTTP_STATEFUL to not be false'\n\t\t);\n\t}\n\tconst retention = {\n\t\tmaxSessions: positiveRetentionInteger(env, retentionNames[0]),\n\t\tsessionIdleTimeoutMs: positiveRetentionInteger(env, retentionNames[1]),\n\t\tsessionSweepIntervalMs: positiveRetentionInteger(env, retentionNames[2]),\n\t};\n\tif (retention.sessionSweepIntervalMs > MAX_SESSION_SWEEP_INTERVAL_MS) {\n\t\tthrow new ConfigurationError(\n\t\t\t`${retentionNames[2]} must be at most ${MAX_SESSION_SWEEP_INTERVAL_MS}`\n\t\t);\n\t}\n\treturn retention;\n}\n\nexport function parseCliTransportConfig(env: NodeJS.ProcessEnv): CliTransportConfig {\n\tconst kind = env.TRACELATTICE_TRANSPORT_TYPE ?? 'stdio';\n\tif (kind === 'stdio') return { kind };\n\tif (kind !== 'streamable-http') {\n\t\tthrow new ConfigurationError(\n\t\t\t`TRACELATTICE_TRANSPORT_TYPE must be one of stdio, streamable-http, got ${kind}`\n\t\t);\n\t}\n\tconst port = parseEnvironmentInteger(\n\t\t'TRACELATTICE_STREAMABLE_HTTP_PORT',\n\t\tenv.TRACELATTICE_STREAMABLE_HTTP_PORT ?? String(DEFAULT_STREAMABLE_HTTP_PORT)\n\t);\n\tif (port > 65535)\n\t\tthrow new ConfigurationError('TRACELATTICE_STREAMABLE_HTTP_PORT must be between 0 and 65535');\n\tconst stateful = env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';\n\treturn {\n\t\tkind,\n\t\tport,\n\t\thost: env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost',\n\t\tcorsOrigin: env.TRACELATTICE_CORS_ORIGIN || '*',\n\t\tenableCors: env.TRACELATTICE_ENABLE_CORS !== 'false',\n\t\tallowedHosts: env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue) => hostValue.trim()),\n\t\tstateful,\n\t\tretention: parseRetention(env, stateful),\n\t};\n}\n"],"names":["DEFAULT_STREAMABLE_HTTP_PORT","MAX_SESSION_SWEEP_INTERVAL_MS","retentionNames","positiveRetentionInteger","env","name","raw","undefined","ConfigurationError","value","parseEnvironmentInteger","parseRetention","stateful","missing","retention","parseCliTransportConfig","kind","port","String","hostValue"],"mappings":";;AAGO,MAAMA,+BAA+B;AAErC,MAAMC,gCAAgC;AAqB7C,MAAMC,iBAAiB;IACtB;IACA;IACA;CACA;AAED,SAASC,yBAAyBC,GAAsB,EAAEC,IAAY;IACrE,MAAMC,MAAMF,GAAG,CAACC,KAAK;IACrB,IAAIC,AAAQC,WAARD,KAAmB,MAAM,IAAIE,mBAAmB,CAAC,QAAQ,EAAEH,MAAM;IACrE,MAAMI,QAAQC,wBAAwBL,MAAMC;IAC5C,IAAIG,AAAU,MAAVA,OAAa,MAAM,IAAID,mBAAmB,GAAGH,KAAK,0BAA0B,CAAC;IACjF,OAAOI;AACR;AAEA,SAASE,eACRP,GAAsB,EACtBQ,QAAiB;IAEjB,MAAMC,UAAUX,eAAe,MAAM,CAAC,CAACG,OAASD,AAAcG,WAAdH,GAAG,CAACC,KAAK;IACzD,IAAIQ,QAAQ,MAAM,KAAKX,eAAe,MAAM,EAAE;IAC9C,IAAIW,QAAQ,MAAM,GAAG,GACpB,MAAM,IAAIL,mBACT,CAAC,6CAA6C,EAAEK,QAAQ,IAAI,CAAC,OAAO;IAGtE,IAAI,CAACD,UACJ,MAAM,IAAIJ,mBACT;IAGF,MAAMM,YAAY;QACjB,aAAaX,yBAAyBC,KAAKF,cAAc,CAAC,EAAE;QAC5D,sBAAsBC,yBAAyBC,KAAKF,cAAc,CAAC,EAAE;QACrE,wBAAwBC,yBAAyBC,KAAKF,cAAc,CAAC,EAAE;IACxE;IACA,IAAIY,UAAU,sBAAsB,GAAGb,+BACtC,MAAM,IAAIO,mBACT,GAAGN,cAAc,CAAC,EAAE,CAAC,iBAAiB,EAAED,+BAA+B;IAGzE,OAAOa;AACR;AAEO,SAASC,wBAAwBX,GAAsB;IAC7D,MAAMY,OAAOZ,IAAI,2BAA2B,IAAI;IAChD,IAAIY,AAAS,YAATA,MAAkB,OAAO;QAAEA;IAAK;IACpC,IAAIA,AAAS,sBAATA,MACH,MAAM,IAAIR,mBACT,CAAC,uEAAuE,EAAEQ,MAAM;IAGlF,MAAMC,OAAOP,wBACZ,qCACAN,IAAI,iCAAiC,IAAIc,OAAOlB;IAEjD,IAAIiB,OAAO,OACV,MAAM,IAAIT,mBAAmB;IAC9B,MAAMI,WAAWR,AAA8C,YAA9CA,IAAI,qCAAqC;IAC1D,OAAO;QACNY;QACAC;QACA,MAAMb,IAAI,iCAAiC,IAAI;QAC/C,YAAYA,IAAI,wBAAwB,IAAI;QAC5C,YAAYA,AAAiC,YAAjCA,IAAI,wBAAwB;QACxC,cAAcA,IAAI,0BAA0B,EAAE,MAAM,KAAK,IAAI,CAACe,YAAcA,UAAU,IAAI;QAC1FP;QACA,WAAWD,eAAeP,KAAKQ;IAChC;AACD"}
|
|
@@ -243,7 +243,6 @@ export declare class ConfigLoader {
|
|
|
243
243
|
* @private
|
|
244
244
|
*/
|
|
245
245
|
private applyFeatureFlagOverrides;
|
|
246
|
-
private parseEnvironmentInteger;
|
|
247
246
|
/**
|
|
248
247
|
* Parses a boolean from an environment variable string.
|
|
249
248
|
* Accepts 'true'/'false'/'1'/'0' case-insensitively.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAkC9D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAEjC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAExD;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE;QACzB;;;WAGG;QACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QACtB;;WAEG;QACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IAEF;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAEzC,0FAA0F;IAC1F,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAExC,yFAAyF;IACzF,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAE3C,oEAAoE;IACpE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAExC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAE1C;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAEtC;;;OAGG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAExC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBAAa,YAAY;IACxB,+DAA+D;IAC/D,OAAO,CAAC,YAAY,CAAW;IAE/B;;;;;;;;;;;;;OAaG;gBACS,UAAU,CAAC,EAAE,MAAM;IAc/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,IAAI,IAAI,iBAAiB,GAAG,IAAI;IAiBhC;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB;IAsF9E,OAAO,CAAC,WAAW;IAsBnB;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IAuCjC;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAOpB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IASnB;;;;;;;;;;;;;;;;;;OAkBG;IACH,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,mBAAmB;CAkBrE"}
|
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { parse } from "yaml";
|
|
5
5
|
import { ConfigurationError, getErrorMessage } from "../errors.js";
|
|
6
|
+
import { parseEnvironmentInteger } from "./EnvironmentInteger.js";
|
|
6
7
|
import * as __rspack_external_valibot from "valibot";
|
|
7
8
|
const ConfigFileOptionsSchema = __rspack_external_valibot.looseObject({
|
|
8
9
|
maxHistorySize: __rspack_external_valibot.optional(__rspack_external_valibot.number()),
|
|
@@ -57,9 +58,9 @@ class ConfigLoader {
|
|
|
57
58
|
}
|
|
58
59
|
applyEnvironmentOverrides(config) {
|
|
59
60
|
const result = this.cloneConfig(config);
|
|
60
|
-
if (void 0 !== process.env.TRACELATTICE_MAX_HISTORY_SIZE) result.maxHistorySize =
|
|
61
|
-
if (void 0 !== process.env.TRACELATTICE_MAX_BRANCHES) result.maxBranches =
|
|
62
|
-
if (void 0 !== process.env.TRACELATTICE_MAX_BRANCH_SIZE) result.maxBranchSize =
|
|
61
|
+
if (void 0 !== process.env.TRACELATTICE_MAX_HISTORY_SIZE) result.maxHistorySize = parseEnvironmentInteger('TRACELATTICE_MAX_HISTORY_SIZE', process.env.TRACELATTICE_MAX_HISTORY_SIZE);
|
|
62
|
+
if (void 0 !== process.env.TRACELATTICE_MAX_BRANCHES) result.maxBranches = parseEnvironmentInteger('TRACELATTICE_MAX_BRANCHES', process.env.TRACELATTICE_MAX_BRANCHES);
|
|
63
|
+
if (void 0 !== process.env.TRACELATTICE_MAX_BRANCH_SIZE) result.maxBranchSize = parseEnvironmentInteger('TRACELATTICE_MAX_BRANCH_SIZE', process.env.TRACELATTICE_MAX_BRANCH_SIZE);
|
|
63
64
|
if (process.env.TRACELATTICE_LOG_LEVEL && [
|
|
64
65
|
'debug',
|
|
65
66
|
'info',
|
|
@@ -70,7 +71,7 @@ class ConfigLoader {
|
|
|
70
71
|
if (void 0 !== process.env.TRACELATTICE_SKILL_DIRS) result.skillDirs = '' === process.env.TRACELATTICE_SKILL_DIRS ? [] : process.env.TRACELATTICE_SKILL_DIRS.split(':');
|
|
71
72
|
if (void 0 !== process.env.TRACELATTICE_TOOL_DIRS) result.toolDirs = '' === process.env.TRACELATTICE_TOOL_DIRS ? [] : process.env.TRACELATTICE_TOOL_DIRS.split(':');
|
|
72
73
|
if (void 0 !== process.env.TRACELATTICE_DISCOVERY_CACHE_TTL) {
|
|
73
|
-
const seconds =
|
|
74
|
+
const seconds = parseEnvironmentInteger('TRACELATTICE_DISCOVERY_CACHE_TTL', process.env.TRACELATTICE_DISCOVERY_CACHE_TTL);
|
|
74
75
|
const ttl = 1000 * seconds;
|
|
75
76
|
if (!Number.isSafeInteger(ttl)) throw new ConfigurationError('TRACELATTICE_DISCOVERY_CACHE_TTL exceeds the safe integer range');
|
|
76
77
|
result.discoveryCache = {
|
|
@@ -79,15 +80,15 @@ class ConfigLoader {
|
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
if (void 0 !== process.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE) {
|
|
82
|
-
const maxSize =
|
|
83
|
+
const maxSize = parseEnvironmentInteger('TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE', process.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE);
|
|
83
84
|
result.discoveryCache = {
|
|
84
85
|
...result.discoveryCache,
|
|
85
86
|
maxSize
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
|
-
if (void 0 !== process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS) result.toolInterleaveTtlMs =
|
|
89
|
-
if (void 0 !== process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS) result.toolInterleaveSweepMs =
|
|
90
|
-
if (void 0 !== process.env.TRACELATTICE_SESSION_MAX_PER_OWNER) result.maxSessionsPerOwner =
|
|
89
|
+
if (void 0 !== process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS) result.toolInterleaveTtlMs = parseEnvironmentInteger('TRACELATTICE_TOOL_INTERLEAVE_TTL_MS', process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS);
|
|
90
|
+
if (void 0 !== process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS) result.toolInterleaveSweepMs = parseEnvironmentInteger('TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS', process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS);
|
|
91
|
+
if (void 0 !== process.env.TRACELATTICE_SESSION_MAX_PER_OWNER) result.maxSessionsPerOwner = parseEnvironmentInteger('TRACELATTICE_SESSION_MAX_PER_OWNER', process.env.TRACELATTICE_SESSION_MAX_PER_OWNER);
|
|
91
92
|
this.applyFeatureFlagOverrides(result);
|
|
92
93
|
return result;
|
|
93
94
|
}
|
|
@@ -155,12 +156,6 @@ class ConfigLoader {
|
|
|
155
156
|
result.features = features;
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
|
-
parseEnvironmentInteger(environmentName, raw) {
|
|
159
|
-
if (!/^(0|[1-9]\d*)$/.test(raw)) throw new ConfigurationError(`${environmentName} must be an integer, got ${raw}`);
|
|
160
|
-
const parsed = Number(raw);
|
|
161
|
-
if (!Number.isSafeInteger(parsed)) throw new ConfigurationError(`${environmentName} must be a safe integer, got ${raw}`);
|
|
162
|
-
return parsed;
|
|
163
|
-
}
|
|
164
159
|
parseBoolean(raw) {
|
|
165
160
|
const v = raw.trim().toLowerCase();
|
|
166
161
|
if ('true' === v || '1' === v) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config/ConfigLoader.js","sources":["../../src/config/ConfigLoader.ts"],"sourcesContent":["/**\n * Configuration file loading with environment variable override support.\n *\n * This module provides the `ConfigLoader` class which handles loading configuration\n * from YAML and JSON files in standard locations, with automatic environment variable\n * overrides for supported settings. Persistence buffer, flush, and retry settings are file-only.\n *\n * @module config\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport * as v from 'valibot';\nimport type { PersistenceConfig } from '../contracts/PersistenceBackend.js';\nimport { ConfigurationError, getErrorMessage } from '../errors.js';\nimport type { FeatureFlags } from '../contracts/features.js';\nimport type { ServerConfigOptions } from '../ServerConfig.js';\n\n/**\n * Lenient runtime schema for config files. Only validates top-level shape;\n * unknown extra fields are preserved by Valibot's default object behavior\n * being strict is avoided by using `v.looseObject`. All fields are optional.\n */\nconst ConfigFileOptionsSchema = v.looseObject({\n\tmaxHistorySize: v.optional(v.number()),\n\tmaxBranches: v.optional(v.number()),\n\tmaxBranchSize: v.optional(v.number()),\n\tlogLevel: v.optional(v.picklist(['debug', 'info', 'warn', 'error'])),\n\tprettyLog: v.optional(v.boolean()),\n\tskillDirs: v.optional(v.array(v.string())),\n\ttoolDirs: v.optional(v.array(v.string())),\n\tdiscoveryCache: v.optional(\n\t\tv.looseObject({\n\t\t\tttl: v.optional(v.number()),\n\t\t\tmaxSize: v.optional(v.number()),\n\t\t})\n\t),\n\tpersistence: v.optional(v.looseObject({})),\n\tpersistenceBufferSize: v.optional(v.number()),\n\tpersistenceFlushInterval: v.optional(v.number()),\n\tpersistenceMaxRetries: v.optional(v.number()),\n\tfeatures: v.optional(v.looseObject({})),\n\ttoolInterleaveTtlMs: v.optional(v.number()),\n\ttoolInterleaveSweepMs: v.optional(v.number()),\n\tmaxSessionsPerOwner: v.optional(v.number()),\n});\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n\n/**\n * Configuration options loaded from config files.\n *\n * These options represent the structure of configuration files (JSON or YAML)\n * that can be loaded from standard locations. Only supported fields have\n * environment overrides; persistence buffer, flush, and retry settings are file-only.\n *\n * @example\n * ```yaml\n * # .claude/config.yaml\n * maxHistorySize: 500\n * maxBranches: 25\n * logLevel: debug\n * prettyLog: true\n * skillDirs:\n * - ./custom-skills\n * toolDirs:\n * - ./custom-tools\n * discoveryCache:\n * ttl: 600000\n * maxSize: 200\n * persistence:\n * enabled: true\n * backend: sqlite\n * options:\n * dbPath: ./data/history.db\n * ```\n */\nexport interface ConfigFileOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * Can be overridden by `TRACELATTICE_MAX_HISTORY_SIZE` environment variable.\n\t */\n\treadonly maxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * Can be overridden by `TRACELATTICE_MAX_BRANCHES` environment variable.\n\t */\n\treadonly maxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * Can be overridden by `TRACELATTICE_MAX_BRANCH_SIZE` environment variable.\n\t */\n\treadonly maxBranchSize?: number;\n\n\t/**\n\t * Logging level for the application.\n\t * Can be overridden by `TRACELATTICE_LOG_LEVEL` environment variable.\n\t */\n\treadonly logLevel?: 'debug' | 'info' | 'warn' | 'error';\n\n\t/**\n\t * Whether to enable pretty (formatted) logging output.\n\t * Can be overridden by `TRACELATTICE_PRETTY_LOG` environment variable (set to \"false\" to disable).\n\t */\n\treadonly prettyLog?: boolean;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * Can be overridden by `TRACELATTICE_SKILL_DIRS` environment variable (colon-separated).\n\t */\n\treadonly skillDirs?: string[];\n\n\t/**\n\t * Directory paths to search for tools.\n\t * Can be overridden by `TRACELATTICE_TOOL_DIRS` environment variable (colon-separated).\n\t */\n\treadonly toolDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t * Can be overridden by `TRACELATTICE_DISCOVERY_CACHE_TTL` and\n\t * `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` environment variables.\n\t */\n\treadonly discoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * Environment variable `TRACELATTICE_DISCOVERY_CACHE_TTL` accepts seconds.\n\t\t */\n\t\treadonly ttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t */\n\t\treadonly maxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\treadonly persistence?: PersistenceConfig;\n\n\t/** Maximum buffered thoughts before flushing; file-only (default 100 in ServerConfig). */\n\treadonly persistenceBufferSize?: number;\n\n\t/** Periodic flush interval in milliseconds; file-only (default 1000 in ServerConfig). */\n\treadonly persistenceFlushInterval?: number;\n\n\t/** Maximum flush retries; file-only (default 3 in ServerConfig). */\n\treadonly persistenceMaxRetries?: number;\n\n\t/**\n\t * Feature flag overrides. Each field can be set independently.\n\t * Can be overridden by `TRACELATTICE_FEATURES_*` environment variables.\n\t */\n\treadonly features?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries.\n\t * Can be overridden by `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS` environment variable.\n\t */\n\treadonly toolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * Can be overridden by `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` environment variable.\n\t */\n\treadonly toolInterleaveSweepMs?: number;\n\n\t/**\n\t * Maximum sessions per owner. Per-owner LRU bucket prevents one user from\n\t * consuming all session slots.\n\t * Can be overridden by `TRACELATTICE_SESSION_MAX_PER_OWNER` environment variable.\n\t */\n\treadonly maxSessionsPerOwner?: number;\n}\n\n/**\n * Loads configuration from files with environment variable overrides.\n *\n * This class searches for configuration files in standard locations and applies\n * environment variable overrides. Files are searched in priority order, with the\n * first valid match being used. Supported environment variables take precedence over file values.\n *\n * @remarks\n * **Config File Search Order (priority):**\n * 1. Custom path or `TRACELATTICE_CONFIG` (if present, replaces the default search list)\n * 2. `.claude/config.json` (project-local)\n * 3. `.claude/config.yaml` (project-local)\n * 4. `.claude/config.yml` (project-local)\n * 5. `~/.claude/config.json` (user-global)\n * 6. `~/.claude/config.yaml` (user-global)\n * 7. `~/.claude/config.yml` (user-global)\n *\n * **Environment Variable Overrides:**\n * | Variable | Type | Description |\n * |----------|------|-------------|\n * | `TRACELATTICE_MAX_HISTORY_SIZE` | number | Max thoughts in history |\n * | `TRACELATTICE_MAX_BRANCHES` | number | Max number of branches |\n * | `TRACELATTICE_MAX_BRANCH_SIZE` | number | Max size of each branch |\n * | `TRACELATTICE_LOG_LEVEL` | string | Logging level (debug/info/warn/error) |\n * | `TRACELATTICE_PRETTY_LOG` | string | \"false\" to disable pretty logging |\n * | `TRACELATTICE_SKILL_DIRS` | string | Colon-separated directory paths |\n * | `TRACELATTICE_TOOL_DIRS` | string | Colon-separated directory paths |\n * | `TRACELATTICE_DISCOVERY_CACHE_TTL` | number | TTL in seconds (converted to ms) |\n * | `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` | number | Max cache entries |\n * | `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS` / `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` | number | Milliseconds |\n * | `TRACELATTICE_SESSION_MAX_PER_OWNER` | number | Max sessions per owner |\n * | `TRACELATTICE_FEATURES_*` | boolean or strategy | Feature toggles or `sequential`/`tot` |\n *\n * Persistence buffer size, flush interval (milliseconds), and retries are file-only.\n *\n * @example\n * ```typescript\n * // Use default search paths\n * const loader1 = new ConfigLoader();\n * const config1 = loader1.load();\n *\n * // Use custom config path\n * const loader2 = new ConfigLoader('./my-config.yaml');\n * const config2 = loader2.load();\n *\n * // Convert to ServerConfig options\n * const serverOptions = loader2.toServerConfigOptions(config2);\n * ```\n */\nexport class ConfigLoader {\n\t/** Array of config file paths to search, in priority order. */\n\tprivate _configPaths: string[];\n\n\t/**\n\t * Creates a new ConfigLoader instance.\n\t *\n\t * @param customPath - Optional custom config file path. If provided, only this path will be checked.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Use default search paths\n\t * const loader1 = new ConfigLoader();\n\t *\n\t * // Use a specific config file\n\t * const loader2 = new ConfigLoader('./custom-config.json');\n\t * ```\n\t */\n\tconstructor(customPath?: string) {\n\t\tconst configuredPath = customPath ?? process.env.TRACELATTICE_CONFIG;\n\t\tthis._configPaths = configuredPath\n\t\t\t? [configuredPath]\n\t\t\t: [\n\t\t\t\t\t'.claude/config.json',\n\t\t\t\t\t'.claude/config.yaml',\n\t\t\t\t\t'.claude/config.yml',\n\t\t\t\t\tjoin(homedir(), '.claude/config.json'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yaml'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yml'),\n\t\t\t\t];\n\t}\n\n\t/**\n\t * Loads configuration from files and applies environment overrides.\n\t *\n\t * Searches for config files in the configured paths (in priority order),\n\t * parses the first match, and applies environment variable overrides.\n\t * A malformed file is logged and skipped. With no valid file, applies environment\n\t * overrides to an empty object (the declared nullable return type does not return null).\n\t *\n\t * @returns The loaded configuration with environment overrides applied, or an empty/overlaid object\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t *\n\t * if (config) {\n\t * console.log('Max history size:', config.maxHistorySize);\n\t * console.log('Log level:', config.logLevel);\n\t * }\n\t * ```\n\t */\n\tload(): ConfigFileOptions | null {\n\t\tlet config: ConfigFileOptions | null = null;\n\n\t\tfor (const configPath of this._configPaths) {\n\t\t\tif (existsSync(configPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconfig = this.parseConfig(configPath);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Failed to load config from ${configPath}:`, getErrorMessage(error));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.applyEnvironmentOverrides(config || {});\n\t}\n\n\t/**\n\t * Applies environment variable overrides to the configuration.\n\t *\n\t * Environment variables take precedence over file-based configuration.\n\t * Supported environment variables:\n\t * - `TRACELATTICE_MAX_HISTORY_SIZE`, `TRACELATTICE_MAX_BRANCHES`,\n\t * `TRACELATTICE_MAX_BRANCH_SIZE` (numbers)\n\t * - `TRACELATTICE_LOG_LEVEL` (debug/info/warn/error)\n\t * - `TRACELATTICE_PRETTY_LOG` (\"false\" to disable)\n\t * - `TRACELATTICE_SKILL_DIRS` (colon-separated paths)\n\t * - `TRACELATTICE_TOOL_DIRS` (colon-separated paths)\n\t * - `TRACELATTICE_DISCOVERY_CACHE_TTL` (in seconds, converted to ms)\n\t * - `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` (number)\n\t * - `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS`, `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` (milliseconds)\n\t * - `TRACELATTICE_SESSION_MAX_PER_OWNER` (number)\n\t * - `TRACELATTICE_FEATURES_*` (booleans true/false/1/0 or sequential/tot strategy)\n\t * Persistence buffer, flush interval, and retry settings have no environment aliases.\n\t *\n\t * @param config - The configuration to apply overrides to\n\t * @returns A new configuration object with environment overrides applied\n\t */\n\tpublic applyEnvironmentOverrides(config: ConfigFileOptions): ConfigFileOptions {\n\t\tconst result = this.cloneConfig(config);\n\n\t\tif (process.env.TRACELATTICE_MAX_HISTORY_SIZE !== undefined) {\n\t\t\tresult.maxHistorySize = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_HISTORY_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_HISTORY_SIZE\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_MAX_BRANCHES !== undefined) {\n\t\t\tresult.maxBranches = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_BRANCHES',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_BRANCHES\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_MAX_BRANCH_SIZE !== undefined) {\n\t\t\tresult.maxBranchSize = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_BRANCH_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_BRANCH_SIZE\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tprocess.env.TRACELATTICE_LOG_LEVEL &&\n\t\t\t['debug', 'info', 'warn', 'error'].includes(process.env.TRACELATTICE_LOG_LEVEL)\n\t\t) {\n\t\t\tresult.logLevel = process.env.TRACELATTICE_LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error';\n\t\t}\n\t\tif (process.env.TRACELATTICE_PRETTY_LOG === 'false') {\n\t\t\tresult.prettyLog = false;\n\t\t}\n\t\tif (process.env.TRACELATTICE_SKILL_DIRS !== undefined) {\n\t\t\tresult.skillDirs =\n\t\t\t\tprocess.env.TRACELATTICE_SKILL_DIRS === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: process.env.TRACELATTICE_SKILL_DIRS.split(':');\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_DIRS !== undefined) {\n\t\t\tresult.toolDirs =\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_DIRS === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: process.env.TRACELATTICE_TOOL_DIRS.split(':');\n\t\t}\n\t\tif (process.env.TRACELATTICE_DISCOVERY_CACHE_TTL !== undefined) {\n\t\t\tconst seconds = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_TTL',\n\t\t\t\tprocess.env.TRACELATTICE_DISCOVERY_CACHE_TTL\n\t\t\t);\n\t\t\tconst ttl = seconds * 1000;\n\t\t\tif (!Number.isSafeInteger(ttl)) {\n\t\t\t\tthrow new ConfigurationError(\n\t\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_TTL exceeds the safe integer range'\n\t\t\t\t);\n\t\t\t}\n\t\t\tresult.discoveryCache = { ...result.discoveryCache, ttl };\n\t\t}\n\t\tif (process.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE !== undefined) {\n\t\t\tconst maxSize = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE\n\t\t\t);\n\t\t\tresult.discoveryCache = { ...result.discoveryCache, maxSize };\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS !== undefined) {\n\t\t\tresult.toolInterleaveTtlMs = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_TOOL_INTERLEAVE_TTL_MS',\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS !== undefined) {\n\t\t\tresult.toolInterleaveSweepMs = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS',\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_SESSION_MAX_PER_OWNER !== undefined) {\n\t\t\tresult.maxSessionsPerOwner = this.parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_SESSION_MAX_PER_OWNER',\n\t\t\t\tprocess.env.TRACELATTICE_SESSION_MAX_PER_OWNER\n\t\t\t);\n\t\t}\n\n\t\tthis.applyFeatureFlagOverrides(result);\n\n\t\treturn result;\n\t}\n\n\tprivate cloneConfig(config: ConfigFileOptions): Mutable<ConfigFileOptions> {\n\t\treturn {\n\t\t\t...config,\n\t\t\t...(config.skillDirs === undefined ? {} : { skillDirs: [...config.skillDirs] }),\n\t\t\t...(config.toolDirs === undefined ? {} : { toolDirs: [...config.toolDirs] }),\n\t\t\t...(config.discoveryCache === undefined\n\t\t\t\t? {}\n\t\t\t\t: { discoveryCache: { ...config.discoveryCache } }),\n\t\t\t...(config.persistence === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tpersistence: {\n\t\t\t\t\t\t\t...config.persistence,\n\t\t\t\t\t\t\t...(config.persistence.options === undefined\n\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t: { options: { ...config.persistence.options } }),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t...(config.features === undefined ? {} : { features: { ...config.features } }),\n\t\t};\n\t}\n\n\t/**\n\t * Applies TRACELATTICE_FEATURES_* environment variable overrides for feature flags.\n\t * Booleans accept 'true'/'false'/'1'/'0' (case-insensitive).\n\t * Invalid reasoningStrategy values are rejected.\n\t *\n\t * @param result - Configuration object to mutate with feature flag overrides\n\t * @private\n\t */\n\tprivate applyFeatureFlagOverrides(result: Mutable<ConfigFileOptions>): void {\n\t\tconst boolMap: Record<string, Exclude<keyof FeatureFlags, 'reasoningStrategy'>> = {\n\t\t\tTRACELATTICE_FEATURES_DAG_EDGES: 'dagEdges',\n\t\t\tTRACELATTICE_FEATURES_CALIBRATION: 'calibration',\n\t\t\tTRACELATTICE_FEATURES_COMPRESSION: 'compression',\n\t\t\tTRACELATTICE_FEATURES_TOOL_INTERLEAVE: 'toolInterleave',\n\t\t\tTRACELATTICE_FEATURES_NEW_THOUGHT_TYPES: 'newThoughtTypes',\n\t\t\tTRACELATTICE_FEATURES_OUTCOME_RECORDING: 'outcomeRecording',\n\t\t};\n\t\tfor (const [envVar, key] of Object.entries(boolMap)) {\n\t\t\tconst raw = process.env[envVar];\n\t\t\tif (raw === undefined) continue;\n\t\t\tconst parsed = this.parseBoolean(raw);\n\t\t\tif (parsed === undefined) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Invalid boolean value for ${envVar}: \"${raw}\" (expected true/false/1/0). Ignoring.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst features: { -readonly [K in keyof FeatureFlags]?: FeatureFlags[K] } =\n\t\t\t\tresult.features ?? {};\n\t\t\tfeatures[key] = parsed;\n\t\t\tresult.features = features;\n\t\t}\n\n\t\tconst strategyRaw = process.env.TRACELATTICE_FEATURES_REASONING_STRATEGY;\n\t\tif (strategyRaw !== undefined) {\n\t\t\tif (strategyRaw !== 'sequential' && strategyRaw !== 'tot') {\n\t\t\t\tthrow new ConfigurationError(\n\t\t\t\t\t`TRACELATTICE_FEATURES_REASONING_STRATEGY must be one of sequential, tot, got ${strategyRaw}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst features: { -readonly [K in keyof FeatureFlags]?: FeatureFlags[K] } =\n\t\t\t\tresult.features ?? {};\n\t\t\tfeatures.reasoningStrategy = strategyRaw;\n\t\t\tresult.features = features;\n\t\t}\n\t}\n\n\tprivate parseEnvironmentInteger(environmentName: string, raw: string): number {\n\t\tif (!/^(0|[1-9]\\d*)$/.test(raw)) {\n\t\t\tthrow new ConfigurationError(`${environmentName} must be an integer, got ${raw}`);\n\t\t}\n\t\tconst parsed = Number(raw);\n\t\tif (!Number.isSafeInteger(parsed)) {\n\t\t\tthrow new ConfigurationError(`${environmentName} must be a safe integer, got ${raw}`);\n\t\t}\n\t\treturn parsed;\n\t}\n\n\t/**\n\t * Parses a boolean from an environment variable string.\n\t * Accepts 'true'/'false'/'1'/'0' case-insensitively.\n\t *\n\t * @param raw - Raw environment variable string\n\t * @returns Parsed boolean, or undefined if the value is invalid\n\t * @private\n\t */\n\tprivate parseBoolean(raw: string): boolean | undefined {\n\t\tconst v = raw.trim().toLowerCase();\n\t\tif (v === 'true' || v === '1') return true;\n\t\tif (v === 'false' || v === '0') return false;\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Parses a configuration file (JSON or YAML).\n\t *\n\t * Detects the file type by extension and uses the appropriate parser.\n\t * Supports `.json`, `.yaml`, and `.yml` file extensions.\n\t *\n\t * @param filePath - Path to the configuration file to parse\n\t * @returns The parsed configuration object\n\t * @throws {Error} If the file cannot be parsed\n\t * @private\n\t */\n\tprivate parseConfig(filePath: string): ConfigFileOptions {\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\tconst ext = filePath.split('.').pop()?.toLowerCase();\n\n\t\tconst raw: unknown = ext === 'yaml' || ext === 'yml' ? parseYaml(content) : JSON.parse(content);\n\n\t\treturn v.parse(ConfigFileOptionsSchema, raw) as ConfigFileOptions;\n\t}\n\n\t/**\n\t * Converts file-based configuration to ServerConfig options.\n\t *\n\t * This is a convenience method for extracting the ServerConfig-relevant\n\t * options from a file-based configuration.\n\t *\n\t * @param config - The configuration to convert\n\t * @returns An object with ServerConfig-compatible options\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t * if (config) {\n\t * const serverOpts = loader.toServerConfigOptions(config);\n\t * const serverConfig = new ServerConfig(serverOpts);\n\t * }\n\t * ```\n\t */\n\ttoServerConfigOptions(config: ConfigFileOptions): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: config.maxHistorySize,\n\t\t\tmaxBranches: config.maxBranches,\n\t\t\tmaxBranchSize: config.maxBranchSize,\n\t\t\tskillDirs: config.skillDirs,\n\t\t\ttoolDirs: config.toolDirs,\n\t\t\tdiscoveryCache: config.discoveryCache,\n\t\t\tpersistence: config.persistence,\n\t\t\tpersistenceBufferSize: config.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: config.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: config.persistenceMaxRetries,\n\t\t\tfeatures: config.features,\n\t\t\ttoolInterleaveTtlMs: config.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: config.toolInterleaveSweepMs,\n\t\t\tmaxSessionsPerOwner: config.maxSessionsPerOwner,\n\t\t};\n\t}\n}\n"],"names":["ConfigFileOptionsSchema","v","ConfigLoader","customPath","configuredPath","process","join","homedir","config","configPath","existsSync","error","console","getErrorMessage","result","undefined","seconds","ttl","Number","ConfigurationError","maxSize","boolMap","envVar","key","Object","raw","parsed","features","strategyRaw","environmentName","filePath","content","readFileSync","ext","parseYaml","JSON"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config/ConfigLoader.js","sources":["../../src/config/ConfigLoader.ts"],"sourcesContent":["/**\n * Configuration file loading with environment variable override support.\n *\n * This module provides the `ConfigLoader` class which handles loading configuration\n * from YAML and JSON files in standard locations, with automatic environment variable\n * overrides for supported settings. Persistence buffer, flush, and retry settings are file-only.\n *\n * @module config\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport * as v from 'valibot';\nimport type { PersistenceConfig } from '../contracts/PersistenceBackend.js';\nimport { ConfigurationError, getErrorMessage } from '../errors.js';\nimport type { FeatureFlags } from '../contracts/features.js';\nimport type { ServerConfigOptions } from '../ServerConfig.js';\nimport { parseEnvironmentInteger } from './EnvironmentInteger.js';\n\n/**\n * Lenient runtime schema for config files. Only validates top-level shape;\n * unknown extra fields are preserved by Valibot's default object behavior\n * being strict is avoided by using `v.looseObject`. All fields are optional.\n */\nconst ConfigFileOptionsSchema = v.looseObject({\n\tmaxHistorySize: v.optional(v.number()),\n\tmaxBranches: v.optional(v.number()),\n\tmaxBranchSize: v.optional(v.number()),\n\tlogLevel: v.optional(v.picklist(['debug', 'info', 'warn', 'error'])),\n\tprettyLog: v.optional(v.boolean()),\n\tskillDirs: v.optional(v.array(v.string())),\n\ttoolDirs: v.optional(v.array(v.string())),\n\tdiscoveryCache: v.optional(\n\t\tv.looseObject({\n\t\t\tttl: v.optional(v.number()),\n\t\t\tmaxSize: v.optional(v.number()),\n\t\t})\n\t),\n\tpersistence: v.optional(v.looseObject({})),\n\tpersistenceBufferSize: v.optional(v.number()),\n\tpersistenceFlushInterval: v.optional(v.number()),\n\tpersistenceMaxRetries: v.optional(v.number()),\n\tfeatures: v.optional(v.looseObject({})),\n\ttoolInterleaveTtlMs: v.optional(v.number()),\n\ttoolInterleaveSweepMs: v.optional(v.number()),\n\tmaxSessionsPerOwner: v.optional(v.number()),\n});\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n\n/**\n * Configuration options loaded from config files.\n *\n * These options represent the structure of configuration files (JSON or YAML)\n * that can be loaded from standard locations. Only supported fields have\n * environment overrides; persistence buffer, flush, and retry settings are file-only.\n *\n * @example\n * ```yaml\n * # .claude/config.yaml\n * maxHistorySize: 500\n * maxBranches: 25\n * logLevel: debug\n * prettyLog: true\n * skillDirs:\n * - ./custom-skills\n * toolDirs:\n * - ./custom-tools\n * discoveryCache:\n * ttl: 600000\n * maxSize: 200\n * persistence:\n * enabled: true\n * backend: sqlite\n * options:\n * dbPath: ./data/history.db\n * ```\n */\nexport interface ConfigFileOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * Can be overridden by `TRACELATTICE_MAX_HISTORY_SIZE` environment variable.\n\t */\n\treadonly maxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * Can be overridden by `TRACELATTICE_MAX_BRANCHES` environment variable.\n\t */\n\treadonly maxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * Can be overridden by `TRACELATTICE_MAX_BRANCH_SIZE` environment variable.\n\t */\n\treadonly maxBranchSize?: number;\n\n\t/**\n\t * Logging level for the application.\n\t * Can be overridden by `TRACELATTICE_LOG_LEVEL` environment variable.\n\t */\n\treadonly logLevel?: 'debug' | 'info' | 'warn' | 'error';\n\n\t/**\n\t * Whether to enable pretty (formatted) logging output.\n\t * Can be overridden by `TRACELATTICE_PRETTY_LOG` environment variable (set to \"false\" to disable).\n\t */\n\treadonly prettyLog?: boolean;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * Can be overridden by `TRACELATTICE_SKILL_DIRS` environment variable (colon-separated).\n\t */\n\treadonly skillDirs?: string[];\n\n\t/**\n\t * Directory paths to search for tools.\n\t * Can be overridden by `TRACELATTICE_TOOL_DIRS` environment variable (colon-separated).\n\t */\n\treadonly toolDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t * Can be overridden by `TRACELATTICE_DISCOVERY_CACHE_TTL` and\n\t * `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` environment variables.\n\t */\n\treadonly discoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * Environment variable `TRACELATTICE_DISCOVERY_CACHE_TTL` accepts seconds.\n\t\t */\n\t\treadonly ttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t */\n\t\treadonly maxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\treadonly persistence?: PersistenceConfig;\n\n\t/** Maximum buffered thoughts before flushing; file-only (default 100 in ServerConfig). */\n\treadonly persistenceBufferSize?: number;\n\n\t/** Periodic flush interval in milliseconds; file-only (default 1000 in ServerConfig). */\n\treadonly persistenceFlushInterval?: number;\n\n\t/** Maximum flush retries; file-only (default 3 in ServerConfig). */\n\treadonly persistenceMaxRetries?: number;\n\n\t/**\n\t * Feature flag overrides. Each field can be set independently.\n\t * Can be overridden by `TRACELATTICE_FEATURES_*` environment variables.\n\t */\n\treadonly features?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries.\n\t * Can be overridden by `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS` environment variable.\n\t */\n\treadonly toolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * Can be overridden by `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` environment variable.\n\t */\n\treadonly toolInterleaveSweepMs?: number;\n\n\t/**\n\t * Maximum sessions per owner. Per-owner LRU bucket prevents one user from\n\t * consuming all session slots.\n\t * Can be overridden by `TRACELATTICE_SESSION_MAX_PER_OWNER` environment variable.\n\t */\n\treadonly maxSessionsPerOwner?: number;\n}\n\n/**\n * Loads configuration from files with environment variable overrides.\n *\n * This class searches for configuration files in standard locations and applies\n * environment variable overrides. Files are searched in priority order, with the\n * first valid match being used. Supported environment variables take precedence over file values.\n *\n * @remarks\n * **Config File Search Order (priority):**\n * 1. Custom path or `TRACELATTICE_CONFIG` (if present, replaces the default search list)\n * 2. `.claude/config.json` (project-local)\n * 3. `.claude/config.yaml` (project-local)\n * 4. `.claude/config.yml` (project-local)\n * 5. `~/.claude/config.json` (user-global)\n * 6. `~/.claude/config.yaml` (user-global)\n * 7. `~/.claude/config.yml` (user-global)\n *\n * **Environment Variable Overrides:**\n * | Variable | Type | Description |\n * |----------|------|-------------|\n * | `TRACELATTICE_MAX_HISTORY_SIZE` | number | Max thoughts in history |\n * | `TRACELATTICE_MAX_BRANCHES` | number | Max number of branches |\n * | `TRACELATTICE_MAX_BRANCH_SIZE` | number | Max size of each branch |\n * | `TRACELATTICE_LOG_LEVEL` | string | Logging level (debug/info/warn/error) |\n * | `TRACELATTICE_PRETTY_LOG` | string | \"false\" to disable pretty logging |\n * | `TRACELATTICE_SKILL_DIRS` | string | Colon-separated directory paths |\n * | `TRACELATTICE_TOOL_DIRS` | string | Colon-separated directory paths |\n * | `TRACELATTICE_DISCOVERY_CACHE_TTL` | number | TTL in seconds (converted to ms) |\n * | `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` | number | Max cache entries |\n * | `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS` / `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` | number | Milliseconds |\n * | `TRACELATTICE_SESSION_MAX_PER_OWNER` | number | Max sessions per owner |\n * | `TRACELATTICE_FEATURES_*` | boolean or strategy | Feature toggles or `sequential`/`tot` |\n *\n * Persistence buffer size, flush interval (milliseconds), and retries are file-only.\n *\n * @example\n * ```typescript\n * // Use default search paths\n * const loader1 = new ConfigLoader();\n * const config1 = loader1.load();\n *\n * // Use custom config path\n * const loader2 = new ConfigLoader('./my-config.yaml');\n * const config2 = loader2.load();\n *\n * // Convert to ServerConfig options\n * const serverOptions = loader2.toServerConfigOptions(config2);\n * ```\n */\nexport class ConfigLoader {\n\t/** Array of config file paths to search, in priority order. */\n\tprivate _configPaths: string[];\n\n\t/**\n\t * Creates a new ConfigLoader instance.\n\t *\n\t * @param customPath - Optional custom config file path. If provided, only this path will be checked.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Use default search paths\n\t * const loader1 = new ConfigLoader();\n\t *\n\t * // Use a specific config file\n\t * const loader2 = new ConfigLoader('./custom-config.json');\n\t * ```\n\t */\n\tconstructor(customPath?: string) {\n\t\tconst configuredPath = customPath ?? process.env.TRACELATTICE_CONFIG;\n\t\tthis._configPaths = configuredPath\n\t\t\t? [configuredPath]\n\t\t\t: [\n\t\t\t\t\t'.claude/config.json',\n\t\t\t\t\t'.claude/config.yaml',\n\t\t\t\t\t'.claude/config.yml',\n\t\t\t\t\tjoin(homedir(), '.claude/config.json'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yaml'),\n\t\t\t\t\tjoin(homedir(), '.claude/config.yml'),\n\t\t\t\t];\n\t}\n\n\t/**\n\t * Loads configuration from files and applies environment overrides.\n\t *\n\t * Searches for config files in the configured paths (in priority order),\n\t * parses the first match, and applies environment variable overrides.\n\t * A malformed file is logged and skipped. With no valid file, applies environment\n\t * overrides to an empty object (the declared nullable return type does not return null).\n\t *\n\t * @returns The loaded configuration with environment overrides applied, or an empty/overlaid object\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t *\n\t * if (config) {\n\t * console.log('Max history size:', config.maxHistorySize);\n\t * console.log('Log level:', config.logLevel);\n\t * }\n\t * ```\n\t */\n\tload(): ConfigFileOptions | null {\n\t\tlet config: ConfigFileOptions | null = null;\n\n\t\tfor (const configPath of this._configPaths) {\n\t\t\tif (existsSync(configPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconfig = this.parseConfig(configPath);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Failed to load config from ${configPath}:`, getErrorMessage(error));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.applyEnvironmentOverrides(config || {});\n\t}\n\n\t/**\n\t * Applies environment variable overrides to the configuration.\n\t *\n\t * Environment variables take precedence over file-based configuration.\n\t * Supported environment variables:\n\t * - `TRACELATTICE_MAX_HISTORY_SIZE`, `TRACELATTICE_MAX_BRANCHES`,\n\t * `TRACELATTICE_MAX_BRANCH_SIZE` (numbers)\n\t * - `TRACELATTICE_LOG_LEVEL` (debug/info/warn/error)\n\t * - `TRACELATTICE_PRETTY_LOG` (\"false\" to disable)\n\t * - `TRACELATTICE_SKILL_DIRS` (colon-separated paths)\n\t * - `TRACELATTICE_TOOL_DIRS` (colon-separated paths)\n\t * - `TRACELATTICE_DISCOVERY_CACHE_TTL` (in seconds, converted to ms)\n\t * - `TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE` (number)\n\t * - `TRACELATTICE_TOOL_INTERLEAVE_TTL_MS`, `TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS` (milliseconds)\n\t * - `TRACELATTICE_SESSION_MAX_PER_OWNER` (number)\n\t * - `TRACELATTICE_FEATURES_*` (booleans true/false/1/0 or sequential/tot strategy)\n\t * Persistence buffer, flush interval, and retry settings have no environment aliases.\n\t *\n\t * @param config - The configuration to apply overrides to\n\t * @returns A new configuration object with environment overrides applied\n\t */\n\tpublic applyEnvironmentOverrides(config: ConfigFileOptions): ConfigFileOptions {\n\t\tconst result = this.cloneConfig(config);\n\n\t\tif (process.env.TRACELATTICE_MAX_HISTORY_SIZE !== undefined) {\n\t\t\tresult.maxHistorySize = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_HISTORY_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_HISTORY_SIZE\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_MAX_BRANCHES !== undefined) {\n\t\t\tresult.maxBranches = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_BRANCHES',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_BRANCHES\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_MAX_BRANCH_SIZE !== undefined) {\n\t\t\tresult.maxBranchSize = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_MAX_BRANCH_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_MAX_BRANCH_SIZE\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tprocess.env.TRACELATTICE_LOG_LEVEL &&\n\t\t\t['debug', 'info', 'warn', 'error'].includes(process.env.TRACELATTICE_LOG_LEVEL)\n\t\t) {\n\t\t\tresult.logLevel = process.env.TRACELATTICE_LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error';\n\t\t}\n\t\tif (process.env.TRACELATTICE_PRETTY_LOG === 'false') {\n\t\t\tresult.prettyLog = false;\n\t\t}\n\t\tif (process.env.TRACELATTICE_SKILL_DIRS !== undefined) {\n\t\t\tresult.skillDirs =\n\t\t\t\tprocess.env.TRACELATTICE_SKILL_DIRS === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: process.env.TRACELATTICE_SKILL_DIRS.split(':');\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_DIRS !== undefined) {\n\t\t\tresult.toolDirs =\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_DIRS === ''\n\t\t\t\t\t? []\n\t\t\t\t\t: process.env.TRACELATTICE_TOOL_DIRS.split(':');\n\t\t}\n\t\tif (process.env.TRACELATTICE_DISCOVERY_CACHE_TTL !== undefined) {\n\t\t\tconst seconds = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_TTL',\n\t\t\t\tprocess.env.TRACELATTICE_DISCOVERY_CACHE_TTL\n\t\t\t);\n\t\t\tconst ttl = seconds * 1000;\n\t\t\tif (!Number.isSafeInteger(ttl)) {\n\t\t\t\tthrow new ConfigurationError(\n\t\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_TTL exceeds the safe integer range'\n\t\t\t\t);\n\t\t\t}\n\t\t\tresult.discoveryCache = { ...result.discoveryCache, ttl };\n\t\t}\n\t\tif (process.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE !== undefined) {\n\t\t\tconst maxSize = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE',\n\t\t\t\tprocess.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE\n\t\t\t);\n\t\t\tresult.discoveryCache = { ...result.discoveryCache, maxSize };\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS !== undefined) {\n\t\t\tresult.toolInterleaveTtlMs = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_TOOL_INTERLEAVE_TTL_MS',\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS !== undefined) {\n\t\t\tresult.toolInterleaveSweepMs = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS',\n\t\t\t\tprocess.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS\n\t\t\t);\n\t\t}\n\t\tif (process.env.TRACELATTICE_SESSION_MAX_PER_OWNER !== undefined) {\n\t\t\tresult.maxSessionsPerOwner = parseEnvironmentInteger(\n\t\t\t\t'TRACELATTICE_SESSION_MAX_PER_OWNER',\n\t\t\t\tprocess.env.TRACELATTICE_SESSION_MAX_PER_OWNER\n\t\t\t);\n\t\t}\n\n\t\tthis.applyFeatureFlagOverrides(result);\n\n\t\treturn result;\n\t}\n\n\tprivate cloneConfig(config: ConfigFileOptions): Mutable<ConfigFileOptions> {\n\t\treturn {\n\t\t\t...config,\n\t\t\t...(config.skillDirs === undefined ? {} : { skillDirs: [...config.skillDirs] }),\n\t\t\t...(config.toolDirs === undefined ? {} : { toolDirs: [...config.toolDirs] }),\n\t\t\t...(config.discoveryCache === undefined\n\t\t\t\t? {}\n\t\t\t\t: { discoveryCache: { ...config.discoveryCache } }),\n\t\t\t...(config.persistence === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tpersistence: {\n\t\t\t\t\t\t\t...config.persistence,\n\t\t\t\t\t\t\t...(config.persistence.options === undefined\n\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t: { options: { ...config.persistence.options } }),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t...(config.features === undefined ? {} : { features: { ...config.features } }),\n\t\t};\n\t}\n\n\t/**\n\t * Applies TRACELATTICE_FEATURES_* environment variable overrides for feature flags.\n\t * Booleans accept 'true'/'false'/'1'/'0' (case-insensitive).\n\t * Invalid reasoningStrategy values are rejected.\n\t *\n\t * @param result - Configuration object to mutate with feature flag overrides\n\t * @private\n\t */\n\tprivate applyFeatureFlagOverrides(result: Mutable<ConfigFileOptions>): void {\n\t\tconst boolMap: Record<string, Exclude<keyof FeatureFlags, 'reasoningStrategy'>> = {\n\t\t\tTRACELATTICE_FEATURES_DAG_EDGES: 'dagEdges',\n\t\t\tTRACELATTICE_FEATURES_CALIBRATION: 'calibration',\n\t\t\tTRACELATTICE_FEATURES_COMPRESSION: 'compression',\n\t\t\tTRACELATTICE_FEATURES_TOOL_INTERLEAVE: 'toolInterleave',\n\t\t\tTRACELATTICE_FEATURES_NEW_THOUGHT_TYPES: 'newThoughtTypes',\n\t\t\tTRACELATTICE_FEATURES_OUTCOME_RECORDING: 'outcomeRecording',\n\t\t};\n\t\tfor (const [envVar, key] of Object.entries(boolMap)) {\n\t\t\tconst raw = process.env[envVar];\n\t\t\tif (raw === undefined) continue;\n\t\t\tconst parsed = this.parseBoolean(raw);\n\t\t\tif (parsed === undefined) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Invalid boolean value for ${envVar}: \"${raw}\" (expected true/false/1/0). Ignoring.`\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst features: { -readonly [K in keyof FeatureFlags]?: FeatureFlags[K] } =\n\t\t\t\tresult.features ?? {};\n\t\t\tfeatures[key] = parsed;\n\t\t\tresult.features = features;\n\t\t}\n\n\t\tconst strategyRaw = process.env.TRACELATTICE_FEATURES_REASONING_STRATEGY;\n\t\tif (strategyRaw !== undefined) {\n\t\t\tif (strategyRaw !== 'sequential' && strategyRaw !== 'tot') {\n\t\t\t\tthrow new ConfigurationError(\n\t\t\t\t\t`TRACELATTICE_FEATURES_REASONING_STRATEGY must be one of sequential, tot, got ${strategyRaw}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst features: { -readonly [K in keyof FeatureFlags]?: FeatureFlags[K] } =\n\t\t\t\tresult.features ?? {};\n\t\t\tfeatures.reasoningStrategy = strategyRaw;\n\t\t\tresult.features = features;\n\t\t}\n\t}\n\n\t/**\n\t * Parses a boolean from an environment variable string.\n\t * Accepts 'true'/'false'/'1'/'0' case-insensitively.\n\t *\n\t * @param raw - Raw environment variable string\n\t * @returns Parsed boolean, or undefined if the value is invalid\n\t * @private\n\t */\n\tprivate parseBoolean(raw: string): boolean | undefined {\n\t\tconst v = raw.trim().toLowerCase();\n\t\tif (v === 'true' || v === '1') return true;\n\t\tif (v === 'false' || v === '0') return false;\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Parses a configuration file (JSON or YAML).\n\t *\n\t * Detects the file type by extension and uses the appropriate parser.\n\t * Supports `.json`, `.yaml`, and `.yml` file extensions.\n\t *\n\t * @param filePath - Path to the configuration file to parse\n\t * @returns The parsed configuration object\n\t * @throws {Error} If the file cannot be parsed\n\t * @private\n\t */\n\tprivate parseConfig(filePath: string): ConfigFileOptions {\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\tconst ext = filePath.split('.').pop()?.toLowerCase();\n\n\t\tconst raw: unknown = ext === 'yaml' || ext === 'yml' ? parseYaml(content) : JSON.parse(content);\n\n\t\treturn v.parse(ConfigFileOptionsSchema, raw) as ConfigFileOptions;\n\t}\n\n\t/**\n\t * Converts file-based configuration to ServerConfig options.\n\t *\n\t * This is a convenience method for extracting the ServerConfig-relevant\n\t * options from a file-based configuration.\n\t *\n\t * @param config - The configuration to convert\n\t * @returns An object with ServerConfig-compatible options\n\t *\n\t * @example\n\t * ```typescript\n\t * const loader = new ConfigLoader();\n\t * const config = loader.load();\n\t * if (config) {\n\t * const serverOpts = loader.toServerConfigOptions(config);\n\t * const serverConfig = new ServerConfig(serverOpts);\n\t * }\n\t * ```\n\t */\n\ttoServerConfigOptions(config: ConfigFileOptions): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: config.maxHistorySize,\n\t\t\tmaxBranches: config.maxBranches,\n\t\t\tmaxBranchSize: config.maxBranchSize,\n\t\t\tskillDirs: config.skillDirs,\n\t\t\ttoolDirs: config.toolDirs,\n\t\t\tdiscoveryCache: config.discoveryCache,\n\t\t\tpersistence: config.persistence,\n\t\t\tpersistenceBufferSize: config.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: config.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: config.persistenceMaxRetries,\n\t\t\tfeatures: config.features,\n\t\t\ttoolInterleaveTtlMs: config.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: config.toolInterleaveSweepMs,\n\t\t\tmaxSessionsPerOwner: config.maxSessionsPerOwner,\n\t\t};\n\t}\n}\n"],"names":["ConfigFileOptionsSchema","v","ConfigLoader","customPath","configuredPath","process","join","homedir","config","configPath","existsSync","error","console","getErrorMessage","result","undefined","parseEnvironmentInteger","seconds","ttl","Number","ConfigurationError","maxSize","boolMap","envVar","key","Object","raw","parsed","features","strategyRaw","filePath","content","readFileSync","ext","parseYaml","JSON"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnvironmentInteger.d.ts","sourceRoot":"","sources":["../../src/config/EnvironmentInteger.ts"],"names":[],"mappings":"AAEA,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CASpF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConfigurationError } from "../errors.js";
|
|
2
|
+
function parseEnvironmentInteger(environmentName, raw) {
|
|
3
|
+
if (!/^(0|[1-9]\d*)$/.test(raw)) throw new ConfigurationError(`${environmentName} must be an integer, got ${raw}`);
|
|
4
|
+
const parsed = Number(raw);
|
|
5
|
+
if (!Number.isSafeInteger(parsed)) throw new ConfigurationError(`${environmentName} must be a safe integer, got ${raw}`);
|
|
6
|
+
return parsed;
|
|
7
|
+
}
|
|
8
|
+
export { parseEnvironmentInteger };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=EnvironmentInteger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config/EnvironmentInteger.js","sources":["../../src/config/EnvironmentInteger.ts"],"sourcesContent":["import { ConfigurationError } from '../errors.js';\n\nexport function parseEnvironmentInteger(environmentName: string, raw: string): number {\n\tif (!/^(0|[1-9]\\d*)$/.test(raw)) {\n\t\tthrow new ConfigurationError(`${environmentName} must be an integer, got ${raw}`);\n\t}\n\tconst parsed = Number(raw);\n\tif (!Number.isSafeInteger(parsed)) {\n\t\tthrow new ConfigurationError(`${environmentName} must be a safe integer, got ${raw}`);\n\t}\n\treturn parsed;\n}\n"],"names":["parseEnvironmentInteger","environmentName","raw","ConfigurationError","parsed","Number"],"mappings":";AAEO,SAASA,wBAAwBC,eAAuB,EAAEC,GAAW;IAC3E,IAAI,CAAC,iBAAiB,IAAI,CAACA,MAC1B,MAAM,IAAIC,mBAAmB,GAAGF,gBAAgB,yBAAyB,EAAEC,KAAK;IAEjF,MAAME,SAASC,OAAOH;IACtB,IAAI,CAACG,OAAO,aAAa,CAACD,SACzB,MAAM,IAAID,mBAAmB,GAAGF,gBAAgB,6BAA6B,EAAEC,KAAK;IAErF,OAAOE;AACR"}
|