@poolzin/pool-bot 2026.3.7 → 2026.3.9

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 (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/.buildstamp +1 -1
  3. package/dist/agents/error-classifier.js +302 -0
  4. package/dist/agents/skills/security.js +217 -0
  5. package/dist/build-info.json +3 -3
  6. package/dist/cli/lazy-commands.example.js +113 -0
  7. package/dist/cli/lazy-commands.js +329 -0
  8. package/dist/cli/program/command-registry.js +13 -0
  9. package/dist/cli/program/register.skills.js +4 -0
  10. package/dist/config/config.js +1 -0
  11. package/dist/config/secrets-integration.js +88 -0
  12. package/dist/context-engine/index.js +33 -0
  13. package/dist/context-engine/legacy.js +181 -0
  14. package/dist/context-engine/registry.js +86 -0
  15. package/dist/context-engine/summarizing.js +293 -0
  16. package/dist/context-engine/types.js +7 -0
  17. package/dist/infra/abort-pattern.js +106 -0
  18. package/dist/infra/retry.js +94 -0
  19. package/dist/secrets/index.js +28 -0
  20. package/dist/secrets/resolver.js +185 -0
  21. package/dist/secrets/runtime.js +142 -0
  22. package/dist/secrets/types.js +11 -0
  23. package/dist/security/dangerous-tools.js +80 -0
  24. package/dist/security/types.js +12 -0
  25. package/dist/skills/commands.js +351 -0
  26. package/dist/skills/index.js +167 -0
  27. package/dist/skills/loader.js +282 -0
  28. package/dist/skills/parser.js +461 -0
  29. package/dist/skills/registry.js +397 -0
  30. package/dist/skills/security.js +318 -0
  31. package/dist/skills/types.js +21 -0
  32. package/dist/test-utils/index.js +219 -0
  33. package/dist/tui/index.js +595 -0
  34. package/docs/INTEGRATION_PLAN.md +475 -0
  35. package/docs/INTEGRATION_SUMMARY.md +215 -0
  36. package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
  37. package/docs/integrations/INTEGRATION_PLAN.md +424 -0
  38. package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
  39. package/docs/integrations/XYOPS_PLAN.md +978 -0
  40. package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
  41. package/docs/skills/SKILL.md +524 -0
  42. package/docs/skills.md +405 -0
  43. package/package.json +1 -1
  44. package/skills/example-skill/SKILL.md +195 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Context Engine Registry
3
+ *
4
+ * Manages available context engines and provides
5
+ * factory methods for creating engine instances.
6
+ */
7
+ export class ContextEngineRegistry {
8
+ engines = new Map();
9
+ instances = new Map();
10
+ /**
11
+ * Register a new context engine
12
+ */
13
+ register(id, EngineClass) {
14
+ this.engines.set(id, EngineClass);
15
+ }
16
+ /**
17
+ * Check if an engine is registered
18
+ */
19
+ has(id) {
20
+ return this.engines.has(id);
21
+ }
22
+ /**
23
+ * Get available engine IDs
24
+ */
25
+ getAvailableEngines() {
26
+ return Array.from(this.engines.keys());
27
+ }
28
+ /**
29
+ * Create or get a cached engine instance
30
+ */
31
+ getEngine(id) {
32
+ // Return cached instance if exists
33
+ const cached = this.instances.get(id);
34
+ if (cached) {
35
+ return cached;
36
+ }
37
+ // Create new instance
38
+ const EngineClass = this.engines.get(id);
39
+ if (!EngineClass) {
40
+ throw new Error(`Context engine "${id}" not registered. Available: ${this.getAvailableEngines().join(", ")}`);
41
+ }
42
+ const instance = new EngineClass();
43
+ this.instances.set(id, instance);
44
+ return instance;
45
+ }
46
+ /**
47
+ * Remove cached instance (for cleanup/testing)
48
+ */
49
+ clearCache(id) {
50
+ if (id) {
51
+ this.instances.delete(id);
52
+ }
53
+ else {
54
+ this.instances.clear();
55
+ }
56
+ }
57
+ /**
58
+ * Get engine info for all registered engines
59
+ */
60
+ getEngineInfos() {
61
+ return this.getAvailableEngines().map((id) => {
62
+ const engine = this.getEngine(id);
63
+ return {
64
+ id,
65
+ name: engine.info.name,
66
+ description: engine.info.description,
67
+ };
68
+ });
69
+ }
70
+ }
71
+ /**
72
+ * Global registry instance
73
+ */
74
+ export const globalContextEngineRegistry = new ContextEngineRegistry();
75
+ /**
76
+ * Resolve which engine to use based on configuration
77
+ */
78
+ export function resolveContextEngine(sessionId, config, registry = globalContextEngineRegistry) {
79
+ // Check for session-specific override
80
+ const engineId = config.sessionEngines?.[sessionId] ?? config.defaultEngine;
81
+ if (!registry.has(engineId)) {
82
+ throw new Error(`Context engine "${engineId}" not found for session ${sessionId}. ` +
83
+ `Available engines: ${registry.getAvailableEngines().join(", ")}`);
84
+ }
85
+ return registry.getEngine(engineId);
86
+ }
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Summarizing Context Engine
3
+ *
4
+ * Advanced context management with intelligent summarization.
5
+ * Provides better token efficiency than the legacy engine.
6
+ *
7
+ * Features:
8
+ * - Sliding window with summarization
9
+ * - Token-aware message prioritization
10
+ * - Configurable summarization strategy
11
+ */
12
+ /**
13
+ * Token estimator using character-based approximation
14
+ * More accurate than simple division
15
+ */
16
+ function estimateTokens(text) {
17
+ // GPT-style tokenization approximation
18
+ // - Average English word: ~1.3 tokens
19
+ // - Whitespace and punctuation: ~0.5 tokens each
20
+ // - Code/non-English: ~2-3 tokens per word
21
+ const words = text.trim().split(/\s+/).length;
22
+ const chars = text.length;
23
+ const punctuation = (text.match(/[.,!?;:()[\]{}"']/g) ?? []).length;
24
+ // Heuristic: code detection (high special char ratio)
25
+ const specialChars = (text.match(/[^\w\s]/g) ?? []).length;
26
+ const isCode = specialChars / chars > 0.15;
27
+ if (isCode) {
28
+ return Math.ceil(chars / 2.5); // Code: ~2.5 chars/token
29
+ }
30
+ // Regular text estimation
31
+ const wordTokens = words * 1.3;
32
+ const punctTokens = punctuation * 0.5;
33
+ const overhead = Math.ceil(chars / 100); // Metadata overhead
34
+ return Math.ceil(wordTokens + punctTokens + overhead);
35
+ }
36
+ /**
37
+ * Summarizing Context Engine
38
+ *
39
+ * Implements sliding window with intelligent summarization:
40
+ * 1. Keep system messages (highest priority)
41
+ * 2. Keep recent conversation (high priority)
42
+ * 3. Summarize older messages into running summary
43
+ */
44
+ export class SummarizingContextEngine {
45
+ info = {
46
+ id: "summarizing",
47
+ name: "Summarizing Context Engine",
48
+ description: "Sliding window with intelligent message summarization",
49
+ supportsVectorSearch: false,
50
+ supportsFullTextSearch: false,
51
+ tokenLimit: undefined,
52
+ };
53
+ config;
54
+ sessions = new Map();
55
+ constructor(config) {
56
+ this.config = {
57
+ maxTokens: config?.maxTokens ?? 8000,
58
+ keepRecentMessages: config?.keepRecentMessages ?? 6,
59
+ summarizationThreshold: config?.summarizationThreshold ?? 4000,
60
+ };
61
+ }
62
+ async bootstrap(params) {
63
+ try {
64
+ this.sessions.set(params.sessionId, {
65
+ messages: [],
66
+ summary: undefined,
67
+ summaryTokens: 0,
68
+ lastAccessed: new Date(),
69
+ totalIngested: 0,
70
+ });
71
+ return { success: true };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ success: false,
76
+ error: error instanceof Error ? error.message : String(error),
77
+ };
78
+ }
79
+ }
80
+ async ingest(params) {
81
+ try {
82
+ const session = this.sessions.get(params.sessionId);
83
+ if (!session) {
84
+ return {
85
+ success: false,
86
+ error: `Session ${params.sessionId} not found`,
87
+ };
88
+ }
89
+ const content = typeof params.message.content === "string"
90
+ ? params.message.content
91
+ : JSON.stringify(params.message.content);
92
+ const estimatedTokens = estimateTokens(content);
93
+ // Calculate priority
94
+ const priority = this.calculatePriority(params.message, session.messages.length);
95
+ const prioritized = {
96
+ ...params.message,
97
+ priority,
98
+ estimatedTokens,
99
+ internalTimestamp: Date.now(),
100
+ };
101
+ session.messages.push(prioritized);
102
+ session.totalIngested++;
103
+ session.lastAccessed = new Date();
104
+ // Check if we need to compact
105
+ const currentTokens = this.getTotalTokenCount(session);
106
+ if (currentTokens > this.config.summarizationThreshold) {
107
+ await this.compact({ sessionId: params.sessionId });
108
+ }
109
+ return {
110
+ success: true,
111
+ tokensConsumed: estimatedTokens,
112
+ };
113
+ }
114
+ catch (error) {
115
+ return {
116
+ success: false,
117
+ error: error instanceof Error ? error.message : String(error),
118
+ };
119
+ }
120
+ }
121
+ async assemble(params) {
122
+ const session = this.sessions.get(params.sessionId);
123
+ if (!session) {
124
+ return {
125
+ messages: params.messages ?? [],
126
+ totalTokens: 0,
127
+ wasTruncated: false,
128
+ };
129
+ }
130
+ // Build context: summary + recent messages
131
+ const resultMessages = [];
132
+ let totalTokens = 0;
133
+ // Add summary if exists
134
+ if (session.summary) {
135
+ const summaryMsg = {
136
+ role: "system",
137
+ content: `[Previous conversation summary: ${session.summary}]`,
138
+ metadata: { isSummary: true },
139
+ };
140
+ resultMessages.push(summaryMsg);
141
+ totalTokens += session.summaryTokens;
142
+ }
143
+ // Add messages within budget
144
+ const budget = params.tokenBudget ?? this.config.maxTokens;
145
+ const sortedMessages = [...session.messages].sort((a, b) => b.priority - a.priority || a.internalTimestamp - b.internalTimestamp);
146
+ const included = [];
147
+ const excluded = [];
148
+ for (const msg of sortedMessages) {
149
+ if (totalTokens + msg.estimatedTokens <= budget) {
150
+ // Strip priority metadata before returning
151
+ const { priority: _, estimatedTokens: __, internalTimestamp: ___, ...cleanMsg } = msg;
152
+ included.push(cleanMsg);
153
+ totalTokens += msg.estimatedTokens;
154
+ }
155
+ else {
156
+ const { priority: _, estimatedTokens: __, internalTimestamp: ___, ...cleanMsg } = msg;
157
+ excluded.push(cleanMsg);
158
+ }
159
+ }
160
+ // Sort by internalTimestamp for final output
161
+ included.sort((a, b) => {
162
+ const ta = session.messages.find(m => m.id === a.id)?.internalTimestamp ?? 0;
163
+ const tb = session.messages.find(m => m.id === b.id)?.internalTimestamp ?? 0;
164
+ return ta - tb;
165
+ });
166
+ resultMessages.push(...included);
167
+ return {
168
+ messages: resultMessages,
169
+ totalTokens,
170
+ wasTruncated: excluded.length > 0,
171
+ excludedMessages: excluded.length > 0 ? excluded : undefined,
172
+ };
173
+ }
174
+ async compact(params) {
175
+ try {
176
+ const session = this.sessions.get(params.sessionId);
177
+ if (!session || session.messages.length <= this.config.keepRecentMessages) {
178
+ return { success: true, tokensSaved: 0 };
179
+ }
180
+ const originalTokens = this.getTotalTokenCount(session);
181
+ // Messages to summarize (older ones)
182
+ const toSummarize = session.messages.slice(0, -this.config.keepRecentMessages);
183
+ const toKeep = session.messages.slice(-this.config.keepRecentMessages);
184
+ if (toSummarize.length === 0) {
185
+ return { success: true, tokensSaved: 0 };
186
+ }
187
+ // Generate summary (simplified - in production, use LLM)
188
+ const summary = this.generateSummary(toSummarize);
189
+ const _summaryTokens = estimateTokens(summary);
190
+ // Update session
191
+ session.summary = session.summary
192
+ ? `${session.summary} ${summary}`
193
+ : summary;
194
+ session.summaryTokens = estimateTokens(session.summary);
195
+ session.messages = toKeep;
196
+ const newTokens = this.getTotalTokenCount(session);
197
+ const tokensSaved = originalTokens - newTokens;
198
+ return {
199
+ success: true,
200
+ summary: session.summary,
201
+ tokensSaved: Math.max(0, tokensSaved),
202
+ };
203
+ }
204
+ catch (error) {
205
+ return {
206
+ success: false,
207
+ error: error instanceof Error ? error.message : String(error),
208
+ };
209
+ }
210
+ }
211
+ async cleanup(params) {
212
+ this.sessions.delete(params.sessionId);
213
+ }
214
+ async getStats(params) {
215
+ const session = this.sessions.get(params.sessionId);
216
+ if (!session) {
217
+ return { messageCount: 0 };
218
+ }
219
+ return {
220
+ messageCount: session.messages.length,
221
+ totalTokens: this.getTotalTokenCount(session),
222
+ lastAccessed: session.lastAccessed,
223
+ };
224
+ }
225
+ /**
226
+ * Calculate message priority (0-100)
227
+ */
228
+ calculatePriority(message, index) {
229
+ let priority = 50; // Base priority
230
+ // System messages are highest priority
231
+ if (message.role === "system") {
232
+ priority += 40;
233
+ }
234
+ // Recent messages get boost
235
+ if (index > -5) {
236
+ priority += 20;
237
+ }
238
+ // Messages with tool calls/results are important
239
+ if (message.metadata?.tool_calls ||
240
+ message.metadata?.tool_call_id) {
241
+ priority += 15;
242
+ }
243
+ return Math.min(100, priority);
244
+ }
245
+ /**
246
+ * Get total token count for session
247
+ */
248
+ getTotalTokenCount(session) {
249
+ const messageTokens = session.messages.reduce((sum, m) => sum + m.estimatedTokens, 0);
250
+ return messageTokens + session.summaryTokens;
251
+ }
252
+ /**
253
+ * Generate a simple summary of messages
254
+ * In production, this would use an LLM
255
+ */
256
+ generateSummary(messages) {
257
+ const userMsgs = messages.filter((m) => m.role === "user");
258
+ const assistantMsgs = messages.filter((m) => m.role === "assistant");
259
+ const topics = this.extractTopics(messages);
260
+ return (`Conversation covered ${userMsgs.length} user queries and ${assistantMsgs.length} responses. ` +
261
+ `Topics: ${topics.join(", ") || "general discussion"}.`);
262
+ }
263
+ /**
264
+ * Extract key topics from messages (simplified)
265
+ */
266
+ extractTopics(messages) {
267
+ const allContent = messages
268
+ .map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content))
269
+ .join(" ")
270
+ .toLowerCase();
271
+ // Simple keyword extraction
272
+ const keywords = [
273
+ "code",
274
+ "function",
275
+ "error",
276
+ "bug",
277
+ "fix",
278
+ "implement",
279
+ "create",
280
+ "update",
281
+ "delete",
282
+ "configure",
283
+ "setup",
284
+ ];
285
+ return keywords.filter((kw) => allContent.includes(kw)).slice(0, 3);
286
+ }
287
+ }
288
+ /**
289
+ * Create a summarizing context engine
290
+ */
291
+ export function createSummarizingContextEngine(config) {
292
+ return new SummarizingContextEngine(config);
293
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Context Engine - Pluggable conversation context management
3
+ *
4
+ * Provides a modular architecture for managing conversation context
5
+ * with support for different storage and retrieval strategies.
6
+ */
7
+ export {};
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Abort Pattern Utilities
3
+ *
4
+ * Provides memory-leak-free abort signal handling.
5
+ *
6
+ * CRITICAL FIX: Uses `.bind()` instead of closures to prevent memory leaks.
7
+ * Issue #7174: Closure-based abort handlers capture scope and leak memory.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // BAD: Captures closure scope (leaks memory)
12
+ * signal.addEventListener('abort', () => controller.abort());
13
+ *
14
+ * // GOOD: No closure capture
15
+ * signal.addEventListener('abort', relayAbort.bind(controller));
16
+ * ```
17
+ */
18
+ /**
19
+ * Relay abort signal without closure capture
20
+ * Prevents memory leak by using bind instead of arrow function
21
+ */
22
+ export function relayAbort() {
23
+ this.abort();
24
+ }
25
+ /**
26
+ * Create an abort relay that doesn't capture closure scope
27
+ */
28
+ export function createAbortRelay(controller) {
29
+ return relayAbort.bind(controller);
30
+ }
31
+ /**
32
+ * Link an abort signal to a controller without memory leaks
33
+ */
34
+ export function linkAbortSignal(source, target) {
35
+ const handler = createAbortRelay(target);
36
+ source.addEventListener('abort', handler, { once: true });
37
+ // Return cleanup function
38
+ return () => {
39
+ source.removeEventListener('abort', handler);
40
+ };
41
+ }
42
+ /**
43
+ * Create a timeout-based abort controller
44
+ */
45
+ export function createTimeoutAbortController(timeoutMs) {
46
+ const controller = new AbortController();
47
+ if (timeoutMs > 0) {
48
+ const timeout = setTimeout(() => {
49
+ controller.abort();
50
+ }, timeoutMs);
51
+ return {
52
+ controller,
53
+ cleanup: () => clearTimeout(timeout),
54
+ };
55
+ }
56
+ return {
57
+ controller,
58
+ cleanup: () => { },
59
+ };
60
+ }
61
+ /**
62
+ * Race multiple abort signals
63
+ */
64
+ export function raceAbortSignals(signals, controller) {
65
+ const handlers = [];
66
+ let alreadyAborted = false;
67
+ for (const signal of signals) {
68
+ if (signal.aborted && !alreadyAborted) {
69
+ alreadyAborted = true;
70
+ controller.abort();
71
+ }
72
+ else if (!alreadyAborted) {
73
+ const handler = createAbortRelay(controller);
74
+ signal.addEventListener('abort', handler, { once: true });
75
+ handlers.push(() => signal.removeEventListener('abort', handler));
76
+ }
77
+ }
78
+ return () => {
79
+ for (const cleanup of handlers) {
80
+ cleanup();
81
+ }
82
+ };
83
+ }
84
+ /**
85
+ * Fetch with timeout and proper abort handling
86
+ */
87
+ export async function fetchWithTimeout(url, options = {}) {
88
+ const { timeoutMs = 30000, ...fetchOptions } = options;
89
+ const { controller, cleanup } = createTimeoutAbortController(timeoutMs);
90
+ // Link existing signal if provided
91
+ let unlink;
92
+ if (fetchOptions.signal) {
93
+ unlink = linkAbortSignal(fetchOptions.signal, controller);
94
+ }
95
+ try {
96
+ const response = await fetch(url, {
97
+ ...fetchOptions,
98
+ signal: controller.signal,
99
+ });
100
+ return response;
101
+ }
102
+ finally {
103
+ unlink?.();
104
+ cleanup();
105
+ }
106
+ }
@@ -85,5 +85,99 @@ export async function retryAsync(fn, attemptsOrOptions = 3, initialDelayMs = 300
85
85
  await sleep(delay);
86
86
  }
87
87
  }
88
+ options.onExhausted?.(lastErr, maxAttempts);
88
89
  throw lastErr ?? new Error("Retry failed");
89
90
  }
91
+ /**
92
+ * Execute a function with retry logic and return detailed result
93
+ */
94
+ export async function retryWithResult(fn, options = {}) {
95
+ const resolved = resolveRetryConfig(DEFAULT_RETRY_CONFIG, options);
96
+ const maxAttempts = resolved.attempts;
97
+ const minDelayMs = resolved.minDelayMs;
98
+ const maxDelayMs = Number.isFinite(resolved.maxDelayMs) ? resolved.maxDelayMs : Number.POSITIVE_INFINITY;
99
+ const jitter = resolved.jitter;
100
+ const shouldRetry = options.shouldRetry ?? (() => true);
101
+ let lastErr;
102
+ let totalDelayMs = 0;
103
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
104
+ try {
105
+ const result = await fn();
106
+ return { result, attempts: attempt, totalDelayMs };
107
+ }
108
+ catch (err) {
109
+ lastErr = err;
110
+ if (attempt >= maxAttempts || !shouldRetry(err, attempt)) {
111
+ break;
112
+ }
113
+ const retryAfterMs = options.retryAfterMs?.(err);
114
+ const hasRetryAfter = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs);
115
+ const baseDelay = hasRetryAfter
116
+ ? Math.max(retryAfterMs, minDelayMs)
117
+ : minDelayMs * 2 ** (attempt - 1);
118
+ let delay = Math.min(baseDelay, maxDelayMs);
119
+ delay = applyJitter(delay, jitter);
120
+ delay = Math.min(Math.max(delay, minDelayMs), maxDelayMs);
121
+ totalDelayMs += delay;
122
+ options.onRetry?.({
123
+ attempt,
124
+ maxAttempts,
125
+ delayMs: delay,
126
+ err,
127
+ label: options.label,
128
+ });
129
+ await sleep(delay);
130
+ }
131
+ }
132
+ options.onExhausted?.(lastErr, maxAttempts);
133
+ throw lastErr ?? new Error("Retry failed");
134
+ }
135
+ /**
136
+ * Check if error is retryable (network errors, rate limits, etc.)
137
+ */
138
+ export function isRetryableError(error) {
139
+ if (error instanceof Error) {
140
+ const message = error.message.toLowerCase();
141
+ // Network errors
142
+ if (message.includes("econnreset"))
143
+ return true;
144
+ if (message.includes("etimedout"))
145
+ return true;
146
+ if (message.includes("econnrefused"))
147
+ return true;
148
+ if (message.includes("enotfound"))
149
+ return true;
150
+ if (message.includes("network"))
151
+ return true;
152
+ if (message.includes("timeout"))
153
+ return true;
154
+ // HTTP status codes that are retryable
155
+ const statusMatch = error.message.match(/status\s+(\d+)/i);
156
+ if (statusMatch) {
157
+ const status = parseInt(statusMatch[1], 10);
158
+ // 429 = Too Many Requests, 502/503/504 = Gateway errors
159
+ if (status === 429 || status === 502 || status === 503 || status === 504) {
160
+ return true;
161
+ }
162
+ }
163
+ }
164
+ return false;
165
+ }
166
+ /**
167
+ * Default retry configuration for API calls
168
+ */
169
+ export const API_RETRY_CONFIG = {
170
+ attempts: 3,
171
+ minDelayMs: 1000,
172
+ maxDelayMs: 10000,
173
+ jitter: 0.1,
174
+ };
175
+ /**
176
+ * Default retry configuration for network calls
177
+ */
178
+ export const NETWORK_RETRY_CONFIG = {
179
+ attempts: 5,
180
+ minDelayMs: 500,
181
+ maxDelayMs: 30000,
182
+ jitter: 0.1,
183
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Secrets Management System
3
+ *
4
+ * Secure secret resolution with support for:
5
+ * - Environment variables (env:default:KEY)
6
+ * - JSON files (file:/path/to/secrets.json:key)
7
+ * - Direct values (backward compatible)
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { SecretsRuntime, resolveValue } from './secrets/index.js';
12
+ *
13
+ * // Create runtime
14
+ * const runtime = new SecretsRuntime({
15
+ * allowDirectValues: true,
16
+ * warnOnDirectValues: true
17
+ * });
18
+ *
19
+ * // Resolve API key
20
+ * runtime.resolve('openaiApiKey', 'env:default:OPENAI_API_KEY');
21
+ *
22
+ * // Get resolved value
23
+ * const apiKey = runtime.get('openaiApiKey');
24
+ * ```
25
+ */
26
+ export * from "./types.js";
27
+ export * from "./resolver.js";
28
+ export * from "./runtime.js";