@game_ryo/lsji 1.1.0 → 1.2.1

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.
@@ -34,8 +34,9 @@ export class PromptManager {
34
34
  async initialize() {
35
35
  if (this.initialized) return;
36
36
 
37
- if (this.storage.db) {
38
- await this.storage.db.exec(`
37
+ // Use the new storage interface
38
+ if (typeof this.storage.exec === 'function') {
39
+ await this.storage.exec(`
39
40
  CREATE TABLE IF NOT EXISTS prompts (
40
41
  name TEXT NOT NULL,
41
42
  version TEXT NOT NULL,
@@ -78,8 +79,8 @@ export class PromptManager {
78
79
  this.templates.get(name).set(version, prompt);
79
80
 
80
81
  // Persist
81
- if (this.storage.db) {
82
- await this.storage.db.run(
82
+ if (typeof this.storage.run === 'function') {
83
+ await this.storage.run(
83
84
  `INSERT OR REPLACE INTO prompts (name, version, template, variables, description, created_at, updated_at)
84
85
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
85
86
  [name, version, template, JSON.stringify(extractedVars), description, prompt.createdAt, prompt.updatedAt]
@@ -102,18 +103,14 @@ export class PromptManager {
102
103
  return versions.get(version) || null;
103
104
  }
104
105
 
105
- // Get latest version
106
- const sortedVersions = Array.from(versions.keys()).sort((a, b) => {
107
- const parseVersion = v => v.split('.').map(Number);
108
- const va = parseVersion(a);
109
- const vb = parseVersion(b);
110
- for (let i = 0; i < 3; i++) {
111
- if (va[i] !== vb[i]) return vb[i] - va[i];
106
+ // Return latest version (highest semver)
107
+ let latest = null;
108
+ for (const [ver, prompt] of versions) {
109
+ if (!latest || this.compareVersions(ver, latest) > 0) {
110
+ latest = ver;
112
111
  }
113
- return 0;
114
- });
115
-
116
- return versions.get(sortedVersions[0]) || null;
112
+ }
113
+ return versions.get(latest);
117
114
  }
118
115
 
119
116
  /**
@@ -123,110 +120,79 @@ export class PromptManager {
123
120
  await this.initialize();
124
121
  const versions = this.templates.get(name);
125
122
  if (!versions) return [];
126
- return Array.from(versions.values()).sort((a, b) =>
127
- new Date(b.createdAt) - new Date(a.createdAt)
128
- );
123
+ return Array.from(versions.values());
129
124
  }
130
125
 
131
126
  /**
132
- * Render a prompt with variables
127
+ * Render a template with variables
133
128
  */
134
- async render(name, variables, version = null) {
129
+ async render(name, variables = {}, version = null) {
135
130
  const prompt = await this.get(name, version);
136
131
  if (!prompt) {
137
132
  throw new Error(`Prompt not found: ${name}${version ? `@${version}` : ''}`);
138
133
  }
139
134
 
140
- // Check required variables
141
- for (const v of prompt.variables) {
142
- if (!(v in variables)) {
143
- throw new Error(`Missing required variable: ${v}`);
144
- }
145
- }
146
-
147
- // Substitute variables
148
135
  let rendered = prompt.template;
149
136
  for (const [key, value] of Object.entries(variables)) {
150
- const placeholder = `{{${key}}}`;
151
- rendered = rendered.replaceAll(placeholder, String(value));
137
+ rendered = rendered.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
152
138
  }
153
139
 
154
- return rendered;
155
- }
156
-
157
- /**
158
- * Render multiple prompts (for system + user messages)
159
- */
160
- async renderAll(prompts, variables) {
161
- const results = [];
162
- for (const { name, version, role = 'user' } of prompts) {
163
- const content = await this.render(name, variables, version);
164
- results.push({ role, content });
140
+ // Check for missing variables
141
+ const missingVars = prompt.variables.filter(v => !(v in variables));
142
+ if (missingVars.length > 0) {
143
+ console.warn(`Missing variables for prompt ${name}: ${missingVars.join(', ')}`);
165
144
  }
166
- return results;
145
+
146
+ return rendered;
167
147
  }
168
148
 
169
149
  /**
170
150
  * Extract variables from template
171
151
  */
172
152
  extractVariables(template) {
173
- const matches = template.match(/{{(\w+)}}/g);
153
+ const matches = template.match(/\{\{(\w+)\}\}/g);
174
154
  if (!matches) return [];
175
155
  return [...new Set(matches.map(m => m.slice(2, -2)))];
176
156
  }
177
157
 
178
158
  /**
179
- * List all prompt names
159
+ * Compare semantic versions
180
160
  */
181
- async list() {
182
- await this.initialize();
183
- return Array.from(this.templates.keys());
184
- }
185
-
186
- /**
187
- * Delete a prompt version
188
- */
189
- async delete(name, version) {
190
- await this.initialize();
191
-
192
- const versions = this.templates.get(name);
193
- if (!versions || !versions.has(version)) {
194
- return false;
195
- }
196
-
197
- versions.delete(version);
198
-
199
- if (this.storage.db) {
200
- await this.storage.db.run(
201
- 'DELETE FROM prompts WHERE name = ? AND version = ?',
202
- [name, version]
203
- );
204
- }
205
-
206
- return true;
161
+ compareVersions(a, b) {
162
+ const parse = v => v.split('.').map(Number);
163
+ const [aMajor, aMinor, aPatch] = parse(a);
164
+ const [bMajor, bMinor, bPatch] = parse(b);
165
+ if (aMajor !== bMajor) return aMajor - bMajor;
166
+ if (aMinor !== bMinor) return aMinor - bMinor;
167
+ return aPatch - bPatch;
207
168
  }
208
169
 
209
170
  /**
210
171
  * Load all prompts from storage
211
172
  */
212
- async loadAll() {
173
+ async loadFromStorage() {
213
174
  await this.initialize();
214
175
 
215
- if (this.storage.db) {
216
- const rows = await this.storage.db.all('SELECT * FROM prompts');
176
+ if (typeof this.storage.all === 'function') {
177
+ const rows = await this.storage.all(
178
+ 'SELECT * FROM prompts ORDER BY name, version'
179
+ );
180
+
217
181
  for (const row of rows) {
218
- if (!this.templates.has(row.name)) {
219
- this.templates.set(row.name, new Map());
220
- }
221
- this.templates.get(row.name).set(row.version, {
182
+ const prompt = {
222
183
  name: row.name,
223
184
  version: row.version,
224
185
  template: row.template,
225
- variables: JSON.parse(row.variables || '[]'),
186
+ variables: JSON.parse(row.variables),
226
187
  description: row.description,
227
188
  createdAt: row.created_at,
228
189
  updatedAt: row.updated_at,
229
- });
190
+ };
191
+
192
+ if (!this.templates.has(prompt.name)) {
193
+ this.templates.set(prompt.name, new Map());
194
+ }
195
+ this.templates.get(prompt.name).set(prompt.version, prompt);
230
196
  }
231
197
  }
232
198
  }
@@ -237,79 +203,31 @@ export class PromptManager {
237
203
  */
238
204
  export const BUILTIN_PROMPTS = {
239
205
  'system:react': {
240
- template: `You are an AI assistant that uses the ReAct pattern (Reasoning + Acting) to solve tasks.
206
+ name: 'system:react',
207
+ version: '1.0.0',
208
+ template: `You are an AI assistant that uses the ReAct (Reasoning + Acting) pattern.
241
209
 
242
210
  You have access to the following tools:
243
211
  {{tools}}
244
212
 
245
- When you need to use a tool, respond with:
213
+ Your task is: {{task}}
214
+
215
+ Use the following format:
216
+
246
217
  THOUGHT: Your reasoning about what to do next
247
- ACTION: The tool name to use
218
+ ACTION: The tool to use (must be one of the available tools)
248
219
  ACTION_INPUT: The parameters for the tool
249
220
 
250
- After the tool returns, you'll see:
251
- OBSERVATION: The result
252
-
253
- Continue this pattern until you can provide the final answer.
221
+ When you have the final answer, respond directly without THOUGHT/ACTION format.
254
222
 
255
- Current task: {{task}}`,
223
+ Begin!`,
256
224
  variables: ['tools', 'task'],
257
- description: 'ReAct system prompt with tool definitions',
258
- },
259
-
260
- 'system:planner': {
261
- template: `You are a planning agent. Break down the task into a sequence of steps.
262
-
263
- Task: {{task}}
264
-
265
- Available tools: {{tools}}
266
-
267
- Create a plan with numbered steps. Each step should specify:
268
- 1. What tool to use (if any)
269
- 2. What parameters to pass
270
- 3. What you expect to learn or achieve
271
-
272
- Output as JSON:
273
- {
274
- "steps": [
275
- {"step": 1, "tool": "tool_name", "params": {}, "description": "..."}
276
- ]
277
- }`,
278
- variables: ['task', 'tools'],
279
- description: 'Planning agent prompt',
280
- },
281
-
282
- 'system:code-reviewer': {
283
- template: `You are an expert code reviewer. Analyze the provided code for:
284
- - Bugs and logic errors
285
- - Security vulnerabilities
286
- - Performance issues
287
- - Code style and best practices
288
- - Test coverage gaps
289
-
290
- Code to review:
291
- {{code}}
292
-
293
- Context: {{context}}
294
-
295
- Provide your review in this format:
296
- ## Summary
297
- Brief overall assessment
298
-
299
- ## Issues Found
300
- - [Severity] File:Line - Description
301
-
302
- ## Suggestions
303
- - Improvement suggestions
304
-
305
- ## Approved: true/false`,
306
- variables: ['code', 'context'],
307
- description: 'Code review prompt',
225
+ description: 'System prompt for ReAct agent',
308
226
  },
309
227
  };
310
228
 
311
229
  /**
312
- * Create prompt manager with built-in templates
230
+ * Create prompt manager from config
313
231
  */
314
232
  export async function createPromptManager(config = {}) {
315
233
  const storage = await createStorage(
@@ -323,6 +241,7 @@ export async function createPromptManager(config = {}) {
323
241
  // Register built-in prompts
324
242
  for (const [name, prompt] of Object.entries(BUILTIN_PROMPTS)) {
325
243
  await manager.register(name, prompt.template, {
244
+ version: prompt.version,
326
245
  variables: prompt.variables,
327
246
  description: prompt.description,
328
247
  });
@@ -280,7 +280,7 @@ export function createApp(config = {}) {
280
280
  connectedClients.add(socket.id);
281
281
  console.log(`Client connected: ${socket.id} (total: ${connectedClients.size})`);
282
282
 
283
- // Send current state - use Promise.all for async operations
283
+ // Send current state
284
284
  Promise.all(
285
285
  Array.from(activeRuns.entries()).map(async ([runId, run]) => {
286
286
  const approvals = await run.hitl.getPendingApprovals(50);
@@ -316,12 +316,7 @@ export function createApp(config = {}) {
316
316
  });
317
317
  });
318
318
 
319
- // Broadcast helper
320
- function broadcast(event, data) {
321
- io.emit(event, data);
322
- }
323
-
324
- return { app, httpServer, io, broadcast, activeRuns };
319
+ return { app, httpServer, io, activeRuns };
325
320
  }
326
321
 
327
322
  /**
@@ -362,7 +357,7 @@ async function runAgent(runId, task, hitlGate, budgetCtrl, workflowId, io) {
362
357
  io.to(`run:${runId}`).emit('thought:new', run.thoughts[run.thoughts.length - 1]);
363
358
 
364
359
  io.to(`run:${runId}`).emit('run:completed', { runId, result });
365
- broadcast('run:updated', { runId, status: run.status, result });
360
+ io.emit('run:updated', { runId, status: run.status, result });
366
361
 
367
362
  } catch (error) {
368
363
  run.status = 'error';
@@ -377,7 +372,7 @@ async function runAgent(runId, task, hitlGate, budgetCtrl, workflowId, io) {
377
372
  io.to(`run:${runId}`).emit('thought:new', run.thoughts[run.thoughts.length - 1]);
378
373
 
379
374
  io.to(`run:${runId}`).emit('run:error', { runId, error: error.message });
380
- broadcast('run:updated', { runId, status: 'error', error: error.message });
375
+ io.emit('run:updated', { runId, status: 'error', error: error.message });
381
376
  }
382
377
  }
383
378
 
@@ -385,7 +380,7 @@ async function runAgent(runId, task, hitlGate, budgetCtrl, workflowId, io) {
385
380
  * Start the server
386
381
  */
387
382
  export async function startServer(config = {}) {
388
- const { app, httpServer, io, broadcast, activeRuns: runs } = createApp(config);
383
+ const { app, httpServer, io, activeRuns: runs } = createApp(config);
389
384
 
390
385
  const port = config.port || process.env.LSJI_SERVER_PORT || 3456;
391
386
  const host = config.host || '0.0.0.0';
@@ -394,7 +389,7 @@ export async function startServer(config = {}) {
394
389
  httpServer.listen(port, host, () => {
395
390
  console.log(`LSJI Server running at http://${host}:${port}`);
396
391
  console.log(`WebSocket ready for connections`);
397
- resolve({ app, httpServer, io, broadcast, activeRuns: runs, port, host });
392
+ resolve({ app, httpServer, io, activeRuns: runs, port, host });
398
393
  });
399
394
  });
400
395
  }