@wrongstack/core 0.1.9 → 0.2.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.
Files changed (69) hide show
  1. package/dist/agent-bridge-DmBiCipY.d.ts +33 -0
  2. package/dist/compactor-DSl2FK7a.d.ts +17 -0
  3. package/dist/config-DXrqb41m.d.ts +193 -0
  4. package/dist/{provider-txgB0Oq9.d.ts → context-u0bryklF.d.ts} +540 -472
  5. package/dist/coordination/index.d.ts +892 -0
  6. package/dist/coordination/index.js +2869 -0
  7. package/dist/coordination/index.js.map +1 -0
  8. package/dist/defaults/index.d.ts +34 -2309
  9. package/dist/defaults/index.js +5610 -4608
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/events-B6Q03pTu.d.ts +290 -0
  12. package/dist/execution/index.d.ts +260 -0
  13. package/dist/execution/index.js +1625 -0
  14. package/dist/execution/index.js.map +1 -0
  15. package/dist/index.d.ts +81 -11
  16. package/dist/index.js +7727 -6174
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +10 -0
  19. package/dist/infrastructure/index.js +575 -0
  20. package/dist/infrastructure/index.js.map +1 -0
  21. package/dist/input-reader-E-ffP2ee.d.ts +12 -0
  22. package/dist/kernel/index.d.ts +15 -4
  23. package/dist/kernel/index.js.map +1 -1
  24. package/dist/logger-BH6AE0W9.d.ts +24 -0
  25. package/dist/logger-BMQgxvdy.d.ts +12 -0
  26. package/dist/mcp-servers-BA1Ofmfj.d.ts +100 -0
  27. package/dist/memory-CEXuo7sz.d.ts +16 -0
  28. package/dist/mode-CV077NjV.d.ts +27 -0
  29. package/dist/models/index.d.ts +60 -0
  30. package/dist/models/index.js +621 -0
  31. package/dist/models/index.js.map +1 -0
  32. package/dist/models-registry-DqzwpBQy.d.ts +46 -0
  33. package/dist/models-registry-Y2xbog0E.d.ts +95 -0
  34. package/dist/multi-agent-BDfkxL5C.d.ts +351 -0
  35. package/dist/observability/index.d.ts +353 -0
  36. package/dist/observability/index.js +691 -0
  37. package/dist/observability/index.js.map +1 -0
  38. package/dist/observability-BhnVLBLS.d.ts +67 -0
  39. package/dist/path-resolver-CPRj4bFY.d.ts +10 -0
  40. package/dist/path-resolver-Crkt8wTQ.d.ts +54 -0
  41. package/dist/plugin-CoYYZKdn.d.ts +447 -0
  42. package/dist/renderer-0A2ZEtca.d.ts +158 -0
  43. package/dist/sdd/index.d.ts +206 -0
  44. package/dist/sdd/index.js +864 -0
  45. package/dist/sdd/index.js.map +1 -0
  46. package/dist/secret-scrubber-3TLUkiCV.d.ts +31 -0
  47. package/dist/secret-scrubber-CwYliRWd.d.ts +54 -0
  48. package/dist/secret-vault-DoISxaKO.d.ts +19 -0
  49. package/dist/security/index.d.ts +46 -0
  50. package/dist/security/index.js +536 -0
  51. package/dist/security/index.js.map +1 -0
  52. package/dist/selector-BRqzvugb.d.ts +51 -0
  53. package/dist/session-reader-C3x96CDR.d.ts +150 -0
  54. package/dist/skill-Bx8jxznf.d.ts +72 -0
  55. package/dist/storage/index.d.ts +540 -0
  56. package/dist/storage/index.js +1802 -0
  57. package/dist/storage/index.js.map +1 -0
  58. package/dist/{system-prompt-vAB0F54-.d.ts → system-prompt-CG9jU5-5.d.ts} +9 -1
  59. package/dist/task-graph-BITvWt4t.d.ts +160 -0
  60. package/dist/tool-executor-CYdZdtno.d.ts +97 -0
  61. package/dist/types/index.d.ts +26 -4
  62. package/dist/types/index.js +1787 -4
  63. package/dist/types/index.js.map +1 -1
  64. package/dist/utils/index.d.ts +49 -2
  65. package/dist/utils/index.js +100 -2
  66. package/dist/utils/index.js.map +1 -1
  67. package/package.json +34 -2
  68. package/dist/mode-Pjt5vMS6.d.ts +0 -815
  69. package/dist/session-reader-9sOTgmeC.d.ts +0 -1087
@@ -0,0 +1,60 @@
1
+ export { D as DefaultModelsRegistry, a as DefaultModelsRegistryOptions, c as classifyFamily } from '../models-registry-DqzwpBQy.js';
2
+ import { c as ModeStore, a as ModeConfig, M as Mode } from '../mode-CV077NjV.js';
3
+ import { g as Provider, M as Message } from '../context-u0bryklF.js';
4
+ import { M as MessageSelector, S as SelectorResult } from '../selector-BRqzvugb.js';
5
+ import '../models-registry-Y2xbog0E.js';
6
+
7
+ declare class DefaultModeStore implements ModeStore {
8
+ private activeModeId;
9
+ private modes;
10
+ private configDir;
11
+ constructor(config: ModeConfig);
12
+ getActiveMode(): Promise<Mode | null>;
13
+ setActiveMode(modeId: string | null): Promise<void>;
14
+ listModes(): Promise<Mode[]>;
15
+ getMode(modeId: string): Promise<Mode | null>;
16
+ addMode(mode: Mode): Promise<void>;
17
+ removeMode(modeId: string): Promise<void>;
18
+ private loadActiveMode;
19
+ private saveActiveMode;
20
+ }
21
+ interface ModeLoaderOptions {
22
+ projectModesDir?: string;
23
+ userModesDir?: string;
24
+ }
25
+ declare function loadProjectModes(modesDir: string): Promise<Mode[]>;
26
+ declare function loadUserModes(modesDir: string): Promise<Mode[]>;
27
+
28
+ interface LLMSelectorOptions {
29
+ /** Provider used for the selector LLM call. Required. */
30
+ provider: Provider;
31
+ /** Model for the selector. Defaults to the provider's default model. */
32
+ model?: string;
33
+ /**
34
+ * Maximum tokens to keep in context (target budget).
35
+ * Selector will aim to keep total content below this.
36
+ */
37
+ maxContextTokens?: number;
38
+ /**
39
+ * Prompt instructing the selector how to behave.
40
+ * Should guide the LLM on importance tiers and output format.
41
+ */
42
+ systemPrompt?: string;
43
+ }
44
+ /**
45
+ * LLM-powered message selector. Calls a sub-LLM to analyze the
46
+ * message history and produce a keep/collapse plan — more surgical
47
+ * than fixed-window rules.
48
+ */
49
+ declare class LLMSelector implements MessageSelector {
50
+ private readonly provider;
51
+ private readonly model;
52
+ private readonly maxContextTokens;
53
+ private readonly systemPrompt;
54
+ constructor(opts: LLMSelectorOptions);
55
+ select(messages: Message[], maxToKeep: number): Promise<SelectorResult>;
56
+ private fallbackSelect;
57
+ private parseSelectorOutput;
58
+ }
59
+
60
+ export { DefaultModeStore, LLMSelector, type LLMSelectorOptions, type ModeLoaderOptions, loadProjectModes, loadUserModes };
@@ -0,0 +1,621 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path3 from 'path';
3
+ import { randomBytes } from 'crypto';
4
+
5
+ // src/models/models-registry.ts
6
+ async function atomicWrite(targetPath, content, opts = {}) {
7
+ const dir = path3.dirname(targetPath);
8
+ await fs.mkdir(dir, { recursive: true });
9
+ const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
10
+ try {
11
+ if (typeof content === "string") {
12
+ await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
13
+ } else {
14
+ await fs.writeFile(tmp, content, { flag: "wx" });
15
+ }
16
+ try {
17
+ const fh = await fs.open(tmp, "r+");
18
+ try {
19
+ await fh.sync();
20
+ } finally {
21
+ await fh.close();
22
+ }
23
+ } catch {
24
+ }
25
+ let mode;
26
+ try {
27
+ const stat3 = await fs.stat(targetPath);
28
+ mode = stat3.mode & 511;
29
+ } catch {
30
+ mode = opts.mode;
31
+ }
32
+ if (mode !== void 0) {
33
+ await fs.chmod(tmp, mode);
34
+ }
35
+ await fs.rename(tmp, targetPath);
36
+ } catch (err) {
37
+ try {
38
+ await fs.unlink(tmp);
39
+ } catch {
40
+ }
41
+ throw err;
42
+ }
43
+ }
44
+
45
+ // src/models/models-registry.ts
46
+ var DEFAULT_URL = "https://models.dev/api.json";
47
+ var DEFAULT_TTL_SECONDS = 24 * 3600;
48
+ var FAMILY_BY_NPM = {
49
+ "@ai-sdk/anthropic": "anthropic",
50
+ "@ai-sdk/google-vertex/anthropic": "anthropic",
51
+ "@ai-sdk/openai": "openai",
52
+ "@ai-sdk/openai-compatible": "openai-compatible",
53
+ "@ai-sdk/groq": "openai-compatible",
54
+ "@ai-sdk/xai": "openai-compatible",
55
+ "@ai-sdk/cerebras": "openai-compatible",
56
+ "@ai-sdk/togetherai": "openai-compatible",
57
+ "@ai-sdk/perplexity": "openai-compatible",
58
+ "@ai-sdk/deepinfra": "openai-compatible",
59
+ "@openrouter/ai-sdk-provider": "openai-compatible",
60
+ "ai-gateway-provider": "openai-compatible",
61
+ "@ai-sdk/vercel": "openai-compatible",
62
+ "@ai-sdk/gateway": "openai-compatible",
63
+ "@aihubmix/ai-sdk-provider": "openai-compatible",
64
+ "venice-ai-sdk-provider": "openai-compatible",
65
+ "@ai-sdk/google": "google"
66
+ };
67
+ function classifyFamily(npm) {
68
+ if (!npm) return "unsupported";
69
+ return FAMILY_BY_NPM[npm] ?? "unsupported";
70
+ }
71
+ var DefaultModelsRegistry = class {
72
+ payload;
73
+ fetchedAt;
74
+ cacheFile;
75
+ url;
76
+ ttlMs;
77
+ fetchImpl;
78
+ seed;
79
+ maxStaleAgeMs;
80
+ constructor(opts) {
81
+ this.cacheFile = opts.cacheFile;
82
+ this.url = opts.url ?? DEFAULT_URL;
83
+ this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
84
+ this.fetchImpl = opts.fetchImpl ?? fetch;
85
+ this.seed = opts.seed;
86
+ const maxStaleSeconds = opts.maxStaleAgeSeconds ?? 7 * 24 * 3600;
87
+ this.maxStaleAgeMs = maxStaleSeconds * 1e3;
88
+ }
89
+ async load(opts = {}) {
90
+ if (this.payload && !opts.force) return this.payload;
91
+ if (this.seed) {
92
+ this.payload = this.seed;
93
+ this.fetchedAt = /* @__PURE__ */ new Date();
94
+ return this.payload;
95
+ }
96
+ if (!opts.force) {
97
+ const cached = await this.readCache();
98
+ if (cached && this.isFresh(cached.fetchedAt)) {
99
+ this.payload = cached.payload;
100
+ this.fetchedAt = new Date(cached.fetchedAt);
101
+ return cached.payload;
102
+ }
103
+ }
104
+ try {
105
+ return await this.refresh();
106
+ } catch (err) {
107
+ const cached = await this.readCache();
108
+ if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
109
+ this.payload = cached.payload;
110
+ this.fetchedAt = new Date(cached.fetchedAt);
111
+ return cached.payload;
112
+ }
113
+ throw err;
114
+ }
115
+ }
116
+ async refresh() {
117
+ const res = await this.fetchImpl(this.url, {
118
+ method: "GET",
119
+ headers: { accept: "application/json" }
120
+ });
121
+ if (!res.ok) {
122
+ throw new Error(`ModelsRegistry: HTTP ${res.status} fetching ${this.url}`);
123
+ }
124
+ const json = await res.json();
125
+ this.payload = json;
126
+ this.fetchedAt = /* @__PURE__ */ new Date();
127
+ const envelope = {
128
+ fetchedAt: this.fetchedAt.toISOString(),
129
+ url: this.url,
130
+ payload: json
131
+ };
132
+ await atomicWrite(this.cacheFile, JSON.stringify(envelope));
133
+ return json;
134
+ }
135
+ async listProviders() {
136
+ const payload = await this.load();
137
+ return Object.values(payload).map((p) => this.resolveProvider(p));
138
+ }
139
+ async getProvider(id) {
140
+ const payload = await this.load();
141
+ const p = payload[id];
142
+ return p ? this.resolveProvider(p) : void 0;
143
+ }
144
+ async getModel(providerId, modelId) {
145
+ const provider = await this.getProvider(providerId);
146
+ if (!provider) return void 0;
147
+ const model = provider.models.find((m) => m.id === modelId);
148
+ if (!model) return void 0;
149
+ return {
150
+ providerId,
151
+ modelId,
152
+ capabilities: {
153
+ tools: model.tool_call ?? false,
154
+ vision: Boolean(model.modalities?.input?.includes("image")),
155
+ reasoning: model.reasoning ?? false,
156
+ maxContext: model.limit?.context ?? 0,
157
+ maxOutput: model.limit?.output,
158
+ knowledge: model.knowledge
159
+ },
160
+ cost: model.cost
161
+ };
162
+ }
163
+ async suggestModel(providerId) {
164
+ const provider = await this.getProvider(providerId);
165
+ if (!provider || provider.models.length === 0) return void 0;
166
+ const ranked = [...provider.models].sort((a, b) => {
167
+ const at = a.release_date ?? a.last_updated ?? "";
168
+ const bt = b.release_date ?? b.last_updated ?? "";
169
+ return bt.localeCompare(at);
170
+ });
171
+ return ranked[0]?.id;
172
+ }
173
+ async ageSeconds() {
174
+ if (!this.fetchedAt) {
175
+ const cached = await this.readCache();
176
+ if (!cached) return Number.POSITIVE_INFINITY;
177
+ return (Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3;
178
+ }
179
+ return (Date.now() - this.fetchedAt.getTime()) / 1e3;
180
+ }
181
+ resolveProvider(p) {
182
+ return {
183
+ id: p.id,
184
+ name: p.name,
185
+ family: classifyFamily(p.npm),
186
+ apiBase: p.api,
187
+ envVars: p.env ?? [],
188
+ doc: p.doc,
189
+ models: Object.values(p.models ?? {}),
190
+ npm: p.npm
191
+ };
192
+ }
193
+ isFresh(fetchedAtIso) {
194
+ return Date.now() - new Date(fetchedAtIso).getTime() < this.ttlMs;
195
+ }
196
+ isWithinMaxStaleAge(fetchedAtIso) {
197
+ return Date.now() - new Date(fetchedAtIso).getTime() < this.maxStaleAgeMs;
198
+ }
199
+ async readCache() {
200
+ try {
201
+ const raw = await fs.readFile(this.cacheFile, "utf8");
202
+ return JSON.parse(raw);
203
+ } catch {
204
+ return void 0;
205
+ }
206
+ }
207
+ /** Used by `wstack models refresh` to expose where the cache lives. */
208
+ cacheLocation() {
209
+ return path3.resolve(this.cacheFile);
210
+ }
211
+ };
212
+
213
+ // src/types/mode.ts
214
+ var DEFAULT_MODES = [
215
+ {
216
+ id: "default",
217
+ name: "Default",
218
+ description: "General-purpose coding assistant",
219
+ prompt: "",
220
+ tags: ["general"]
221
+ },
222
+ {
223
+ id: "code-reviewer",
224
+ name: "Code Reviewer",
225
+ description: "Focus on code quality, best practices, and potential bugs",
226
+ prompt: `## Code Reviewer Mode
227
+
228
+ When reviewing code:
229
+ - Look for potential bugs, race conditions, and edge cases
230
+ - Check for security vulnerabilities (SQL injection, XSS, CSRF, etc.)
231
+ - Evaluate error handling completeness
232
+ - Assess code readability and maintainability
233
+ - Check for performance anti-patterns
234
+ - Verify test coverage for critical paths
235
+ - Ensure naming conventions are followed`,
236
+ tags: ["review", "quality", "security"],
237
+ toolPreferences: ["read", "grep", "git", "diff", "test"]
238
+ },
239
+ {
240
+ id: "code-auditor",
241
+ name: "Code Auditor",
242
+ description: "Security-focused code analysis",
243
+ prompt: `## Code Auditor Mode
244
+
245
+ When auditing code for security:
246
+ - Identify injection vulnerabilities (SQL, Command, XSS, LDAP)
247
+ - Check authentication and authorization patterns
248
+ - Look for sensitive data exposure (secrets, PII in logs)
249
+ - Verify cryptographic implementations
250
+ - Check for insecure dependencies or configurations
251
+ - Assess input validation and output encoding
252
+ - Look for timing attacks and information leakage`,
253
+ tags: ["security", "audit", "compliance"],
254
+ toolPreferences: ["grep", "read", "audit", "bash"]
255
+ },
256
+ {
257
+ id: "architect",
258
+ name: "Software Architect",
259
+ description: "Design patterns, scalability, and system design",
260
+ prompt: `## Architect Mode
261
+
262
+ When designing or reviewing architecture:
263
+ - Evaluate scalability and future growth
264
+ - Check for appropriate design patterns
265
+ - Assess coupling and cohesion
266
+ - Look forSOLID principle violations
267
+ - Evaluate data modeling decisions
268
+ - Check for eventual consistency issues
269
+ - Assess API design and contract stability
270
+ - Consider operational aspects (monitoring, logging, deployment)`,
271
+ tags: ["architecture", "design", "scalability"],
272
+ toolPreferences: ["read", "glob", "tree", "diff"]
273
+ },
274
+ {
275
+ id: "debugger",
276
+ name: "Debugger",
277
+ description: "Root cause analysis and error investigation",
278
+ prompt: `## Debugger Mode
279
+
280
+ When investigating bugs:
281
+ - Reproduce the issue with minimal steps
282
+ - Check error messages and stack traces thoroughly
283
+ - Look for related logs and historical context
284
+ - Verify assumptions about data flow
285
+ - Check for race conditions in async code
286
+ - Validate environment and configuration
287
+ - Use binary search to isolate the root cause
288
+ - Verify fixes with tests before considering done`,
289
+ tags: ["debug", "investigation", "error-resolution"],
290
+ toolPreferences: ["read", "grep", "bash", "logs", "test"]
291
+ },
292
+ {
293
+ id: "tester",
294
+ name: "QA Engineer",
295
+ description: "Test coverage, edge cases, and quality assurance",
296
+ prompt: `## Tester Mode
297
+
298
+ When testing or writing tests:
299
+ - Cover happy path and error paths equally
300
+ - Think about edge cases and boundary conditions
301
+ - Check for missing null/undefined handling tests
302
+ - Verify error messages are tested
303
+ - Look for race condition tests in async code
304
+ - Assess mutation testing opportunities
305
+ - Check for integration test gaps
306
+ - Verify test isolation and cleanup`,
307
+ tags: ["testing", "qa", "quality"],
308
+ toolPreferences: ["read", "grep", "test", "bash"]
309
+ },
310
+ {
311
+ id: "devops",
312
+ name: "DevOps Engineer",
313
+ description: "Infrastructure, deployment, and operations",
314
+ prompt: `## DevOps Mode
315
+
316
+ When working on infrastructure:
317
+ - Check for containerization and deployment readiness
318
+ - Verify CI/CD pipeline configurations
319
+ - Assess monitoring and alerting setup
320
+ - Look for health check endpoints
321
+ - Check for graceful shutdown handling
322
+ - Verify backup and disaster recovery plans
323
+ - Assess secrets management
324
+ - Check for resource limits and quotas`,
325
+ tags: ["devops", "infrastructure", "operations"],
326
+ toolPreferences: ["read", "bash", "grep", "logs", "git"]
327
+ },
328
+ {
329
+ id: "refactorer",
330
+ name: "Refactorer",
331
+ description: "Code improvement and modernization",
332
+ prompt: `## Refactorer Mode
333
+
334
+ When refactoring code:
335
+ - Maintain existing behavior \u2014 tests must pass before and after
336
+ - Make one change at a time, verify after each
337
+ - Prefer small, focused commits
338
+ - Preserve API contracts unless explicitly changing
339
+ - Remove dead code and comments
340
+ - Improve naming as you go
341
+ - Don't mix formatting changes with logic changes
342
+ - Keep performance in mind \u2014 don't regress`,
343
+ tags: ["refactor", "modernization", "improvement"],
344
+ toolPreferences: ["read", "edit", "test", "git", "grep"]
345
+ }
346
+ ];
347
+
348
+ // src/models/mode-store.ts
349
+ var DefaultModeStore = class {
350
+ activeModeId = null;
351
+ modes;
352
+ configDir;
353
+ constructor(config) {
354
+ this.configDir = config.directory;
355
+ this.modes = [...DEFAULT_MODES];
356
+ }
357
+ async getActiveMode() {
358
+ if (!this.activeModeId) {
359
+ await this.loadActiveMode();
360
+ }
361
+ if (!this.activeModeId) return null;
362
+ return this.modes.find((m) => m.id === this.activeModeId) ?? null;
363
+ }
364
+ async setActiveMode(modeId) {
365
+ this.activeModeId = modeId;
366
+ await this.saveActiveMode();
367
+ }
368
+ async listModes() {
369
+ return [...this.modes];
370
+ }
371
+ async getMode(modeId) {
372
+ return this.modes.find((m) => m.id === modeId) ?? null;
373
+ }
374
+ async addMode(mode) {
375
+ const idx = this.modes.findIndex((m) => m.id === mode.id);
376
+ if (idx >= 0) {
377
+ this.modes[idx] = mode;
378
+ } else {
379
+ this.modes.push(mode);
380
+ }
381
+ }
382
+ async removeMode(modeId) {
383
+ const builtIn = DEFAULT_MODES.find((m) => m.id === modeId);
384
+ if (builtIn) {
385
+ throw new Error(`Cannot remove built-in mode "${modeId}"`);
386
+ }
387
+ this.modes = this.modes.filter((m) => m.id !== modeId);
388
+ if (this.activeModeId === modeId) {
389
+ this.activeModeId = null;
390
+ await this.saveActiveMode();
391
+ }
392
+ }
393
+ async loadActiveMode() {
394
+ try {
395
+ const configPath = path3.join(this.configDir, "mode.json");
396
+ const content = await fs.readFile(configPath, "utf8");
397
+ const data = JSON.parse(content);
398
+ this.activeModeId = data.activeMode ?? null;
399
+ } catch {
400
+ this.activeModeId = "default";
401
+ }
402
+ }
403
+ async saveActiveMode() {
404
+ try {
405
+ await fs.mkdir(this.configDir, { recursive: true });
406
+ const configPath = path3.join(this.configDir, "mode.json");
407
+ await fs.writeFile(
408
+ configPath,
409
+ JSON.stringify({ activeMode: this.activeModeId }, null, 2),
410
+ "utf8"
411
+ );
412
+ } catch {
413
+ }
414
+ }
415
+ };
416
+ async function loadProjectModes(modesDir) {
417
+ const modes = [];
418
+ try {
419
+ const entries = await fs.readdir(modesDir);
420
+ for (const entry of entries) {
421
+ if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
422
+ const filePath = path3.join(modesDir, entry);
423
+ const stat3 = await fs.stat(filePath);
424
+ if (!stat3.isFile()) continue;
425
+ const content = await fs.readFile(filePath, "utf8");
426
+ const id = path3.basename(entry, path3.extname(entry));
427
+ modes.push({
428
+ id,
429
+ name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
430
+ description: content.split("\n")[0] ?? id,
431
+ prompt: content,
432
+ tags: ["project"]
433
+ });
434
+ }
435
+ } catch {
436
+ }
437
+ return modes;
438
+ }
439
+ async function loadUserModes(modesDir) {
440
+ const modes = [];
441
+ try {
442
+ const manifestPath = path3.join(modesDir, "modes.json");
443
+ const content = await fs.readFile(manifestPath, "utf8");
444
+ const manifest = JSON.parse(content);
445
+ for (const mode of manifest.modes) {
446
+ modes.push(mode);
447
+ }
448
+ } catch {
449
+ }
450
+ return modes;
451
+ }
452
+
453
+ // src/types/blocks.ts
454
+ function isTextBlock(b) {
455
+ return b.type === "text";
456
+ }
457
+
458
+ // src/models/llm-selector.ts
459
+ var DEFAULT_SYSTEM_PROMPT = `You are a context pruning assistant. Given a conversation history and a token budget, decide which message ranges are worth keeping verbatim and which should be collapsed into summaries.
460
+
461
+ Output a JSON object with this structure:
462
+ {
463
+ "kept": [{"from": 0, "to": 5, "importance": "critical"}],
464
+ "collapsed": [{"from": 6, "to": 20, "summary": "optional summary"}],
465
+ "reasoning": "brief explanation of decisions"
466
+ }
467
+
468
+ Importance tiers:
469
+ - "critical": decisions, file edits, tool results that affect state, final answers
470
+ - "high": substantive tool use, complex reasoning, non-obvious observations
471
+ - "medium": routine exchanges, confirmations, straightforward Q&A
472
+
473
+ Rules:
474
+ - Always keep the most recent K pairs (preserve recency)
475
+ - Never collapse the final 2 user/assistant pairs (working memory)
476
+ - Preserve tool results that modified files or had external effects
477
+ - Collapse old, low-information exchanges (greetings, acknowledgements, etc.)
478
+ - If unsure, keep rather than collapse (errors are more costly than waste)
479
+
480
+ Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
481
+ function estimateTokens(messages) {
482
+ let total = 0;
483
+ for (const m of messages) {
484
+ if (typeof m.content === "string") {
485
+ total += Math.ceil(m.content.length / 4);
486
+ } else if (Array.isArray(m.content)) {
487
+ for (const b of m.content) {
488
+ if (b.type === "text") total += Math.ceil(b.text.length / 4);
489
+ else total += Math.ceil(JSON.stringify(b).length / 4);
490
+ }
491
+ }
492
+ }
493
+ return total;
494
+ }
495
+ function formatMessages(messages, maxChars = 8e3) {
496
+ const lines = [];
497
+ let used = 0;
498
+ for (let i = 0; i < messages.length; i++) {
499
+ const m = messages[i];
500
+ const role = m.role.padEnd(10, " ");
501
+ let text;
502
+ if (typeof m.content === "string") {
503
+ text = m.content.slice(0, 500);
504
+ } else {
505
+ const content = m.content;
506
+ text = content.filter(isTextBlock).map((b) => b.text).join(" ");
507
+ const toolUses = content.filter((b) => b.type === "tool_use");
508
+ if (toolUses.length > 0) {
509
+ text += ` [tools: ${toolUses.map((b) => b.name).join(", ")}]`;
510
+ }
511
+ }
512
+ const line = `[${i}][${role}]: ${text}`;
513
+ if (used + line.length > maxChars) break;
514
+ lines.push(line);
515
+ used += line.length;
516
+ }
517
+ return lines.join("\n");
518
+ }
519
+ var LLMSelector = class {
520
+ provider;
521
+ model;
522
+ maxContextTokens;
523
+ systemPrompt;
524
+ constructor(opts) {
525
+ this.provider = opts.provider;
526
+ this.model = opts.model ?? "unknown";
527
+ this.maxContextTokens = opts.maxContextTokens ?? 4e4;
528
+ this.systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
529
+ }
530
+ async select(messages, maxToKeep) {
531
+ const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
532
+ const historyText = formatMessages(messages);
533
+ const totalTokens = estimateTokens(messages);
534
+ const systemText = `${this.systemPrompt}
535
+
536
+ Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
537
+ `;
538
+ const budgetInstruction = totalTokens > effectiveBudget ? `
539
+
540
+ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiveBudget}). You MUST collapse enough to fit. Prefer collapsing older/lower-importance ranges.` : "";
541
+ const req = {
542
+ model: this.model,
543
+ system: [{ type: "text", text: systemText + budgetInstruction }],
544
+ messages: [{ role: "user", content: historyText }],
545
+ maxTokens: 1024
546
+ };
547
+ let raw;
548
+ try {
549
+ const ac = new AbortController();
550
+ const res = await this.provider.complete(req, { signal: ac.signal });
551
+ const textBlocks = res.content.filter(isTextBlock);
552
+ raw = textBlocks.map((b) => b.text).join("\n").trim();
553
+ } catch (err) {
554
+ return this.fallbackSelect(messages, effectiveBudget);
555
+ }
556
+ return this.parseSelectorOutput(raw, messages.length);
557
+ }
558
+ fallbackSelect(messages, budget) {
559
+ const toKeep = [];
560
+ const toCollapse = [];
561
+ let tokenCount = 0;
562
+ let startIdx = 0;
563
+ for (let i = messages.length - 1; i >= 0; i--) {
564
+ const m = messages[i];
565
+ const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
566
+ (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
567
+ 0
568
+ );
569
+ if (tokenCount + cost <= budget) {
570
+ tokenCount += cost;
571
+ } else {
572
+ startIdx = i + 1;
573
+ break;
574
+ }
575
+ }
576
+ if (startIdx > 0) {
577
+ toCollapse.push({ from: 0, to: startIdx - 1 });
578
+ }
579
+ toKeep.push({ from: startIdx, to: messages.length - 1, importance: "high" });
580
+ return {
581
+ kept: toKeep,
582
+ collapsed: toCollapse,
583
+ reasoning: `Fallback: kept last ${messages.length - startIdx} messages within ${budget} token budget`
584
+ };
585
+ }
586
+ parseSelectorOutput(raw, messageCount) {
587
+ const jsonStart = raw.indexOf("{");
588
+ const jsonEnd = raw.lastIndexOf("}");
589
+ if (jsonStart === -1 || jsonEnd === -1) {
590
+ return this.fallbackSelect(
591
+ Array.from({ length: messageCount }, () => ({ role: "user", content: "" })),
592
+ this.maxContextTokens
593
+ );
594
+ }
595
+ let parsed;
596
+ try {
597
+ parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));
598
+ } catch {
599
+ return this.fallbackSelect(
600
+ Array.from({ length: messageCount }, () => ({ role: "user", content: "" })),
601
+ this.maxContextTokens
602
+ );
603
+ }
604
+ const obj = parsed;
605
+ const kept = obj.kept ?? [];
606
+ const collapsed = obj.collapsed ?? [];
607
+ return {
608
+ kept: kept.map((k) => ({
609
+ from: k.from,
610
+ to: k.to,
611
+ importance: k.importance ?? "medium"
612
+ })),
613
+ collapsed: collapsed.map((c) => ({ from: c.from, to: c.to, summary: c.summary })),
614
+ reasoning: typeof obj.reasoning === "string" ? obj.reasoning : ""
615
+ };
616
+ }
617
+ };
618
+
619
+ export { DefaultModeStore, DefaultModelsRegistry, LLMSelector, classifyFamily, loadProjectModes, loadUserModes };
620
+ //# sourceMappingURL=index.js.map
621
+ //# sourceMappingURL=index.js.map