skyloom 1.14.6 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +74 -0
- package/CONVERSION_PLAN.md +191 -191
- package/README.md +523 -220
- package/config/default.yaml +46 -43
- package/config/models.yaml +928 -155
- package/config/providers.yaml +109 -6
- package/dist/agents/snow.d.ts +2 -0
- package/dist/agents/snow.d.ts.map +1 -1
- package/dist/agents/snow.js +36 -5
- package/dist/agents/snow.js.map +1 -1
- package/dist/cli/loom_chat.d.ts.map +1 -1
- package/dist/cli/loom_chat.js +207 -1
- package/dist/cli/loom_chat.js.map +1 -1
- package/dist/cli/main.js +190 -40
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +6 -31
- package/dist/cli/tui.js.map +1 -1
- package/dist/core/agent.d.ts +6 -4
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +61 -20
- package/dist/core/agent.js.map +1 -1
- package/dist/core/catalog.d.ts.map +1 -1
- package/dist/core/catalog.js +30 -9
- package/dist/core/catalog.js.map +1 -1
- package/dist/core/commands.d.ts +110 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +633 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/concurrency.d.ts +38 -0
- package/dist/core/concurrency.d.ts.map +1 -0
- package/dist/core/concurrency.js +65 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/factory.js +16 -16
- package/dist/core/file_checkpoint.d.ts +9 -0
- package/dist/core/file_checkpoint.d.ts.map +1 -1
- package/dist/core/file_checkpoint.js +33 -1
- package/dist/core/file_checkpoint.js.map +1 -1
- package/dist/core/llm.d.ts.map +1 -1
- package/dist/core/llm.js +66 -13
- package/dist/core/llm.js.map +1 -1
- package/dist/core/memory.js +51 -51
- package/dist/core/schemas.d.ts +16 -0
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +32 -0
- package/dist/core/schemas.js.map +1 -1
- package/dist/core/security.d.ts.map +1 -1
- package/dist/core/security.js +27 -0
- package/dist/core/security.js.map +1 -1
- package/dist/core/skymd.js +14 -14
- package/dist/core/trace.d.ts +105 -0
- package/dist/core/trace.d.ts.map +1 -0
- package/dist/core/trace.js +213 -0
- package/dist/core/trace.js.map +1 -0
- package/dist/tools/builtin.d.ts +2 -6
- package/dist/tools/builtin.d.ts.map +1 -1
- package/dist/tools/builtin.js +180 -125
- package/dist/tools/builtin.js.map +1 -1
- package/dist/tools/extra.d.ts +13 -0
- package/dist/tools/extra.d.ts.map +1 -0
- package/dist/tools/extra.js +827 -0
- package/dist/tools/extra.js.map +1 -0
- package/dist/tools/guards.d.ts +12 -0
- package/dist/tools/guards.d.ts.map +1 -0
- package/dist/tools/guards.js +143 -0
- package/dist/tools/guards.js.map +1 -0
- package/dist/tools/model_tool.d.ts.map +1 -1
- package/dist/tools/model_tool.js +24 -4
- package/dist/tools/model_tool.js.map +1 -1
- package/dist/web/markdown.d.ts +32 -0
- package/dist/web/markdown.d.ts.map +1 -0
- package/dist/web/markdown.js +202 -0
- package/dist/web/markdown.js.map +1 -0
- package/dist/web/server.d.ts +4 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +14 -582
- package/dist/web/server.js.map +1 -1
- package/dist/web/ui.d.ts +31 -0
- package/dist/web/ui.d.ts.map +1 -0
- package/dist/web/ui.js +1009 -0
- package/dist/web/ui.js.map +1 -0
- package/docs/AESTHETIC_DESIGN.md +152 -152
- package/docs/OPTIMIZATION_PLAN.md +178 -178
- package/package.json +68 -68
- package/src/agents/snow.ts +38 -5
- package/src/cli/commands_md.ts +112 -112
- package/src/cli/input_macros.ts +83 -83
- package/src/cli/loom.ts +1041 -1041
- package/src/cli/loom_chat.ts +772 -603
- package/src/cli/main.ts +853 -723
- package/src/cli/tui.ts +264 -289
- package/src/core/agent/guard.ts +133 -133
- package/src/core/agent/task.ts +100 -100
- package/src/core/agent.ts +1630 -1590
- package/src/core/agent_helpers.ts +500 -500
- package/src/core/bus.ts +221 -221
- package/src/core/cache.ts +153 -153
- package/src/core/catalog.ts +199 -178
- package/src/core/circuit_breaker.ts +119 -119
- package/src/core/commands.ts +704 -0
- package/src/core/concurrency.ts +73 -0
- package/src/core/config.ts +365 -365
- package/src/core/constants.ts +95 -95
- package/src/core/factory.ts +656 -656
- package/src/core/file_checkpoint.ts +163 -136
- package/src/core/hooks.ts +126 -126
- package/src/core/llm.ts +972 -915
- package/src/core/logger.ts +143 -143
- package/src/core/mcp.ts +1001 -1001
- package/src/core/memory.ts +1201 -1201
- package/src/core/middleware.ts +350 -350
- package/src/core/model_config.ts +159 -159
- package/src/core/pipelines.ts +424 -424
- package/src/core/schemas.ts +319 -282
- package/src/core/security.ts +27 -0
- package/src/core/semantic.ts +211 -211
- package/src/core/skill.ts +384 -384
- package/src/core/skymd.ts +143 -143
- package/src/core/theme.ts +65 -65
- package/src/core/tool.ts +457 -457
- package/src/core/trace.ts +236 -0
- package/src/core/verify.ts +71 -71
- package/src/plugins/loader.ts +91 -91
- package/src/skills/loader.ts +75 -75
- package/src/tools/builtin.ts +571 -493
- package/src/tools/computer.ts +279 -279
- package/src/tools/extra.ts +662 -0
- package/src/tools/guards.ts +82 -0
- package/src/tools/model_tool.ts +93 -74
- package/src/tools/todo.ts +76 -76
- package/src/web/markdown.ts +193 -0
- package/src/web/server.ts +117 -693
- package/src/web/ui.ts +949 -0
- package/tests/agent.test.ts +211 -159
- package/tests/agent_helpers.test.ts +48 -48
- package/tests/catalog.test.ts +86 -86
- package/tests/checkpoint_commands.test.ts +124 -124
- package/tests/claude_compat.test.ts +110 -110
- package/tests/commands.test.ts +103 -0
- package/tests/concurrency.test.ts +102 -0
- package/tests/config.test.ts +41 -41
- package/tests/extra_tools.test.ts +212 -0
- package/tests/fence_plugin.test.ts +52 -52
- package/tests/guard.test.ts +75 -75
- package/tests/loom.test.ts +337 -337
- package/tests/memory.test.ts +170 -170
- package/tests/model_config.test.ts +109 -109
- package/tests/skymd.test.ts +146 -146
- package/tests/ssrf.test.ts +38 -38
- package/tests/structured_retry.test.ts +87 -0
- package/tests/task.test.ts +60 -60
- package/tests/todo_toolstats.test.ts +94 -94
- package/tests/trace.test.ts +128 -0
- package/tests/tui.test.ts +67 -67
- package/tests/web.test.ts +169 -0
- package/tsconfig.json +38 -38
package/src/core/cache.ts
CHANGED
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight LRU cache for LLM responses.
|
|
3
|
-
*
|
|
4
|
-
* Deduplicates identical requests within a configurable time window.
|
|
5
|
-
* Keyed by (model, messages_json) hash.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import crypto from 'crypto';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Cache entry containing timestamp and response.
|
|
12
|
-
*/
|
|
13
|
-
interface CacheEntry {
|
|
14
|
-
timestamp: number;
|
|
15
|
-
response: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* LRU cache for LLM completions with TTL expiration.
|
|
20
|
-
*/
|
|
21
|
-
export class LLMCache {
|
|
22
|
-
private maxSize: number;
|
|
23
|
-
private ttlSeconds: number;
|
|
24
|
-
private cache: Map<string, CacheEntry> = new Map();
|
|
25
|
-
|
|
26
|
-
constructor(maxSize: number = 128, ttlSeconds: number = 60) {
|
|
27
|
-
this.maxSize = maxSize;
|
|
28
|
-
this.ttlSeconds = ttlSeconds;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Generate a cache key from model, messages, and parameters.
|
|
33
|
-
*
|
|
34
|
-
* Uses SHA256 hash for deterministic, compact keys. Parameters are sorted
|
|
35
|
-
* to ensure consistent keys regardless of key order.
|
|
36
|
-
*/
|
|
37
|
-
private makeKey(
|
|
38
|
-
model: string,
|
|
39
|
-
messages: Record<string, any>[],
|
|
40
|
-
params?: Record<string, any>
|
|
41
|
-
): string {
|
|
42
|
-
const raw = JSON.stringify([model, messages, params || {}], this.jsonReplacer);
|
|
43
|
-
return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 32);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* JSON replacer for consistent serialization.
|
|
48
|
-
*/
|
|
49
|
-
private jsonReplacer(_key: string, value: any): any {
|
|
50
|
-
if (typeof value === 'function') {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get a cached response if it exists and hasn't expired.
|
|
58
|
-
*
|
|
59
|
-
* @param model - LLM model name
|
|
60
|
-
* @param messages - Conversation messages
|
|
61
|
-
* @param params - Optional parameters
|
|
62
|
-
* @returns Cached response or null if miss/expired
|
|
63
|
-
*/
|
|
64
|
-
get(
|
|
65
|
-
model: string,
|
|
66
|
-
messages: Record<string, any>[],
|
|
67
|
-
params?: Record<string, any>
|
|
68
|
-
): string | null {
|
|
69
|
-
const key = this.makeKey(model, messages, params);
|
|
70
|
-
const entry = this.cache.get(key);
|
|
71
|
-
|
|
72
|
-
if (!entry) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check if expired
|
|
77
|
-
const now = Date.now();
|
|
78
|
-
const ageSeconds = (now - entry.timestamp) / 1000;
|
|
79
|
-
|
|
80
|
-
if (ageSeconds >= this.ttlSeconds) {
|
|
81
|
-
this.cache.delete(key);
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Move to end (LRU)
|
|
86
|
-
this.cache.delete(key);
|
|
87
|
-
this.cache.set(key, entry);
|
|
88
|
-
|
|
89
|
-
return entry.response;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Cache a response.
|
|
94
|
-
*
|
|
95
|
-
* Refuses to cache empty or very short content to avoid bloat.
|
|
96
|
-
* Also performs LRU eviction if cache exceeds max_size.
|
|
97
|
-
*
|
|
98
|
-
* @param model - LLM model name
|
|
99
|
-
* @param messages - Conversation messages
|
|
100
|
-
* @param response - LLM response to cache
|
|
101
|
-
* @param params - Optional parameters
|
|
102
|
-
*/
|
|
103
|
-
set(
|
|
104
|
-
model: string,
|
|
105
|
-
messages: Record<string, any>[],
|
|
106
|
-
response: string,
|
|
107
|
-
params?: Record<string, any>
|
|
108
|
-
): void {
|
|
109
|
-
// Don't cache empty or very short responses
|
|
110
|
-
if (!response || response.length < 10) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const key = this.makeKey(model, messages, params);
|
|
115
|
-
const entry: CacheEntry = {
|
|
116
|
-
timestamp: Date.now(),
|
|
117
|
-
response,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
this.cache.delete(key);
|
|
121
|
-
this.cache.set(key, entry);
|
|
122
|
-
|
|
123
|
-
// Evict oldest entries if over capacity
|
|
124
|
-
while (this.cache.size > this.maxSize) {
|
|
125
|
-
const firstKey = this.cache.keys().next().value;
|
|
126
|
-
if (firstKey !== undefined) this.cache.delete(firstKey);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Clear all cache entries.
|
|
132
|
-
*/
|
|
133
|
-
clear(): void {
|
|
134
|
-
this.cache.clear();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get current cache size.
|
|
139
|
-
*/
|
|
140
|
-
get size(): number {
|
|
141
|
-
return this.cache.size;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get cache hit rate statistics.
|
|
146
|
-
*/
|
|
147
|
-
getStats(): { size: number; maxSize: number } {
|
|
148
|
-
return {
|
|
149
|
-
size: this.cache.size,
|
|
150
|
-
maxSize: this.maxSize,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight LRU cache for LLM responses.
|
|
3
|
+
*
|
|
4
|
+
* Deduplicates identical requests within a configurable time window.
|
|
5
|
+
* Keyed by (model, messages_json) hash.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Cache entry containing timestamp and response.
|
|
12
|
+
*/
|
|
13
|
+
interface CacheEntry {
|
|
14
|
+
timestamp: number;
|
|
15
|
+
response: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* LRU cache for LLM completions with TTL expiration.
|
|
20
|
+
*/
|
|
21
|
+
export class LLMCache {
|
|
22
|
+
private maxSize: number;
|
|
23
|
+
private ttlSeconds: number;
|
|
24
|
+
private cache: Map<string, CacheEntry> = new Map();
|
|
25
|
+
|
|
26
|
+
constructor(maxSize: number = 128, ttlSeconds: number = 60) {
|
|
27
|
+
this.maxSize = maxSize;
|
|
28
|
+
this.ttlSeconds = ttlSeconds;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate a cache key from model, messages, and parameters.
|
|
33
|
+
*
|
|
34
|
+
* Uses SHA256 hash for deterministic, compact keys. Parameters are sorted
|
|
35
|
+
* to ensure consistent keys regardless of key order.
|
|
36
|
+
*/
|
|
37
|
+
private makeKey(
|
|
38
|
+
model: string,
|
|
39
|
+
messages: Record<string, any>[],
|
|
40
|
+
params?: Record<string, any>
|
|
41
|
+
): string {
|
|
42
|
+
const raw = JSON.stringify([model, messages, params || {}], this.jsonReplacer);
|
|
43
|
+
return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 32);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* JSON replacer for consistent serialization.
|
|
48
|
+
*/
|
|
49
|
+
private jsonReplacer(_key: string, value: any): any {
|
|
50
|
+
if (typeof value === 'function') {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a cached response if it exists and hasn't expired.
|
|
58
|
+
*
|
|
59
|
+
* @param model - LLM model name
|
|
60
|
+
* @param messages - Conversation messages
|
|
61
|
+
* @param params - Optional parameters
|
|
62
|
+
* @returns Cached response or null if miss/expired
|
|
63
|
+
*/
|
|
64
|
+
get(
|
|
65
|
+
model: string,
|
|
66
|
+
messages: Record<string, any>[],
|
|
67
|
+
params?: Record<string, any>
|
|
68
|
+
): string | null {
|
|
69
|
+
const key = this.makeKey(model, messages, params);
|
|
70
|
+
const entry = this.cache.get(key);
|
|
71
|
+
|
|
72
|
+
if (!entry) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if expired
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const ageSeconds = (now - entry.timestamp) / 1000;
|
|
79
|
+
|
|
80
|
+
if (ageSeconds >= this.ttlSeconds) {
|
|
81
|
+
this.cache.delete(key);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Move to end (LRU)
|
|
86
|
+
this.cache.delete(key);
|
|
87
|
+
this.cache.set(key, entry);
|
|
88
|
+
|
|
89
|
+
return entry.response;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Cache a response.
|
|
94
|
+
*
|
|
95
|
+
* Refuses to cache empty or very short content to avoid bloat.
|
|
96
|
+
* Also performs LRU eviction if cache exceeds max_size.
|
|
97
|
+
*
|
|
98
|
+
* @param model - LLM model name
|
|
99
|
+
* @param messages - Conversation messages
|
|
100
|
+
* @param response - LLM response to cache
|
|
101
|
+
* @param params - Optional parameters
|
|
102
|
+
*/
|
|
103
|
+
set(
|
|
104
|
+
model: string,
|
|
105
|
+
messages: Record<string, any>[],
|
|
106
|
+
response: string,
|
|
107
|
+
params?: Record<string, any>
|
|
108
|
+
): void {
|
|
109
|
+
// Don't cache empty or very short responses
|
|
110
|
+
if (!response || response.length < 10) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const key = this.makeKey(model, messages, params);
|
|
115
|
+
const entry: CacheEntry = {
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
response,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.cache.delete(key);
|
|
121
|
+
this.cache.set(key, entry);
|
|
122
|
+
|
|
123
|
+
// Evict oldest entries if over capacity
|
|
124
|
+
while (this.cache.size > this.maxSize) {
|
|
125
|
+
const firstKey = this.cache.keys().next().value;
|
|
126
|
+
if (firstKey !== undefined) this.cache.delete(firstKey);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Clear all cache entries.
|
|
132
|
+
*/
|
|
133
|
+
clear(): void {
|
|
134
|
+
this.cache.clear();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get current cache size.
|
|
139
|
+
*/
|
|
140
|
+
get size(): number {
|
|
141
|
+
return this.cache.size;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get cache hit rate statistics.
|
|
146
|
+
*/
|
|
147
|
+
getStats(): { size: number; maxSize: number } {
|
|
148
|
+
return {
|
|
149
|
+
size: this.cache.size,
|
|
150
|
+
maxSize: this.maxSize,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|