loki-mode 5.17.0 → 5.18.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/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with zero human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v5.17.0
6
+ # Loki Mode v5.18.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -253,4 +253,4 @@ Auto-detected or force with `LOKI_COMPLEXITY`:
253
253
 
254
254
  ---
255
255
 
256
- **v5.17.0 | Unified Event Bus, MCP Integration, Complete Memory System | ~250 lines core**
256
+ **v5.18.0 | Security Fixes, Memory Integration, Cross-Tool Synergy | ~250 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.17.0
1
+ 5.18.0
@@ -6,7 +6,30 @@
6
6
 
7
7
  import { cliBridge } from "../services/cli-bridge.ts";
8
8
  import { eventBus } from "../services/event-bus.ts";
9
- import type { HealthResponse } from "../types/api.ts";
9
+ import type { HealthResponse, Phase } from "../types/api.ts";
10
+
11
+ // Base path for memory storage
12
+ const MEMORY_BASE_PATH = ".loki/memory";
13
+
14
+ /**
15
+ * Memory pattern summary for status response
16
+ */
17
+ interface PatternSummary {
18
+ id: string;
19
+ pattern: string;
20
+ category: string;
21
+ confidence: number;
22
+ }
23
+
24
+ /**
25
+ * Memory context included in detailed status
26
+ */
27
+ interface MemoryContext {
28
+ available: boolean;
29
+ currentPhase: Phase | null;
30
+ relevantPatterns: PatternSummary[];
31
+ patternCount: number;
32
+ }
10
33
 
11
34
  const startTime = Date.now();
12
35
  const version = Deno.env.get("LOKI_VERSION") || "dev";
@@ -87,6 +110,13 @@ export async function detailedStatus(_req: Request): Promise<Response> {
87
110
  const completedCount = sessions.filter((s) => s.status === "completed").length;
88
111
  const failedCount = sessions.filter((s) => s.status === "failed").length;
89
112
 
113
+ // Determine current phase from active session
114
+ const runningSession = sessions.find((s) => s.status === "running");
115
+ const currentPhase = runningSession?.currentPhase || null;
116
+
117
+ // Get memory context with relevant patterns
118
+ const memoryContext = await getMemoryContext(currentPhase);
119
+
90
120
  return new Response(
91
121
  JSON.stringify({
92
122
  version,
@@ -109,6 +139,7 @@ export async function detailedStatus(_req: Request): Promise<Response> {
109
139
  denoVersion: Deno.version.deno,
110
140
  v8Version: Deno.version.v8,
111
141
  },
142
+ memoryContext,
112
143
  }),
113
144
  {
114
145
  headers: { "Content-Type": "application/json" },
@@ -147,6 +178,107 @@ async function checkProviders(): Promise<{
147
178
  return { claude, codex, gemini };
148
179
  }
149
180
 
181
+ /**
182
+ * Get memory context with relevant patterns for the current phase
183
+ */
184
+ async function getMemoryContext(currentPhase: Phase | null): Promise<MemoryContext> {
185
+ const emptyContext: MemoryContext = {
186
+ available: false,
187
+ currentPhase,
188
+ relevantPatterns: [],
189
+ patternCount: 0,
190
+ };
191
+
192
+ try {
193
+ // Build category filter based on current phase
194
+ const categoryFilter = currentPhase ? `'${currentPhase}'` : "None";
195
+
196
+ const script = `
197
+ import sys
198
+ import json
199
+ sys.path.insert(0, '.')
200
+
201
+ try:
202
+ from memory.engine import MemoryEngine
203
+ from memory.storage import MemoryStorage
204
+
205
+ storage = MemoryStorage('${MEMORY_BASE_PATH}')
206
+ engine = MemoryEngine(storage=storage, base_path='${MEMORY_BASE_PATH}')
207
+
208
+ # Get patterns, optionally filtered by phase/category
209
+ category = ${categoryFilter}
210
+
211
+ # Get all patterns first to get total count
212
+ all_patterns = engine.find_patterns(min_confidence=0.5)
213
+ total_count = len(all_patterns)
214
+
215
+ # Get relevant patterns for the current phase
216
+ if category:
217
+ # Try to find patterns matching the phase category
218
+ phase_patterns = engine.find_patterns(category=category, min_confidence=0.5)
219
+ if not phase_patterns:
220
+ # Fall back to highest confidence patterns
221
+ phase_patterns = sorted(all_patterns, key=lambda p: getattr(p, 'confidence', 0.5) if hasattr(p, 'confidence') else p.get('confidence', 0.5), reverse=True)
222
+ else:
223
+ # No phase, return highest confidence patterns
224
+ phase_patterns = sorted(all_patterns, key=lambda p: getattr(p, 'confidence', 0.5) if hasattr(p, 'confidence') else p.get('confidence', 0.5), reverse=True)
225
+
226
+ # Limit to top 3
227
+ top_patterns = phase_patterns[:3]
228
+
229
+ results = []
230
+ for p in top_patterns:
231
+ p_dict = p.to_dict() if hasattr(p, 'to_dict') else (p.__dict__ if hasattr(p, '__dict__') else p)
232
+ results.append({
233
+ 'id': p_dict.get('id', ''),
234
+ 'pattern': p_dict.get('pattern', ''),
235
+ 'category': p_dict.get('category', ''),
236
+ 'confidence': p_dict.get('confidence', 0.8)
237
+ })
238
+
239
+ output = {
240
+ 'available': True,
241
+ 'relevantPatterns': results,
242
+ 'patternCount': total_count
243
+ }
244
+ print(json.dumps(output))
245
+
246
+ except ImportError:
247
+ # Memory module not available
248
+ print(json.dumps({'available': False, 'relevantPatterns': [], 'patternCount': 0}))
249
+ except Exception as e:
250
+ # Any other error - memory not initialized or empty
251
+ print(json.dumps({'available': False, 'relevantPatterns': [], 'patternCount': 0, 'error': str(e)}))
252
+ `;
253
+
254
+ const command = new Deno.Command("python3", {
255
+ args: ["-c", script],
256
+ cwd: Deno.cwd(),
257
+ stdout: "piped",
258
+ stderr: "piped",
259
+ });
260
+
261
+ const { code, stdout } = await command.output();
262
+
263
+ if (code !== 0) {
264
+ return emptyContext;
265
+ }
266
+
267
+ const result = new TextDecoder().decode(stdout);
268
+ const parsed = JSON.parse(result.trim());
269
+
270
+ return {
271
+ available: parsed.available || false,
272
+ currentPhase,
273
+ relevantPatterns: parsed.relevantPatterns || [],
274
+ patternCount: parsed.patternCount || 0,
275
+ };
276
+ } catch {
277
+ // Memory system not available or error occurred
278
+ return emptyContext;
279
+ }
280
+ }
281
+
150
282
  /**
151
283
  * Format uptime to human-readable string
152
284
  */
@@ -263,3 +263,29 @@ Deno.test("GET /api/status returns detailed status", async () => {
263
263
  assertExists(data.events);
264
264
  assertExists(data.system);
265
265
  });
266
+
267
+ Deno.test("GET /api/status includes memory context", async () => {
268
+ const handler = createHandler({ port: 8420, host: "localhost", cors: true, auth: false });
269
+ const req = createRequest("GET", "/api/status");
270
+ const res = await handler(req);
271
+
272
+ assertEquals(res.status, 200);
273
+ const data = await res.json();
274
+
275
+ // Verify memoryContext structure exists
276
+ assertExists(data.memoryContext);
277
+ assertEquals(typeof data.memoryContext.available, "boolean");
278
+ assertEquals(Array.isArray(data.memoryContext.relevantPatterns), true);
279
+ assertEquals(typeof data.memoryContext.patternCount, "number");
280
+
281
+ // Patterns array should be limited to max 3
282
+ assertEquals(data.memoryContext.relevantPatterns.length <= 3, true);
283
+
284
+ // Each pattern should have required fields if there are any
285
+ for (const pattern of data.memoryContext.relevantPatterns) {
286
+ assertExists(pattern.id);
287
+ assertExists(pattern.pattern);
288
+ assertExists(pattern.category);
289
+ assertExists(pattern.confidence);
290
+ }
291
+ });
package/autonomy/loki CHANGED
@@ -105,6 +105,7 @@ fi
105
105
 
106
106
  RUN_SH="$SKILL_DIR/autonomy/run.sh"
107
107
  SANDBOX_SH="$SKILL_DIR/autonomy/sandbox.sh"
108
+ EMIT_SH="$SKILL_DIR/events/emit.sh"
108
109
  LOKI_DIR=".loki"
109
110
 
110
111
  # Get version from VERSION file
@@ -116,6 +117,15 @@ get_version() {
116
117
  fi
117
118
  }
118
119
 
120
+ # Emit event (non-blocking)
121
+ # Usage: emit_event <type> <source> <action> [key=value ...]
122
+ emit_event() {
123
+ if [ -f "$EMIT_SH" ]; then
124
+ # Run in background to be non-blocking
125
+ ("$EMIT_SH" "$@" >/dev/null 2>&1 &)
126
+ fi
127
+ }
128
+
119
129
  # Show help
120
130
  show_help() {
121
131
  local version=$(get_version)
@@ -308,6 +318,7 @@ cmd_start() {
308
318
  fi
309
319
 
310
320
  echo -e "${GREEN}Starting Loki Mode in Docker sandbox...${NC}"
321
+ emit_event session cli start "provider=$effective_provider" "sandbox=true" "prd_path=${prd_file:-}"
311
322
  exec "$SANDBOX_SH" start "${args[@]}"
312
323
  fi
313
324
 
@@ -321,6 +332,9 @@ cmd_start() {
321
332
  fi
322
333
  echo ""
323
334
 
335
+ # Emit session start event
336
+ emit_event session cli start "provider=$effective_provider" "prd_path=${prd_file:-}"
337
+
324
338
  exec "$RUN_SH" "${args[@]}"
325
339
  }
326
340
 
@@ -346,6 +360,8 @@ cmd_stop() {
346
360
 
347
361
  if is_session_running; then
348
362
  touch "$LOKI_DIR/STOP"
363
+ # Emit session stop event
364
+ emit_event session cli stop "reason=user_requested"
349
365
  echo -e "${RED}STOP signal sent. Execution will halt immediately.${NC}"
350
366
  else
351
367
  echo -e "${YELLOW}No active session running.${NC}"
@@ -367,6 +383,8 @@ cmd_pause() {
367
383
 
368
384
  if is_session_running; then
369
385
  touch "$LOKI_DIR/PAUSE"
386
+ # Emit session pause event
387
+ emit_event session cli pause "reason=user_requested"
370
388
  echo -e "${YELLOW}PAUSE signal sent. Execution will pause after current task.${NC}"
371
389
  echo -e "${DIM}Resume with: loki resume${NC}"
372
390
  else
@@ -397,6 +415,8 @@ cmd_resume() {
397
415
  fi
398
416
 
399
417
  if [ -n "$removed_signal" ]; then
418
+ # Emit session resume event
419
+ emit_event session cli resume "cleared_signal=$removed_signal"
400
420
  if is_session_running; then
401
421
  echo -e "${GREEN}$removed_signal signal cleared. Session will resume automatically.${NC}"
402
422
  else
@@ -0,0 +1,137 @@
1
+ # Loki Mode Synergy Implementation Tasks
2
+
3
+ This document tracks all implementation tasks to achieve tool synergy and competitive leadership.
4
+
5
+ ---
6
+
7
+ ## Phase 1: Event Bus Foundation (v5.17.0) - COMPLETED
8
+
9
+ | Task ID | Task | Status | Notes |
10
+ |---------|------|--------|-------|
11
+ | SYN-001 | Create Python event bus module | Done | events/bus.py |
12
+ | SYN-002 | Create TypeScript event bus module | Done | events/bus.ts |
13
+ | SYN-003 | Create Bash event emitter helper | Done | events/emit.sh |
14
+ | SYN-004 | Add event bus tests | Done | 10/10 passing |
15
+ | SYN-005 | Document event bus architecture | Done | SYNERGY-ROADMAP.md |
16
+
17
+ ---
18
+
19
+ ## Phase 2: Memory Integration (v5.18.0) - PENDING
20
+
21
+ | Task ID | Task | Status | Priority | Notes |
22
+ |---------|------|--------|----------|-------|
23
+ | SYN-006 | Create unified memory access layer | Pending | High | Single interface for all tools |
24
+ | SYN-007 | Add memory context to VS Code sidebar | Pending | High | Show relevant patterns for current file |
25
+ | SYN-008 | Add memory retrieval to CLI start | Pending | Medium | Load relevant context at session start |
26
+ | SYN-009 | Add automatic context loading to MCP | Pending | High | MCP tools auto-load memory |
27
+ | SYN-010 | Add memory-informed suggestions to API | Pending | Medium | /api/suggestions endpoint |
28
+ | SYN-011 | Integrate VS Code file edits with memory | Pending | Medium | Record edits as episodes |
29
+
30
+ ---
31
+
32
+ ## Phase 3: Smart State Sync (v5.19.0) - PENDING
33
+
34
+ | Task ID | Task | Status | Priority | Notes |
35
+ |---------|------|--------|----------|-------|
36
+ | SYN-012 | Create centralized state manager | Pending | High | Single source of truth |
37
+ | SYN-013 | Replace direct file reads with state manager | Pending | High | All components use manager |
38
+ | SYN-014 | Add optimistic updates with conflict resolution | Pending | Medium | Handle concurrent writes |
39
+ | SYN-015 | Add state versioning for rollback | Pending | Low | Recovery capability |
40
+ | SYN-016 | Add state change notifications | Pending | High | Real-time sync |
41
+
42
+ ---
43
+
44
+ ## Phase 4: Cross-Tool Learning (v5.20.0) - PENDING
45
+
46
+ | Task ID | Task | Status | Priority | Notes |
47
+ |---------|------|--------|----------|-------|
48
+ | SYN-017 | Define learning signal types | Pending | High | What can be learned |
49
+ | SYN-018 | Implement learning collectors in CLI | Pending | Medium | CLI emits learning signals |
50
+ | SYN-019 | Implement learning collectors in API | Pending | Medium | API emits learning signals |
51
+ | SYN-020 | Implement learning collectors in VS Code | Pending | Medium | VS Code emits learning signals |
52
+ | SYN-021 | Implement learning collectors in MCP | Pending | Medium | MCP emits learning signals |
53
+ | SYN-022 | Create learning aggregator | Pending | High | Consolidate signals |
54
+ | SYN-023 | Add learning-based suggestions | Pending | High | Use learnings to improve |
55
+ | SYN-024 | Add learning metrics dashboard | Pending | Low | Visualize learning |
56
+
57
+ ---
58
+
59
+ ## Phase 5: Unified Dashboard (v5.21.0) - PENDING
60
+
61
+ | Task ID | Task | Status | Priority | Notes |
62
+ |---------|------|--------|----------|-------|
63
+ | SYN-025 | Extract dashboard as reusable web components | Pending | High | Standalone components |
64
+ | SYN-026 | Create VS Code webview integration | Pending | High | Embed dashboard in VS Code |
65
+ | SYN-027 | Unify styling and behavior | Pending | Medium | Consistent UX |
66
+ | SYN-028 | Add dashboard feature parity check | Pending | Low | Ensure all features available |
67
+
68
+ ---
69
+
70
+ ## Competitive Gap Tasks (vs claude-flow, claude-mem, etc.)
71
+
72
+ | Task ID | Task | Status | Priority | Competitor Gap |
73
+ |---------|------|--------|----------|----------------|
74
+ | COMP-001 | Implement swarm intelligence patterns | Pending | Medium | claude-flow has 60+ agents |
75
+ | COMP-002 | Add Byzantine fault tolerance | Pending | Low | claude-flow resilience |
76
+ | COMP-003 | Improve embedding quality | Pending | Medium | claude-mem uses OpenAI |
77
+ | COMP-004 | Add importance scoring for memories | Pending | High | claude-mem has decay |
78
+ | COMP-005 | Implement memory namespaces | Pending | Medium | claude-mem project isolation |
79
+ | COMP-006 | Add real-time collaboration | Pending | Low | Multi-user support |
80
+ | COMP-007 | Improve context window optimization | Pending | High | Better token efficiency |
81
+
82
+ ---
83
+
84
+ ## Security Tasks (from Peer Review)
85
+
86
+ | Task ID | Task | Status | Priority | Notes |
87
+ |---------|------|--------|----------|-------|
88
+ | SEC-001 | Fix command injection in session-init.sh | Done | Critical | Fixed via env vars |
89
+ | SEC-002 | Fix command injection in store-episode.sh | Done | Critical | Fixed via env vars |
90
+ | SEC-003 | Fix path traversal in mcp/server.py | Done | High | Added validate_path() |
91
+ | SEC-004 | Improve fork bomb pattern detection | Done | Medium | Regex updated in v5.16.0 |
92
+ | SEC-005 | Add input sanitization to all hooks | Done | High | Via path validation |
93
+
94
+ ---
95
+
96
+ ## Quick Wins (Can Do Now)
97
+
98
+ | Task ID | Task | Status | Priority | Effort |
99
+ |---------|------|--------|----------|--------|
100
+ | QW-001 | CLI emits events on command execution | Done | High | Low |
101
+ | QW-002 | API returns relevant patterns with status | Done | Medium | Low |
102
+ | QW-003 | MCP emits tool call events | Done | Medium | Low |
103
+ | QW-004 | Add suggestions endpoint to API | Pending | Medium | Medium |
104
+ | QW-005 | VS Code shows memory stats in status bar | Done | Low | Low |
105
+
106
+ ---
107
+
108
+ ## Task Dependencies
109
+
110
+ ```
111
+ SYN-006 (unified memory) -> SYN-007, SYN-008, SYN-009, SYN-010, SYN-011
112
+ SYN-012 (state manager) -> SYN-013, SYN-014, SYN-015, SYN-016
113
+ SYN-017 (learning signals) -> SYN-018, SYN-019, SYN-020, SYN-021 -> SYN-022 -> SYN-023
114
+ SYN-025 (web components) -> SYN-026, SYN-027
115
+
116
+ SEC-001, SEC-002 -> Should be done ASAP (Critical)
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Progress Tracking
122
+
123
+ | Phase | Total Tasks | Completed | In Progress | Pending |
124
+ |-------|-------------|-----------|-------------|---------|
125
+ | Phase 1 (Event Bus) | 5 | 5 | 0 | 0 |
126
+ | Phase 2 (Memory) | 6 | 0 | 0 | 6 |
127
+ | Phase 3 (State) | 5 | 0 | 0 | 5 |
128
+ | Phase 4 (Learning) | 8 | 0 | 0 | 8 |
129
+ | Phase 5 (Dashboard) | 4 | 0 | 0 | 4 |
130
+ | Competitive | 7 | 0 | 0 | 7 |
131
+ | Security | 5 | 0 | 0 | 5 |
132
+ | Quick Wins | 5 | 0 | 0 | 5 |
133
+ | **Total** | **45** | **5** | **0** | **40** |
134
+
135
+ ---
136
+
137
+ *Last Updated: 2026-02-03*
@@ -45,6 +45,11 @@ from .consolidation import (
45
45
  compress_episodes_to_pattern_desc,
46
46
  )
47
47
 
48
+ from .unified_access import (
49
+ UnifiedMemoryAccess,
50
+ MemoryContext,
51
+ )
52
+
48
53
  __all__ = [
49
54
  # Schemas
50
55
  "ActionEntry",
@@ -79,4 +84,7 @@ __all__ = [
79
84
  "Cluster",
80
85
  "compress_episode_to_summary",
81
86
  "compress_episodes_to_pattern_desc",
87
+ # Unified Access
88
+ "UnifiedMemoryAccess",
89
+ "MemoryContext",
82
90
  ]