0xkobold 0.5.2 → 0.6.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 (64) hide show
  1. package/README.md +88 -11
  2. package/dist/package.json +3 -3
  3. package/dist/skills/session-resume-skill.js +121 -0
  4. package/dist/skills/session-resume-skill.js.map +1 -0
  5. package/dist/src/agent/DraconicRunRegistry.js +0 -9
  6. package/dist/src/agent/DraconicRunRegistry.js.map +1 -1
  7. package/dist/src/agent/auth-profiles.js +290 -0
  8. package/dist/src/agent/auth-profiles.js.map +1 -0
  9. package/dist/src/agent/embedded-runner.js +115 -29
  10. package/dist/src/agent/embedded-runner.js.map +1 -1
  11. package/dist/src/agent/index.js +4 -0
  12. package/dist/src/agent/index.js.map +1 -1
  13. package/dist/src/agent/model-fallback.js +173 -0
  14. package/dist/src/agent/model-fallback.js.map +1 -0
  15. package/dist/src/agent/tools/spawn-agent.js.map +1 -1
  16. package/dist/src/cli/commands/gateway.js +4 -8
  17. package/dist/src/cli/commands/gateway.js.map +1 -1
  18. package/dist/src/cron/notifications.js +15 -5
  19. package/dist/src/cron/notifications.js.map +1 -1
  20. package/dist/src/cron/runner.js +15 -6
  21. package/dist/src/cron/runner.js.map +1 -1
  22. package/dist/src/event-bus/index.js.map +1 -1
  23. package/dist/src/extensions/core/gateway-status-extension.js +54 -0
  24. package/dist/src/extensions/core/gateway-status-extension.js.map +1 -0
  25. package/dist/src/gateway/gateway-server.js +383 -0
  26. package/dist/src/gateway/gateway-server.js.map +1 -0
  27. package/dist/src/gateway/index.js +15 -8
  28. package/dist/src/gateway/index.js.map +1 -1
  29. package/dist/src/gateway/methods/agent.js +348 -0
  30. package/dist/src/gateway/methods/agent.js.map +1 -0
  31. package/dist/src/gateway/methods/index.js +30 -0
  32. package/dist/src/gateway/methods/index.js.map +1 -0
  33. package/dist/src/gateway/methods/types.js +7 -0
  34. package/dist/src/gateway/methods/types.js.map +1 -0
  35. package/dist/src/gateway/persistence/AgentStore.js +6 -0
  36. package/dist/src/gateway/persistence/AgentStore.js.map +1 -1
  37. package/dist/src/gateway/protocol/frames.js +43 -0
  38. package/dist/src/gateway/protocol/frames.js.map +1 -0
  39. package/dist/src/gateway/protocol/index.js +5 -0
  40. package/dist/src/gateway/protocol/index.js.map +1 -0
  41. package/dist/src/gateway/quickstart.js +34 -0
  42. package/dist/src/gateway/quickstart.js.map +1 -0
  43. package/dist/src/index.js +33 -0
  44. package/dist/src/index.js.map +1 -1
  45. package/dist/src/memory/memory-integration.js +204 -0
  46. package/dist/src/memory/memory-integration.js.map +1 -0
  47. package/dist/src/memory/session-memory-bridge.js +177 -0
  48. package/dist/src/memory/session-memory-bridge.js.map +1 -0
  49. package/dist/src/memory/session-resume.js +265 -0
  50. package/dist/src/memory/session-resume.js.map +1 -0
  51. package/dist/src/memory/session-store.js +224 -0
  52. package/dist/src/memory/session-store.js.map +1 -0
  53. package/dist/src/skills/framework.js +104 -16
  54. package/dist/src/skills/framework.js.map +1 -1
  55. package/dist/src/tui/components/footer.js +106 -0
  56. package/dist/src/tui/components/footer.js.map +1 -0
  57. package/dist/src/tui/components/status-bar.js +82 -16
  58. package/dist/src/tui/components/status-bar.js.map +1 -1
  59. package/dist/src/tui/gateway-chat-client.js +218 -0
  60. package/dist/src/tui/gateway-chat-client.js.map +1 -0
  61. package/dist/src/tui/index.js +1 -0
  62. package/dist/src/tui/index.js.map +1 -1
  63. package/package.json +3 -3
  64. package/skills/session-resume-skill.ts +150 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Session Resume System
3
+ *
4
+ * Allows searching and resuming previous sessions.
5
+ * Auto-saves sessions on shutdown.
6
+ * Suggests relevant past sessions on startup.
7
+ */
8
+ import { eventBus } from "../event-bus";
9
+ import { getSessionStore } from "./session-store";
10
+ import { getSessionMemoryBridge } from "./session-memory-bridge";
11
+ class SessionResumeSystem {
12
+ store = getSessionStore();
13
+ bridge = getSessionMemoryBridge();
14
+ currentSessionKey;
15
+ shutdownInProgress = false;
16
+ constructor() {
17
+ console.log("[SessionResume] Initializing...");
18
+ this.setupShutdownHandlers();
19
+ this.setupEventListeners();
20
+ }
21
+ /**
22
+ * Setup graceful shutdown handlers
23
+ */
24
+ setupShutdownHandlers() {
25
+ // Ctrl+C (SIGINT)
26
+ process.on("SIGINT", async () => {
27
+ console.log("\n[SIGINT] Graceful shutdown...");
28
+ await this.performGracefulShutdown();
29
+ process.exit(0);
30
+ });
31
+ // SIGTERM (Docker kill, etc)
32
+ process.on("SIGTERM", async () => {
33
+ console.log("\n[SIGTERM] Graceful shutdown...");
34
+ await this.performGracefulShutdown();
35
+ process.exit(0);
36
+ });
37
+ // Before exit (last resort)
38
+ process.on("exit", (code) => {
39
+ if (!this.shutdownInProgress && this.currentSessionKey) {
40
+ console.log("[SessionResume] Emergency save on exit...");
41
+ // Synchronous fallback - can't use async here
42
+ try {
43
+ const fs = require("fs");
44
+ const path = require("path");
45
+ const os = require("os");
46
+ const dumpPath = path.join(os.homedir(), ".0xkobold", "emergency-sessions.json");
47
+ const sessions = this.store.list().map(k => ({ key: k, saved: Date.now() }));
48
+ fs.writeFileSync(dumpPath, JSON.stringify(sessions, null, 2));
49
+ }
50
+ catch { }
51
+ }
52
+ });
53
+ }
54
+ /**
55
+ * Graceful shutdown - save current session
56
+ */
57
+ async performGracefulShutdown() {
58
+ if (this.shutdownInProgress)
59
+ return;
60
+ this.shutdownInProgress = true;
61
+ try {
62
+ if (this.currentSessionKey) {
63
+ console.log(`[SessionResume] Saving session ${this.currentSessionKey.slice(0, 8)}...`);
64
+ await this.saveSessionToPerennial(this.currentSessionKey, "shutdown");
65
+ }
66
+ // Also save all active sessions
67
+ const activeSessions = this.store.getActiveSessions();
68
+ for (const session of activeSessions) {
69
+ if (session.messageCount > 0) {
70
+ await this.saveSessionToPerennial(session.sessionKey, "shutdown-batch");
71
+ }
72
+ }
73
+ console.log(`[SessionResume] Saved ${activeSessions.length} active sessions`);
74
+ }
75
+ catch (err) {
76
+ console.error("[SessionResume] Shutdown save failed:", err);
77
+ }
78
+ }
79
+ /**
80
+ * Setup event listeners
81
+ */
82
+ setupEventListeners() {
83
+ // Track current session
84
+ eventBus.on("gateway.session_connected", async (event) => {
85
+ const { sessionKey } = event.payload;
86
+ this.currentSessionKey = sessionKey;
87
+ console.log(`[SessionResume] Now tracking session: ${sessionKey.slice(0, 8)}`);
88
+ });
89
+ // When session disconnects
90
+ eventBus.on("gateway.session_disconnected", async (event) => {
91
+ const { sessionKey } = event.payload;
92
+ if (this.currentSessionKey === sessionKey) {
93
+ await this.saveSessionToPerennial(sessionKey, "disconnect");
94
+ this.currentSessionKey = undefined;
95
+ }
96
+ });
97
+ }
98
+ /**
99
+ * Save session summary to perennial memory
100
+ */
101
+ async saveSessionToPerennial(sessionKey, reason) {
102
+ const session = this.store.get(sessionKey);
103
+ if (!session || session.messageCount === 0)
104
+ return;
105
+ const enriched = await this.bridge.getEnrichedSession(sessionKey);
106
+ if (!enriched)
107
+ return;
108
+ // Build summary
109
+ const summary = enriched.conversationSummary || `Session with ${session.messageCount} messages`;
110
+ const content = `[Session ${reason}] ${sessionKey.slice(0, 8)}: ${summary}`;
111
+ // Emit to perennial
112
+ eventBus.emit("perennial.save", {
113
+ content,
114
+ category: "context",
115
+ tags: ["session-summary", `reason-${reason}`, `thread-${session.memoryThreadId}`],
116
+ importance: Math.min(0.9, 0.5 + session.messageCount * 0.01),
117
+ project: "0xKobold",
118
+ });
119
+ // Also save thread summary
120
+ eventBus.emit("perennial.save", {
121
+ content: `Conversation thread ${session.memoryThreadId}: ${summary}`,
122
+ category: "context",
123
+ tags: ["conversation-thread", `session-${sessionKey.slice(0, 8)}`],
124
+ importance: 0.7,
125
+ project: "0xKobold",
126
+ });
127
+ console.log(`[SessionResume] Saved session ${sessionKey.slice(0, 8)} (${session.messageCount} msgs)`);
128
+ }
129
+ /**
130
+ * Search through all sessions
131
+ */
132
+ async searchSessions(query, options = {}) {
133
+ const { limit = 10, includeInactive = true } = options;
134
+ // Get all sessions
135
+ const allSessions = includeInactive
136
+ ? this.store.list().map(k => this.store.get(k)).filter(Boolean)
137
+ : this.store.getActiveSessions();
138
+ // Score each session
139
+ const scored = await Promise.all(allSessions.map(async (session) => {
140
+ const enriched = await this.bridge.getEnrichedSession(session.sessionKey);
141
+ // Calculate relevance
142
+ const searchText = [
143
+ session.conversationSummary,
144
+ enriched?.conversationSummary,
145
+ ...enriched?.recentMemories.map(m => m.content) || [],
146
+ ].join(" ");
147
+ const relevanceScore = this.calculateRelevance(searchText, query);
148
+ return {
149
+ sessionKey: session.sessionKey,
150
+ memoryThreadId: session.memoryThreadId || "unknown",
151
+ lastActive: new Date(session.updatedAt).toISOString(),
152
+ messageCount: session.messageCount,
153
+ summary: session.conversationSummary,
154
+ relevanceScore,
155
+ preview: searchText.slice(0, 200) + "...",
156
+ };
157
+ }));
158
+ // Sort by relevance and return top
159
+ return scored
160
+ .filter(s => s.relevanceScore > 0.1)
161
+ .sort((a, b) => b.relevanceScore - a.relevanceScore)
162
+ .slice(0, limit);
163
+ }
164
+ /**
165
+ * Get resume suggestions for new session
166
+ */
167
+ async getResumeSuggestions(currentContext) {
168
+ // Get recent active sessions (not current)
169
+ const activeSessions = this.store.getActiveSessions()
170
+ .filter(s => s.sessionKey !== this.currentSessionKey)
171
+ .filter(s => s.messageCount > 0)
172
+ .slice(0, 5);
173
+ const suggestions = [];
174
+ for (const session of activeSessions) {
175
+ const enriched = await this.bridge.getEnrichedSession(session.sessionKey);
176
+ const lastMessage = session.conversationSummary?.split("\n").pop() || "Previous conversation";
177
+ const timeAgo = this.formatTimeAgo(session.updatedAt);
178
+ let reason = `Last active ${timeAgo}`;
179
+ if (currentContext && enriched?.conversationSummary) {
180
+ const relevance = this.calculateRelevance(enriched.conversationSummary, currentContext);
181
+ if (relevance > 0.3) {
182
+ reason = `Related to current topic (${Math.round(relevance * 100)}% match)`;
183
+ }
184
+ }
185
+ suggestions.push({
186
+ sessionKey: session.sessionKey,
187
+ reason,
188
+ lastMessage: lastMessage.slice(0, 100),
189
+ timeAgo,
190
+ });
191
+ }
192
+ return suggestions;
193
+ }
194
+ /**
195
+ * Resume a session by key or thread ID
196
+ */
197
+ async resumeSession(identifier) {
198
+ // Try as session key first
199
+ let session = this.store.get(identifier);
200
+ // Try as memory thread ID
201
+ if (!session) {
202
+ session = this.store.getByMemoryThread(identifier);
203
+ }
204
+ // Try partial match
205
+ if (!session) {
206
+ const all = this.store.list();
207
+ const partial = all.find(k => k.includes(identifier) || identifier.includes(k));
208
+ if (partial) {
209
+ session = this.store.get(partial);
210
+ }
211
+ }
212
+ if (!session) {
213
+ return { success: false, error: "Session not found" };
214
+ }
215
+ // Get full context
216
+ const context = await this.bridge.getMemoryContext(session.sessionKey);
217
+ const enriched = await this.bridge.getEnrichedSession(session.sessionKey);
218
+ // Build resume context
219
+ const resumeContext = [
220
+ `Resuming session ${session.sessionKey.slice(0, 8)}...`,
221
+ enriched?.conversationSummary ? `Previous: ${enriched.conversationSummary.slice(0, 500)}` : null,
222
+ enriched?.recentMemories?.length ?
223
+ `Recent memories:\n${enriched.recentMemories.slice(-5).map(m => `- ${m.content.slice(0, 80)}`).join("\n")}` : null,
224
+ ].filter(Boolean).join("\n\n");
225
+ return {
226
+ success: true,
227
+ sessionKey: session.sessionKey,
228
+ context: resumeContext,
229
+ };
230
+ }
231
+ /**
232
+ * Get current session stats
233
+ */
234
+ getCurrentSessionStats() {
235
+ const active = this.store.getActiveSessions();
236
+ return {
237
+ activeSessions: active.length,
238
+ currentSession: this.currentSessionKey?.slice(0, 8),
239
+ totalMessages: active.reduce((sum, s) => sum + s.messageCount, 0),
240
+ };
241
+ }
242
+ // Private helpers
243
+ calculateRelevance(text, query) {
244
+ const textWords = new Set(text.toLowerCase().split(/\s+/));
245
+ const queryWords = new Set(query.toLowerCase().split(/\s+/));
246
+ const intersection = [...queryWords].filter(w => textWords.has(w));
247
+ return intersection.length / Math.max(queryWords.size, 1);
248
+ }
249
+ formatTimeAgo(timestamp) {
250
+ const seconds = Math.floor((Date.now() - timestamp) / 1000);
251
+ if (seconds < 60)
252
+ return `${seconds}s ago`;
253
+ if (seconds < 3600)
254
+ return `${Math.floor(seconds / 60)}m ago`;
255
+ if (seconds < 86400)
256
+ return `${Math.floor(seconds / 3600)}h ago`;
257
+ return `${Math.floor(seconds / 86400)}d ago`;
258
+ }
259
+ }
260
+ // Singleton
261
+ const resumeSystem = new SessionResumeSystem();
262
+ export function getSessionResumeSystem() {
263
+ return resumeSystem;
264
+ }
265
+ //# sourceMappingURL=session-resume.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-resume.js","sourceRoot":"","sources":["../../../src/memory/session-resume.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Session Store (Phase 2 + Phase 5)
3
+ *
4
+ * SQLite-based session persistence for gateway.
5
+ * Phase 5: Links to memory systems for conversation continuity.
6
+ */
7
+ import { Database } from "bun:sqlite";
8
+ import { mkdirSync } from "node:fs";
9
+ import { dirname } from "node:path";
10
+ import { randomUUID } from "node:crypto";
11
+ const DB_PATH = `${process.env.HOME || "~"}/.0xkobold/sessions.db`;
12
+ let db = null;
13
+ function getDb() {
14
+ if (db)
15
+ return db;
16
+ mkdirSync(dirname(DB_PATH), { recursive: true });
17
+ db = new Database(DB_PATH);
18
+ db.run(`
19
+ CREATE TABLE IF NOT EXISTS sessions (
20
+ session_key TEXT PRIMARY KEY,
21
+ session_id TEXT NOT NULL,
22
+ agent_id TEXT,
23
+ updated_at INTEGER NOT NULL,
24
+ created_at INTEGER NOT NULL,
25
+ model_override TEXT,
26
+ provider_override TEXT,
27
+ thinking_level TEXT,
28
+ verbose_level TEXT,
29
+ auth_profile_override TEXT,
30
+ skills_snapshot TEXT,
31
+ cli_session_ids TEXT,
32
+ spawned_by TEXT,
33
+ system_prompt_report TEXT,
34
+ -- Phase 5: Memory integration columns
35
+ memory_thread_id TEXT,
36
+ perennial_session_id TEXT,
37
+ user_profile_id TEXT,
38
+ last_run_id TEXT,
39
+ conversation_summary TEXT,
40
+ message_count INTEGER DEFAULT 0
41
+ )
42
+ `);
43
+ db.run(`
44
+ CREATE INDEX IF NOT EXISTS idx_sessions_updated
45
+ ON sessions(updated_at)
46
+ `);
47
+ // Phase 5: Indexes for memory lookups
48
+ db.run(`
49
+ CREATE INDEX IF NOT EXISTS idx_sessions_memory_thread
50
+ ON sessions(memory_thread_id)
51
+ `);
52
+ db.run(`
53
+ CREATE INDEX IF NOT EXISTS idx_sessions_user_profile
54
+ ON sessions(user_profile_id)
55
+ `);
56
+ db.run(`
57
+ CREATE INDEX IF NOT EXISTS idx_sessions_perennial
58
+ ON sessions(perennial_session_id)
59
+ `);
60
+ return db;
61
+ }
62
+ function rowToEntry(row) {
63
+ return {
64
+ sessionId: row.session_id,
65
+ sessionKey: row.session_key,
66
+ agentId: row.agent_id,
67
+ updatedAt: row.updated_at,
68
+ createdAt: row.created_at,
69
+ modelOverride: row.model_override,
70
+ providerOverride: row.provider_override,
71
+ thinkingLevel: row.thinking_level,
72
+ verboseLevel: row.verbose_level,
73
+ authProfileOverride: row.auth_profile_override,
74
+ skillsSnapshot: row.skills_snapshot ? JSON.parse(row.skills_snapshot) : undefined,
75
+ cliSessionIds: row.cli_session_ids ? JSON.parse(row.cli_session_ids) : undefined,
76
+ spawnedBy: row.spawned_by,
77
+ systemPromptReport: row.system_prompt_report
78
+ ? JSON.parse(row.system_prompt_report)
79
+ : undefined,
80
+ // Phase 5
81
+ memoryThreadId: row.memory_thread_id,
82
+ perennialSessionId: row.perennial_session_id,
83
+ userProfileId: row.user_profile_id,
84
+ lastRunId: row.last_run_id,
85
+ conversationSummary: row.conversation_summary,
86
+ messageCount: row.message_count || 0,
87
+ };
88
+ }
89
+ export function createSessionStore() {
90
+ const db = getDb();
91
+ const cache = new Map();
92
+ // Load existing sessions into cache
93
+ const rows = db.query("SELECT * FROM sessions").all();
94
+ for (const row of rows) {
95
+ const entry = rowToEntry(row);
96
+ cache.set(entry.sessionKey, entry);
97
+ }
98
+ return {
99
+ get(sessionKey) {
100
+ // Check cache first
101
+ const cached = cache.get(sessionKey);
102
+ if (cached)
103
+ return cached;
104
+ // Try database
105
+ const row = db
106
+ .query("SELECT * FROM sessions WHERE session_key = ?")
107
+ .get(sessionKey);
108
+ if (!row)
109
+ return undefined;
110
+ const entry = rowToEntry(row);
111
+ cache.set(sessionKey, entry);
112
+ return entry;
113
+ },
114
+ set(sessionKey, entry) {
115
+ cache.set(sessionKey, entry);
116
+ const now = Date.now();
117
+ db.run(`
118
+ INSERT INTO sessions (
119
+ session_key, session_id, agent_id, updated_at, created_at,
120
+ model_override, provider_override, thinking_level, verbose_level,
121
+ auth_profile_override, skills_snapshot, cli_session_ids, spawned_by, system_prompt_report,
122
+ memory_thread_id, perennial_session_id, user_profile_id, last_run_id,
123
+ conversation_summary, message_count
124
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
125
+ ON CONFLICT(session_key) DO UPDATE SET
126
+ session_id = excluded.session_id,
127
+ agent_id = excluded.agent_id,
128
+ updated_at = excluded.updated_at,
129
+ model_override = excluded.model_override,
130
+ provider_override = excluded.provider_override,
131
+ thinking_level = excluded.thinking_level,
132
+ verbose_level = excluded.verbose_level,
133
+ auth_profile_override = excluded.auth_profile_override,
134
+ skills_snapshot = excluded.skills_snapshot,
135
+ cli_session_ids = excluded.cli_session_ids,
136
+ spawned_by = excluded.spawned_by,
137
+ system_prompt_report = excluded.system_prompt_report,
138
+ memory_thread_id = excluded.memory_thread_id,
139
+ perennial_session_id = excluded.perennial_session_id,
140
+ user_profile_id = excluded.user_profile_id,
141
+ last_run_id = excluded.last_run_id,
142
+ conversation_summary = excluded.conversation_summary,
143
+ message_count = excluded.message_count
144
+ `, [
145
+ sessionKey,
146
+ entry.sessionId,
147
+ entry.agentId ?? null,
148
+ now,
149
+ entry.createdAt ?? now,
150
+ entry.modelOverride ?? null,
151
+ entry.providerOverride ?? null,
152
+ entry.thinkingLevel ?? null,
153
+ entry.verboseLevel ?? null,
154
+ entry.authProfileOverride ?? null,
155
+ entry.skillsSnapshot ? JSON.stringify(entry.skillsSnapshot) : null,
156
+ entry.cliSessionIds ? JSON.stringify(entry.cliSessionIds) : null,
157
+ entry.spawnedBy ?? null,
158
+ entry.systemPromptReport ? JSON.stringify(entry.systemPromptReport) : null,
159
+ // Phase 5
160
+ entry.memoryThreadId ?? null,
161
+ entry.perennialSessionId ?? null,
162
+ entry.userProfileId ?? null,
163
+ entry.lastRunId ?? null,
164
+ entry.conversationSummary ?? null,
165
+ entry.messageCount ?? 0,
166
+ ]);
167
+ },
168
+ delete(sessionKey) {
169
+ cache.delete(sessionKey);
170
+ db.run("DELETE FROM sessions WHERE session_key = ?", [sessionKey]);
171
+ },
172
+ list() {
173
+ return Array.from(cache.keys());
174
+ },
175
+ cleanup(maxAgeMs) {
176
+ const cutoff = Date.now() - maxAgeMs;
177
+ // Remove from database
178
+ db.run("DELETE FROM sessions WHERE updated_at < ?", [cutoff]);
179
+ // Remove from cache
180
+ for (const [key, entry] of cache) {
181
+ if (entry.updatedAt < cutoff) {
182
+ cache.delete(key);
183
+ }
184
+ }
185
+ },
186
+ // Phase 5: Memory-aware queries
187
+ getByMemoryThread(threadId) {
188
+ const row = db
189
+ .query("SELECT * FROM sessions WHERE memory_thread_id = ? LIMIT 1")
190
+ .get(threadId);
191
+ if (!row)
192
+ return undefined;
193
+ return rowToEntry(row);
194
+ },
195
+ getActiveSessions() {
196
+ const cutoff = Date.now() - 24 * 60 * 60 * 1000; // 24 hours
197
+ const rows = db
198
+ .query("SELECT * FROM sessions WHERE updated_at > ? ORDER BY updated_at DESC")
199
+ .all(cutoff);
200
+ return rows.map(rowToEntry);
201
+ },
202
+ getSessionsForUser(userProfileId) {
203
+ const rows = db
204
+ .query("SELECT * FROM sessions WHERE user_profile_id = ? ORDER BY updated_at DESC")
205
+ .all(userProfileId);
206
+ return rows.map(rowToEntry);
207
+ },
208
+ };
209
+ }
210
+ export function generateSessionId() {
211
+ return randomUUID();
212
+ }
213
+ // Global singleton
214
+ let globalStore = null;
215
+ export function getSessionStore() {
216
+ if (!globalStore) {
217
+ globalStore = createSessionStore();
218
+ }
219
+ return globalStore;
220
+ }
221
+ export function resetSessionStore() {
222
+ globalStore = null;
223
+ }
224
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../../src/memory/session-store.ts"],"names":[],"mappings":"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"}
@@ -176,34 +176,122 @@ export function resetSkillRegistry() {
176
176
  registry = null;
177
177
  }
178
178
  /**
179
- * Install skill from source
179
+ * Install skill from source (local path, git URL, or tarball)
180
180
  */
181
181
  export async function installSkill(source, targetDir) {
182
182
  const skillsDir = targetDir || SKILL_DIRS[1];
183
183
  try {
184
184
  // Create skills directory if needed
185
185
  await fs.mkdir(skillsDir, { recursive: true });
186
- // TODO(v0.5.1): Clone from git, extract from tarball, etc.
187
- // For now, assume source is a local path
188
- const skillName = path.basename(source);
189
- const targetPath = path.join(skillsDir, skillName);
190
- if (existsSync(targetPath)) {
191
- return { success: false, message: `Skill ${skillName} already installed` };
192
- }
193
- // Simple copy for now
194
- await fs.mkdir(targetPath, { recursive: true });
195
- const files = await fs.readdir(source);
196
- for (const file of files) {
197
- const src = path.join(source, file);
198
- const dest = path.join(targetPath, file);
199
- await fs.copyFile(src, dest);
186
+ // Detect source type and install accordingly
187
+ if (source.startsWith('git+') || source.endsWith('.git') || source.includes('github.com') || source.includes('gitlab.com')) {
188
+ return await installFromGit(source, skillsDir);
189
+ }
190
+ else if (source.endsWith('.tar.gz') || source.endsWith('.tgz')) {
191
+ return await installFromTarball(source, skillsDir);
192
+ }
193
+ else {
194
+ // Local path copy
195
+ return await installFromLocal(source, skillsDir);
200
196
  }
201
- return { success: true, message: `Installed ${skillName} to ${targetPath}` };
202
197
  }
203
198
  catch (error) {
204
199
  return { success: false, message: `Installation failed: ${error}` };
205
200
  }
206
201
  }
202
+ /**
203
+ * Install skill from local directory
204
+ */
205
+ async function installFromLocal(source, skillsDir) {
206
+ const skillName = path.basename(source);
207
+ const targetPath = path.join(skillsDir, skillName);
208
+ if (existsSync(targetPath)) {
209
+ return { success: false, message: `Skill ${skillName} already installed` };
210
+ }
211
+ await fs.mkdir(targetPath, { recursive: true });
212
+ const files = await fs.readdir(source);
213
+ for (const file of files) {
214
+ const src = path.join(source, file);
215
+ const dest = path.join(targetPath, file);
216
+ const stat = await fs.stat(src);
217
+ if (stat.isDirectory()) {
218
+ // Recursively copy directories (simple implementation)
219
+ await fs.mkdir(dest, { recursive: true });
220
+ }
221
+ else {
222
+ await fs.copyFile(src, dest);
223
+ }
224
+ }
225
+ return { success: true, message: `Installed ${skillName} from local path to ${targetPath}` };
226
+ }
227
+ /**
228
+ * Install skill from git repository
229
+ */
230
+ async function installFromGit(repoUrl, skillsDir) {
231
+ const { execSync } = await import('child_process');
232
+ // Extract skill name from repo URL
233
+ const repoName = path.basename(repoUrl, '.git');
234
+ const targetPath = path.join(skillsDir, repoName);
235
+ if (existsSync(targetPath)) {
236
+ return { success: false, message: `Skill ${repoName} already installed` };
237
+ }
238
+ try {
239
+ // Clone the repository
240
+ console.log(`[Skills] Cloning ${repoUrl}...`);
241
+ execSync(`git clone "${repoUrl}" "${targetPath}"`, {
242
+ cwd: skillsDir,
243
+ timeout: 60000,
244
+ stdio: 'pipe'
245
+ });
246
+ return { success: true, message: `Installed ${repoName} from git to ${targetPath}` };
247
+ }
248
+ catch (error) {
249
+ // Clean up partial clone
250
+ if (existsSync(targetPath)) {
251
+ await fs.rm(targetPath, { recursive: true, force: true });
252
+ }
253
+ throw new Error(`Git clone failed: ${error.message}`);
254
+ }
255
+ }
256
+ /**
257
+ * Install skill from tarball
258
+ */
259
+ async function installFromTarball(tarballPath, skillsDir) {
260
+ const { execSync } = await import('child_process');
261
+ const tarName = path.basename(tarballPath, '.tar.gz').replace('.tgz', '');
262
+ const extractDir = path.join(skillsDir, `__extract_${Date.now()}`);
263
+ try {
264
+ // Extract tarball
265
+ console.log(`[Skills] Extracting ${tarName}...`);
266
+ execSync(`tar -xzf "${tarballPath}" -C "${extractDir}"`, {
267
+ timeout: 30000,
268
+ stdio: 'pipe'
269
+ });
270
+ // Find the extracted directory (usually one subdir)
271
+ const entries = await fs.readdir(extractDir);
272
+ const skillDir = entries.find(e => !e.startsWith('.'));
273
+ if (!skillDir) {
274
+ throw new Error('No skill directory found in tarball');
275
+ }
276
+ const sourcePath = path.join(extractDir, skillDir);
277
+ const targetPath = path.join(skillsDir, skillDir);
278
+ if (existsSync(targetPath)) {
279
+ await fs.rm(extractDir, { recursive: true, force: true });
280
+ return { success: false, message: `Skill ${skillDir} already installed` };
281
+ }
282
+ // Move to final location
283
+ await fs.rename(sourcePath, targetPath);
284
+ await fs.rm(extractDir, { recursive: true, force: true });
285
+ return { success: true, message: `Installed ${skillDir} from tarball to ${targetPath}` };
286
+ }
287
+ catch (error) {
288
+ // Cleanup
289
+ if (existsSync(extractDir)) {
290
+ await fs.rm(extractDir, { recursive: true, force: true });
291
+ }
292
+ throw new Error(`Tarball extraction failed: ${error.message}`);
293
+ }
294
+ }
207
295
  /**
208
296
  * Get skill marketplace (curated list)
209
297
  */