@iworkforces/tracelattice 2.3.1 → 2.3.5

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.
Files changed (32) hide show
  1. package/README.md +13 -0
  2. package/dist/cli.js +90 -12
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/CliTransportConfig.d.ts +22 -0
  5. package/dist/config/CliTransportConfig.d.ts.map +1 -0
  6. package/dist/config/CliTransportConfig.js +52 -0
  7. package/dist/config/CliTransportConfig.js.map +1 -0
  8. package/dist/config/ConfigLoader.d.ts +0 -1
  9. package/dist/config/ConfigLoader.d.ts.map +1 -1
  10. package/dist/config/ConfigLoader.js +9 -14
  11. package/dist/config/ConfigLoader.js.map +1 -1
  12. package/dist/config/EnvironmentInteger.d.ts +2 -0
  13. package/dist/config/EnvironmentInteger.d.ts.map +1 -0
  14. package/dist/config/EnvironmentInteger.js +10 -0
  15. package/dist/config/EnvironmentInteger.js.map +1 -0
  16. package/dist/lib.d.ts +12 -2
  17. package/dist/lib.d.ts.map +1 -1
  18. package/dist/lib.js +9 -4
  19. package/dist/lib.js.map +1 -1
  20. package/dist/persistence/BunSqliteDriver.d.ts +11 -0
  21. package/dist/persistence/BunSqliteDriver.d.ts.map +1 -0
  22. package/dist/persistence/BunSqliteDriver.js +36 -0
  23. package/dist/persistence/BunSqliteDriver.js.map +1 -0
  24. package/dist/persistence/SqliteDriverLoader.d.ts +11 -0
  25. package/dist/persistence/SqliteDriverLoader.d.ts.map +1 -0
  26. package/dist/persistence/SqliteDriverLoader.js +19 -0
  27. package/dist/persistence/SqliteDriverLoader.js.map +1 -0
  28. package/dist/persistence/SqlitePersistence.d.ts +2 -1
  29. package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
  30. package/dist/persistence/SqlitePersistence.js +4 -9
  31. package/dist/persistence/SqlitePersistence.js.map +1 -1
  32. package/package.json +2 -2
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
@@ -778,6 +778,7 @@ class PersistenceClosedError extends SequentialThinkingError {
778
778
 
779
779
  __webpack_require__.d(__webpack_exports__, {
780
780
  Qt: () => (CliShutdownTimeoutError),
781
+ j1: () => (ConfigurationError),
781
782
  u1: () => (getErrorMessage),
782
783
  yI: () => (ValidationError)
783
784
  });
@@ -2825,6 +2826,79 @@ function createCliShutdownHandler(lifecycle, boundary) {
2825
2826
  };
2826
2827
  }
2827
2828
 
2829
+ ;// CONCATENATED MODULE: ./src/config/EnvironmentInteger.ts
2830
+
2831
+ function parseEnvironmentInteger(environmentName, raw) {
2832
+ if (!/^(0|[1-9]\d*)$/.test(raw)) {
2833
+ throw new errors/* .ConfigurationError */.j1(`${environmentName} must be an integer, got ${raw}`);
2834
+ }
2835
+ const parsed = Number(raw);
2836
+ if (!Number.isSafeInteger(parsed)) {
2837
+ throw new errors/* .ConfigurationError */.j1(`${environmentName} must be a safe integer, got ${raw}`);
2838
+ }
2839
+ return parsed;
2840
+ }
2841
+
2842
+ ;// CONCATENATED MODULE: ./src/config/CliTransportConfig.ts
2843
+
2844
+
2845
+ const DEFAULT_STREAMABLE_HTTP_PORT = 9007;
2846
+ // Node and Bun clamp larger setInterval delays to 1 ms.
2847
+ const MAX_SESSION_SWEEP_INTERVAL_MS = 2147483647;
2848
+ const retentionNames = [
2849
+ 'TRACELATTICE_STREAMABLE_HTTP_MAX_SESSIONS',
2850
+ 'TRACELATTICE_STREAMABLE_HTTP_SESSION_IDLE_TIMEOUT_MS',
2851
+ 'TRACELATTICE_STREAMABLE_HTTP_SESSION_SWEEP_INTERVAL_MS'
2852
+ ];
2853
+ function positiveRetentionInteger(env, name) {
2854
+ const raw = env[name];
2855
+ if (raw === undefined) throw new errors/* .ConfigurationError */.j1(`Missing ${name}`);
2856
+ const value = parseEnvironmentInteger(name, raw);
2857
+ if (value === 0) throw new errors/* .ConfigurationError */.j1(`${name} must be greater than zero`);
2858
+ return value;
2859
+ }
2860
+ function parseRetention(env, stateful) {
2861
+ const missing = retentionNames.filter((name)=>env[name] === undefined);
2862
+ if (missing.length === retentionNames.length) return undefined;
2863
+ if (missing.length > 0) {
2864
+ throw new errors/* .ConfigurationError */.j1(`Missing Streamable HTTP retention variables: ${missing.join(', ')}`);
2865
+ }
2866
+ if (!stateful) {
2867
+ throw new errors/* .ConfigurationError */.j1('Streamable HTTP retention requires TRACELATTICE_STREAMABLE_HTTP_STATEFUL to not be false');
2868
+ }
2869
+ const retention = {
2870
+ maxSessions: positiveRetentionInteger(env, retentionNames[0]),
2871
+ sessionIdleTimeoutMs: positiveRetentionInteger(env, retentionNames[1]),
2872
+ sessionSweepIntervalMs: positiveRetentionInteger(env, retentionNames[2])
2873
+ };
2874
+ if (retention.sessionSweepIntervalMs > MAX_SESSION_SWEEP_INTERVAL_MS) {
2875
+ throw new errors/* .ConfigurationError */.j1(`${retentionNames[2]} must be at most ${MAX_SESSION_SWEEP_INTERVAL_MS}`);
2876
+ }
2877
+ return retention;
2878
+ }
2879
+ function parseCliTransportConfig(env) {
2880
+ const kind = env.TRACELATTICE_TRANSPORT_TYPE ?? 'stdio';
2881
+ if (kind === 'stdio') return {
2882
+ kind
2883
+ };
2884
+ if (kind !== 'streamable-http') {
2885
+ throw new errors/* .ConfigurationError */.j1(`TRACELATTICE_TRANSPORT_TYPE must be one of stdio, streamable-http, got ${kind}`);
2886
+ }
2887
+ const port = parseEnvironmentInteger('TRACELATTICE_STREAMABLE_HTTP_PORT', env.TRACELATTICE_STREAMABLE_HTTP_PORT ?? String(DEFAULT_STREAMABLE_HTTP_PORT));
2888
+ if (port > 65535) throw new errors/* .ConfigurationError */.j1('TRACELATTICE_STREAMABLE_HTTP_PORT must be between 0 and 65535');
2889
+ const stateful = env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';
2890
+ return {
2891
+ kind,
2892
+ port,
2893
+ host: env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost',
2894
+ corsOrigin: env.TRACELATTICE_CORS_ORIGIN || '*',
2895
+ enableCors: env.TRACELATTICE_ENABLE_CORS !== 'false',
2896
+ allowedHosts: env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue)=>hostValue.trim()),
2897
+ stateful,
2898
+ retention: parseRetention(env, stateful)
2899
+ };
2900
+ }
2901
+
2828
2902
  ;// CONCATENATED MODULE: external "./lib.js"
2829
2903
 
2830
2904
  // EXTERNAL MODULE: ./src/context/RequestContext.ts + 1 modules
@@ -3102,6 +3176,7 @@ var schema = __webpack_require__("R");
3102
3176
 
3103
3177
 
3104
3178
 
3179
+
3105
3180
  // Get version from package.json
3106
3181
  const CLI_NAME = 'tracelattice';
3107
3182
  const cli_filename = (0,__rspack_external_node_url_3991086a.fileURLToPath)(import.meta.url);
@@ -3116,7 +3191,6 @@ if (shouldShowVersion) {
3116
3191
  process.exit(0);
3117
3192
  }
3118
3193
  async function main() {
3119
- const transportType = process.env.TRACELATTICE_TRANSPORT_TYPE || 'stdio';
3120
3194
  const adapter = new __rspack_external__tmcp_adapter_valibot_0faddfa6.ValibotJsonSchemaAdapter();
3121
3195
  const server = new __rspack_external_tmcp.McpServer({
3122
3196
  name: CLI_NAME,
@@ -3133,37 +3207,41 @@ async function main() {
3133
3207
  const thinkingServer = await (0,__rspack_external__lib_js_1645bc55.initializeServer)();
3134
3208
  const lifecycle = new CliLifecycle(thinkingServer);
3135
3209
  try {
3210
+ const transportConfig = parseCliTransportConfig(process.env);
3136
3211
  server.tool({
3137
3212
  name: 'sequentialthinking_tools',
3138
3213
  description: schema/* .SEQUENTIAL_THINKING_TOOL.description */.iV.description,
3139
3214
  schema: schema/* .SequentialThinkingSchema */.ZK
3140
3215
  }, async (input)=>thinkingServer.processThought(input));
3141
- if (transportType === 'streamable-http') {
3142
- await startStreamableHttpTransport(server, thinkingServer, lifecycle);
3216
+ if (transportConfig.kind === 'streamable-http') {
3217
+ await startStreamableHttpTransport(server, thinkingServer, {
3218
+ lifecycle,
3219
+ config: transportConfig
3220
+ });
3143
3221
  } else {
3144
3222
  await startStdioTransport(server, thinkingServer, lifecycle);
3145
3223
  }
3146
- registerShutdownHandlers(lifecycle, thinkingServer, transportType === 'stdio');
3224
+ registerShutdownHandlers(lifecycle, thinkingServer, transportConfig.kind === 'stdio');
3147
3225
  } catch (error) {
3148
3226
  await lifecycle.rollbackStartup(error);
3149
3227
  }
3150
3228
  }
3151
3229
  /**
3152
3230
  * Start Streamable HTTP transport (MCP spec recommended)
3153
- */ async function startStreamableHttpTransport(server, thinkingServer, lifecycle) {
3231
+ */ async function startStreamableHttpTransport(server, thinkingServer, options) {
3154
3232
  const { StreamableHttpTransport } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "q"));
3155
- const port = parseInt(process.env.TRACELATTICE_STREAMABLE_HTTP_PORT || '9007', 10);
3156
- const host = process.env.TRACELATTICE_STREAMABLE_HTTP_HOST || 'localhost';
3233
+ const { lifecycle, config } = options;
3234
+ const { port, host } = config;
3157
3235
  const transportMetrics = thinkingServer.getContainer().resolve('Metrics');
3158
- const stateful = process.env.TRACELATTICE_STREAMABLE_HTTP_STATEFUL !== 'false';
3159
3236
  const streamableTransport = new StreamableHttpTransport({
3160
3237
  port,
3161
3238
  host,
3162
- corsOrigin: process.env.TRACELATTICE_CORS_ORIGIN || '*',
3163
- enableCors: process.env.TRACELATTICE_ENABLE_CORS !== 'false',
3164
- allowedHosts: process.env.TRACELATTICE_ALLOWED_HOSTS?.split(',').map((hostValue)=>hostValue.trim()),
3239
+ corsOrigin: config.corsOrigin,
3240
+ enableCors: config.enableCors,
3241
+ allowedHosts: config.allowedHosts,
3165
3242
  metrics: transportMetrics,
3166
- stateful
3243
+ stateful: config.stateful,
3244
+ ...config.retention
3167
3245
  });
3168
3246
  lifecycle.attachTransport(streamableTransport);
3169
3247
  // 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;AAiC9D;;;;;;;;;;;;;;;;;;;;;;;;;;;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,OAAO,CAAC,uBAAuB;IAW/B;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAOpB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IASnB;;;;;;;;;;;;;;;;;;OAkBG;IACH,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,mBAAmB;CAkBrE"}
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 = this.parseEnvironmentInteger('TRACELATTICE_MAX_HISTORY_SIZE', process.env.TRACELATTICE_MAX_HISTORY_SIZE);
61
- if (void 0 !== process.env.TRACELATTICE_MAX_BRANCHES) result.maxBranches = this.parseEnvironmentInteger('TRACELATTICE_MAX_BRANCHES', process.env.TRACELATTICE_MAX_BRANCHES);
62
- if (void 0 !== process.env.TRACELATTICE_MAX_BRANCH_SIZE) result.maxBranchSize = this.parseEnvironmentInteger('TRACELATTICE_MAX_BRANCH_SIZE', process.env.TRACELATTICE_MAX_BRANCH_SIZE);
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 = this.parseEnvironmentInteger('TRACELATTICE_DISCOVERY_CACHE_TTL', process.env.TRACELATTICE_DISCOVERY_CACHE_TTL);
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 = this.parseEnvironmentInteger('TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE', process.env.TRACELATTICE_DISCOVERY_CACHE_MAX_SIZE);
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 = this.parseEnvironmentInteger('TRACELATTICE_TOOL_INTERLEAVE_TTL_MS', process.env.TRACELATTICE_TOOL_INTERLEAVE_TTL_MS);
89
- if (void 0 !== process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS) result.toolInterleaveSweepMs = this.parseEnvironmentInteger('TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS', process.env.TRACELATTICE_TOOL_INTERLEAVE_SWEEP_MS);
90
- if (void 0 !== process.env.TRACELATTICE_SESSION_MAX_PER_OWNER) result.maxSessionsPerOwner = this.parseEnvironmentInteger('TRACELATTICE_SESSION_MAX_PER_OWNER', process.env.TRACELATTICE_SESSION_MAX_PER_OWNER);
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,2 @@
1
+ export declare function parseEnvironmentInteger(environmentName: string, raw: string): number;
2
+ //# sourceMappingURL=EnvironmentInteger.d.ts.map
@@ -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"}