create-walle 0.9.11 → 0.9.13

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 (167) hide show
  1. package/README.md +3 -3
  2. package/package.json +2 -2
  3. package/template/bin/dev.sh +7 -1
  4. package/template/bin/setup.js +53 -9
  5. package/template/bin/sync-images.js +53 -0
  6. package/template/builder-journal.md +17 -0
  7. package/template/claude-task-manager/api-prompts.js +98 -13
  8. package/template/claude-task-manager/api-reviews.js +82 -5
  9. package/template/claude-task-manager/db.js +32 -5
  10. package/template/claude-task-manager/docs/session-capture-foundation-design.md +1273 -0
  11. package/template/claude-task-manager/lib/claude-desktop-sessions.js +696 -0
  12. package/template/claude-task-manager/lib/coding-agent-models.js +49 -1
  13. package/template/claude-task-manager/lib/session-capture.js +421 -0
  14. package/template/claude-task-manager/lib/session-history.js +135 -15
  15. package/template/claude-task-manager/lib/session-jobs.js +10 -5
  16. package/template/claude-task-manager/lib/session-stream.js +87 -19
  17. package/template/claude-task-manager/lib/setup-provider-config.js +115 -0
  18. package/template/claude-task-manager/lib/walle-ctm-history.js +72 -0
  19. package/template/claude-task-manager/lib/walle-session-context.js +61 -0
  20. package/template/claude-task-manager/lib/walle-transcript.js +176 -0
  21. package/template/claude-task-manager/public/css/setup.css +35 -8
  22. package/template/claude-task-manager/public/css/walle-session.css +56 -0
  23. package/template/claude-task-manager/public/css/walle.css +120 -0
  24. package/template/claude-task-manager/public/index.html +814 -181
  25. package/template/claude-task-manager/public/js/message-renderer.js +148 -19
  26. package/template/claude-task-manager/public/js/reviews.js +120 -62
  27. package/template/claude-task-manager/public/js/setup.js +75 -31
  28. package/template/claude-task-manager/public/js/stream-view.js +115 -55
  29. package/template/claude-task-manager/public/js/walle-session.js +84 -2
  30. package/template/claude-task-manager/public/js/walle.js +308 -54
  31. package/template/claude-task-manager/server.js +1092 -146
  32. package/template/claude-task-manager/session-integrity.js +181 -54
  33. package/template/claude-task-manager/session-utils.js +123 -41
  34. package/template/claude-task-manager/workers/state-detectors/codex.js +5 -2
  35. package/template/package.json +1 -1
  36. package/template/wall-e/adapters/ctm.js +39 -18
  37. package/template/wall-e/agent-runners/contract.js +17 -0
  38. package/template/wall-e/agent-runners/index.js +22 -0
  39. package/template/wall-e/agent-runtime/harness.js +212 -0
  40. package/template/wall-e/agent-runtime/index.js +8 -0
  41. package/template/wall-e/agent-runtime/registry.js +67 -0
  42. package/template/wall-e/agent-runtime/session-store.js +179 -0
  43. package/template/wall-e/agent-runtime/spawn.js +208 -0
  44. package/template/wall-e/api-walle.js +174 -7
  45. package/template/wall-e/brain.js +266 -28
  46. package/template/wall-e/channels/policy.js +88 -0
  47. package/template/wall-e/channels/registry.js +15 -1
  48. package/template/wall-e/channels/reply-dispatcher.js +70 -0
  49. package/template/wall-e/channels/session-bindings.js +51 -0
  50. package/template/wall-e/chat/code-review-context.js +29 -0
  51. package/template/wall-e/chat.js +188 -42
  52. package/template/wall-e/coding/acp-adapter.js +188 -0
  53. package/template/wall-e/coding/agent-catalog.js +129 -0
  54. package/template/wall-e/coding/compaction-service.js +247 -0
  55. package/template/wall-e/coding/execution-trace.js +3 -0
  56. package/template/wall-e/coding/instruction-service.js +224 -0
  57. package/template/wall-e/coding/model-message.js +67 -0
  58. package/template/wall-e/coding/permission-rules-store.js +111 -0
  59. package/template/wall-e/coding/permission-service.js +266 -0
  60. package/template/wall-e/coding/prompt-bundle.js +67 -0
  61. package/template/wall-e/coding/prompt-runtime.js +243 -0
  62. package/template/wall-e/coding/provider-transform.js +188 -0
  63. package/template/wall-e/coding/runtime-mode.js +132 -0
  64. package/template/wall-e/coding/snapshot-service.js +155 -0
  65. package/template/wall-e/coding/stream-processor.js +268 -0
  66. package/template/wall-e/coding/task-tool.js +255 -0
  67. package/template/wall-e/coding/tool-registry.js +361 -0
  68. package/template/wall-e/coding/transcript-writer.js +143 -0
  69. package/template/wall-e/coding/workspace-replay.js +324 -0
  70. package/template/wall-e/coding-context.js +4 -22
  71. package/template/wall-e/coding-orchestrator.js +307 -18
  72. package/template/wall-e/coding-prompts.js +44 -3
  73. package/template/wall-e/context/context-builder.js +43 -1
  74. package/template/wall-e/context/topic-matcher.js +1 -1
  75. package/template/wall-e/eval/agent-runner.js +59 -13
  76. package/template/wall-e/eval/benchmarks/memory-retrieval.json +155 -57
  77. package/template/wall-e/eval/benchmarks.js +100 -16
  78. package/template/wall-e/eval/eval-orchestrator.js +218 -8
  79. package/template/wall-e/eval/harvester.js +62 -5
  80. package/template/wall-e/eval/head-to-head.js +23 -2
  81. package/template/wall-e/eval/humaneval-adapter.js +30 -5
  82. package/template/wall-e/eval/livecodebench-adapter.js +29 -5
  83. package/template/wall-e/eval/manifest.js +186 -0
  84. package/template/wall-e/eval/run-agent-benchmarks.js +66 -2
  85. package/template/wall-e/eval/session-retrieval-benchmark.js +150 -0
  86. package/template/wall-e/eval/session-transcripts.js +57 -4
  87. package/template/wall-e/eval/swebench-adapter.js +109 -3
  88. package/template/wall-e/evaluation/agent-router.js +53 -1
  89. package/template/wall-e/evaluation/coding-quorum.js +48 -1
  90. package/template/wall-e/evaluation/router.js +4 -2
  91. package/template/wall-e/evaluation/tier-selector.js +11 -1
  92. package/template/wall-e/extraction/contradiction.js +2 -2
  93. package/template/wall-e/extraction/indexer.js +2 -1
  94. package/template/wall-e/extraction/knowledge-extractor.js +2 -2
  95. package/template/wall-e/hooks/cli.js +92 -0
  96. package/template/wall-e/hooks/discovery.js +119 -0
  97. package/template/wall-e/hooks/index.js +7 -0
  98. package/template/wall-e/hooks/manifest.js +55 -0
  99. package/template/wall-e/hooks/runtime.js +84 -0
  100. package/template/wall-e/hooks/session-memory.js +225 -0
  101. package/template/wall-e/http/auth.js +6 -2
  102. package/template/wall-e/http/chat-api.js +54 -8
  103. package/template/wall-e/integrations/claude-plugin/hooks/hooks.json +27 -0
  104. package/template/wall-e/integrations/claude-plugin/hooks/walle-precompact-hook.sh +5 -0
  105. package/template/wall-e/integrations/claude-plugin/hooks/walle-stop-hook.sh +5 -0
  106. package/template/wall-e/integrations/codex-plugin/hooks/walle-hook.sh +7 -0
  107. package/template/wall-e/integrations/codex-plugin/hooks.json +37 -0
  108. package/template/wall-e/listening/calendar.js +3 -1
  109. package/template/wall-e/llm/client.js +64 -10
  110. package/template/wall-e/llm/google.js +39 -5
  111. package/template/wall-e/llm/ollama.js +1 -1
  112. package/template/wall-e/llm/ollama.plugin.json +1 -1
  113. package/template/wall-e/llm/provider-availability.js +10 -0
  114. package/template/wall-e/llm/provider-error.js +269 -0
  115. package/template/wall-e/llm/tool-adapter.js +48 -12
  116. package/template/wall-e/loops/boot.js +2 -1
  117. package/template/wall-e/loops/initiative.js +2 -2
  118. package/template/wall-e/loops/tasks.js +8 -47
  119. package/template/wall-e/loops/workspace-prompts.js +20 -0
  120. package/template/wall-e/mcp-server.js +442 -1
  121. package/template/wall-e/memory/session-ingest-service.js +159 -0
  122. package/template/wall-e/memory/source-indexer.js +289 -0
  123. package/template/wall-e/plugins/discovery.js +83 -0
  124. package/template/wall-e/plugins/manifest-loader.js +50 -10
  125. package/template/wall-e/plugins/manifest-schema.js +69 -0
  126. package/template/wall-e/plugins/model-catalog.js +55 -0
  127. package/template/wall-e/prompts/coding/base.txt +2 -0
  128. package/template/wall-e/prompts/coding/deepseek.txt +1 -0
  129. package/template/wall-e/prompts/coding/memory-protocol.md +9 -0
  130. package/template/wall-e/prompts/coding/plan.txt +1 -0
  131. package/template/wall-e/runtime/execution-trace.js +220 -0
  132. package/template/wall-e/security/audit.js +266 -0
  133. package/template/wall-e/security/ssrf.js +236 -0
  134. package/template/wall-e/session-files.js +303 -0
  135. package/template/wall-e/skills/_bundled/slack-backfill/SKILL.md +3 -0
  136. package/template/wall-e/skills/_bundled/slack-sync/SKILL.md +3 -0
  137. package/template/wall-e/skills/internal-skill-registry.js +2 -2
  138. package/template/wall-e/skills/script-skill-runner.js +143 -0
  139. package/template/wall-e/skills/skill-executor.js +5 -6
  140. package/template/wall-e/skills/skill-fallback.js +3 -1
  141. package/template/wall-e/skills/skill-harness-registry.js +7 -8
  142. package/template/wall-e/skills/skill-planner.js +52 -4
  143. package/template/wall-e/skills/slack-ingest.js +11 -3
  144. package/template/wall-e/sources/base.js +90 -0
  145. package/template/wall-e/sources/builtin.js +33 -0
  146. package/template/wall-e/sources/claude-code-jsonl.js +78 -0
  147. package/template/wall-e/sources/codex-jsonl.js +125 -0
  148. package/template/wall-e/sources/coding-session-utils.js +117 -0
  149. package/template/wall-e/sources/contract-suite.js +59 -0
  150. package/template/wall-e/sources/gemini-jsonl.js +85 -0
  151. package/template/wall-e/sources/index.js +9 -0
  152. package/template/wall-e/sources/jsonl-utils.js +181 -0
  153. package/template/wall-e/sources/record-types.js +252 -0
  154. package/template/wall-e/sources/registry.js +92 -0
  155. package/template/wall-e/sources/transforms.js +100 -0
  156. package/template/wall-e/sources/walle-jsonl.js +108 -0
  157. package/template/wall-e/tools/coding-middleware.js +31 -1
  158. package/template/wall-e/tools/file-tracker.js +25 -1
  159. package/template/wall-e/tools/local-tools.js +75 -47
  160. package/template/wall-e/tools/session-sharing.js +68 -1
  161. package/template/wall-e/tools/shell-analyzer.js +1 -1
  162. package/template/wall-e/tools/shell-policy.js +47 -0
  163. package/template/wall-e/tools/snapshot.js +42 -0
  164. package/template/wall-e/training/harvester.js +62 -5
  165. package/template/wall-e/utils/repair.js +253 -1
  166. package/template/website/index.html +3 -3
  167. package/template/wall-e/skills/_bundled/slack-mentions/.watched-threads.json +0 -18
@@ -5,7 +5,13 @@
5
5
  * Scans for orphaned records, verifies constraints, can rebuild indexes.
6
6
  */
7
7
 
8
+ const fs = require('node:fs');
8
9
  const brain = require('../brain');
10
+ const { collectIngestRecords } = require('../sources/base');
11
+ const { ensureBuiltinSourceAdapters } = require('../sources/builtin');
12
+ const sourceRegistry = require('../sources/registry');
13
+ const { RECORD_TYPES } = require('../sources/record-types');
14
+ const { rebuildSourceIndex } = require('../memory/source-indexer');
9
15
 
10
16
  /**
11
17
  * Scan database for integrity issues.
@@ -67,11 +73,28 @@ function scanIntegrity() {
67
73
  issues.push({ type: 'sqlite_integrity', description: `Integrity check error: ${err.message}`, ids: [] });
68
74
  }
69
75
 
76
+ // 4. Source/session memory consistency
77
+ try {
78
+ const sourceScan = scanSourceIntegrity();
79
+ issues.push(...sourceScan.issues);
80
+ } catch {}
81
+
82
+ // 5. Memory index and embedding consistency
83
+ try {
84
+ issues.push(...scanMemoryIndexIntegrity().issues);
85
+ } catch {}
86
+ try {
87
+ issues.push(...scanEmbeddingIntegrity().issues);
88
+ } catch {}
89
+
70
90
  const stats = {
71
91
  total_issues: issues.length,
72
92
  sqlite_ok: integrityOk,
73
93
  orphaned_knowledge: issues.filter(i => i.type === 'orphaned_knowledge').length,
74
94
  orphaned_entity_refs: issues.filter(i => i.type === 'orphaned_entity_ref').length,
95
+ source_issues: issues.filter(i => i.type.startsWith('source_')).length,
96
+ memory_index_issues: issues.filter(i => i.type.startsWith('memory_index_')).length,
97
+ embedding_issues: issues.filter(i => i.type.startsWith('embedding_')).length,
75
98
  };
76
99
 
77
100
  return { issues, stats };
@@ -142,4 +165,233 @@ function rebuildMemoryIndex() {
142
165
  return { rebuilt };
143
166
  }
144
167
 
145
- module.exports = { scanIntegrity, pruneOrphanedRefs, rebuildMemoryIndex };
168
+ function scanSourceIntegrity({ sourceId = '', limit = 1000 } = {}) {
169
+ const db = brain.getDb();
170
+ const issues = [];
171
+ const params = [];
172
+ const conditions = [
173
+ 'archived_at IS NULL',
174
+ "(source LIKE '%-jsonl' OR memory_type LIKE 'coding_session_%' OR source = 'walle-diary')",
175
+ ];
176
+ if (sourceId) {
177
+ conditions.push('(source_id = ? OR source_id LIKE ? OR metadata LIKE ?)');
178
+ params.push(sourceId, `${sourceId}:%`, `%"sourceId":"${sourceId}"%`);
179
+ }
180
+ params.push(Math.min(Math.max(Number(limit) || 1000, 1), 10000));
181
+
182
+ const rows = db.prepare(`
183
+ SELECT id, source, source_id, memory_type, metadata
184
+ FROM memories
185
+ WHERE ${conditions.join(' AND ')}
186
+ ORDER BY timestamp DESC
187
+ LIMIT ?
188
+ `).all(...params);
189
+
190
+ for (const row of rows) {
191
+ const metadata = parseMetadata(row.metadata);
192
+ const stableSourceId = metadata.sourceId || inferSourceId(row.source_id);
193
+ if (!stableSourceId) {
194
+ issues.push({
195
+ type: 'source_missing_source_id',
196
+ description: `Memory ${row.id} has no stable source id`,
197
+ ids: [row.id],
198
+ });
199
+ }
200
+ if (row.source !== 'walle-diary' && !metadata.sourceFile) {
201
+ issues.push({
202
+ type: 'source_missing_raw_ref',
203
+ description: `Memory ${row.id} has no sourceFile metadata for replay`,
204
+ ids: [row.id, stableSourceId || row.source_id],
205
+ });
206
+ }
207
+ if (metadata.sourceFile && !fs.existsSync(metadata.sourceFile)) {
208
+ issues.push({
209
+ type: 'source_missing_file',
210
+ description: `Memory ${row.id} references missing source file ${metadata.sourceFile}`,
211
+ ids: [row.id, stableSourceId || row.source_id],
212
+ });
213
+ }
214
+ }
215
+
216
+ return {
217
+ issues,
218
+ stats: {
219
+ scanned: rows.length,
220
+ total_issues: issues.length,
221
+ missing_source_id: issues.filter(i => i.type === 'source_missing_source_id').length,
222
+ missing_raw_ref: issues.filter(i => i.type === 'source_missing_raw_ref').length,
223
+ missing_file: issues.filter(i => i.type === 'source_missing_file').length,
224
+ },
225
+ };
226
+ }
227
+
228
+ function scanMemoryIndexIntegrity() {
229
+ const db = brain.getDb();
230
+ const issues = [];
231
+ const hasIndex = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_index'").get();
232
+ if (!hasIndex) return { issues, stats: { scanned: 0, total_issues: 0 } };
233
+
234
+ const orphaned = db.prepare(`
235
+ SELECT mi.memory_id
236
+ FROM memory_index mi
237
+ LEFT JOIN memories m ON m.id = mi.memory_id
238
+ WHERE m.id IS NULL
239
+ LIMIT 1000
240
+ `).all();
241
+ for (const row of orphaned) {
242
+ issues.push({
243
+ type: 'memory_index_orphan',
244
+ description: `memory_index references missing memory ${row.memory_id}`,
245
+ ids: [row.memory_id],
246
+ });
247
+ }
248
+
249
+ const missing = db.prepare(`
250
+ SELECT m.id
251
+ FROM memories m
252
+ LEFT JOIN memory_index mi ON mi.memory_id = m.id
253
+ WHERE m.archived_at IS NULL
254
+ AND length(m.content) > 20
255
+ AND mi.memory_id IS NULL
256
+ AND (m.source LIKE '%-jsonl' OR m.memory_type LIKE 'coding_session_%' OR m.source = 'walle-diary')
257
+ LIMIT 1000
258
+ `).all();
259
+ for (const row of missing) {
260
+ issues.push({
261
+ type: 'memory_index_missing',
262
+ description: `Coding/session memory ${row.id} has no source index row`,
263
+ ids: [row.id],
264
+ });
265
+ }
266
+
267
+ return {
268
+ issues,
269
+ stats: {
270
+ orphaned: orphaned.length,
271
+ missing: missing.length,
272
+ total_issues: issues.length,
273
+ },
274
+ };
275
+ }
276
+
277
+ function scanEmbeddingIntegrity() {
278
+ const db = brain.getDb();
279
+ const issues = [];
280
+ const hasMap = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='embedding_map'").get();
281
+ if (!hasMap) return { issues, stats: { checked: false, total_issues: 0 } };
282
+
283
+ const orphanedMemories = db.prepare(`
284
+ SELECT em.entity_id
285
+ FROM embedding_map em
286
+ LEFT JOIN memories m ON m.id = em.entity_id
287
+ WHERE em.entity_type = 'memory' AND m.id IS NULL
288
+ LIMIT 1000
289
+ `).all();
290
+ for (const row of orphanedMemories) {
291
+ issues.push({ type: 'embedding_orphan_memory', description: `Embedding references missing memory ${row.entity_id}`, ids: [row.entity_id] });
292
+ }
293
+
294
+ const orphanedKnowledge = db.prepare(`
295
+ SELECT em.entity_id
296
+ FROM embedding_map em
297
+ LEFT JOIN knowledge k ON k.id = em.entity_id
298
+ WHERE em.entity_type = 'knowledge' AND k.id IS NULL
299
+ LIMIT 1000
300
+ `).all();
301
+ for (const row of orphanedKnowledge) {
302
+ issues.push({ type: 'embedding_orphan_knowledge', description: `Embedding references missing knowledge ${row.entity_id}`, ids: [row.entity_id] });
303
+ }
304
+
305
+ const models = db.prepare('SELECT model, COUNT(*) as c FROM embedding_map GROUP BY model').all();
306
+ for (const model of models) {
307
+ const vecTable = embeddingVecTableName(model.model);
308
+ const table = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(vecTable);
309
+ if (!table) {
310
+ issues.push({ type: 'embedding_missing_vec_table', description: `Embedding map has model ${model.model} but no ${vecTable}`, ids: [model.model] });
311
+ continue;
312
+ }
313
+ try {
314
+ const vecCount = db.prepare(`SELECT COUNT(*) as c FROM "${vecTable}"`).get().c;
315
+ if (vecCount !== model.c) {
316
+ issues.push({
317
+ type: 'embedding_vec_count_mismatch',
318
+ description: `Embedding map model ${model.model} has ${model.c} rows but ${vecTable} has ${vecCount}`,
319
+ ids: [model.model],
320
+ });
321
+ }
322
+ } catch {}
323
+ }
324
+
325
+ return {
326
+ issues,
327
+ stats: {
328
+ models: models.length,
329
+ orphaned_memory_embeddings: orphanedMemories.length,
330
+ orphaned_knowledge_embeddings: orphanedKnowledge.length,
331
+ total_issues: issues.length,
332
+ },
333
+ };
334
+ }
335
+
336
+ function embeddingVecTableName(model) {
337
+ let key = String(model || '').replace(/[^A-Za-z0-9_]/g, '_');
338
+ try {
339
+ const { EMBEDDING_MODELS } = require('../embeddings');
340
+ key = EMBEDDING_MODELS?.[model]?.key || key;
341
+ } catch {}
342
+ return `embedding_vec_${key}`;
343
+ }
344
+
345
+ async function replaySource({ adapterId, uri, sourceFile, sourceId, cwd = '', privacyClass, privacyFloor = null, dryRun = true, metadata = {} } = {}) {
346
+ ensureBuiltinSourceAdapters();
347
+ const adapter = sourceRegistry.instantiate(adapterId);
348
+ if (!adapter) throw new Error(`Unknown source adapter: ${adapterId}`);
349
+ const sourceRef = {
350
+ adapterId,
351
+ uri: uri || sourceFile,
352
+ sourceFile: sourceFile || uri,
353
+ sourceId,
354
+ cwd,
355
+ privacyClass,
356
+ metadata,
357
+ };
358
+ if (dryRun) {
359
+ const { records, diagnostics, source } = await collectIngestRecords(adapter, sourceRef);
360
+ return {
361
+ dryRun: true,
362
+ adapterId,
363
+ sourceId: source.sourceId,
364
+ records: records.length,
365
+ memoryRecords: records.filter((record) => record.type === RECORD_TYPES.MEMORY_RECORD).length,
366
+ diagnostics,
367
+ };
368
+ }
369
+ const { createSessionIngestService } = require('../memory/session-ingest-service');
370
+ return createSessionIngestService({ brain }).ingestSource(sourceRef, { privacyFloor, dryRun: false });
371
+ }
372
+
373
+ function parseMetadata(value) {
374
+ try {
375
+ const parsed = JSON.parse(value || '{}');
376
+ return parsed && typeof parsed === 'object' ? parsed : {};
377
+ } catch {
378
+ return {};
379
+ }
380
+ }
381
+
382
+ function inferSourceId(sourceId = '') {
383
+ const parts = String(sourceId || '').split(':');
384
+ if (parts.length <= 2) return sourceId || '';
385
+ return parts.slice(0, 2).join(':');
386
+ }
387
+
388
+ module.exports = {
389
+ pruneOrphanedRefs,
390
+ rebuildMemoryIndex,
391
+ rebuildSourceIndex,
392
+ replaySource,
393
+ scanEmbeddingIntegrity,
394
+ scanIntegrity,
395
+ scanMemoryIndexIntegrity,
396
+ scanSourceIntegrity,
397
+ };
@@ -240,7 +240,7 @@
240
240
  <div class="hero">
241
241
  <h1>Your AI Coding <span class="accent">Dashboard</span> +<br>Personal <span class="accent">Agent</span></h1>
242
242
  <p class="sub">
243
- Run Claude Code, Codex, Gemini, and Aider sessions side by side. Manage prompts,
243
+ Run Claude Code, Codex, Gemini, Aider, OpenCode, and Cursor Agent sessions side by side. Manage prompts,
244
244
  queue tasks, and let an AI agent build a second brain from your work life.
245
245
  </p>
246
246
  <div class="install-box" onclick="navigator.clipboard.writeText('npx create-walle install ./walle');this.querySelector('.copy-hint').textContent='Copied!'">
@@ -285,7 +285,7 @@
285
285
  <div class="feature-card">
286
286
  <span class="icon">&#9641;&#9641;</span>
287
287
  <h3>Terminal Multiplexer</h3>
288
- <p>Run Claude Code, Codex, Gemini CLI, and Aider sessions side by side with live status, persistent scrollback, model switching, and AI-generated titles.</p>
288
+ <p>Run Claude Code, Codex, Gemini CLI, Aider, OpenCode, Cursor Agent, and more side by side with live status, persistent scrollback, model switching, and AI-generated titles.</p>
289
289
  </div>
290
290
  <div class="feature-card">
291
291
  <span class="icon">&#9998;</span>
@@ -347,7 +347,7 @@
347
347
  <div class="feature-card">
348
348
  <span class="icon">&#127760;</span>
349
349
  <h3>Multi-Model</h3>
350
- <p>Works with Claude, GPT, Gemini, and local models via Ollama. Smart routing picks the best model for each task.</p>
350
+ <p>Works with Claude, GPT, Gemini, DeepSeek, and local models via Ollama, LM Studio, or MLX. Smart routing picks the best model for each task.</p>
351
351
  </div>
352
352
  <div class="feature-card">
353
353
  <span class="icon">&#128241;</span>
@@ -1,18 +0,0 @@
1
- {
2
- "D02U25VN72R:1775337680.059359": {
3
- "channel_id": "D02U25VN72R",
4
- "thread_ts": "1775337680.059359",
5
- "task_id": "6bc67ff2-2f75-48b5-8931-effe8152833a",
6
- "session_id": "task-6bc67ff2-2f75-48b5-8931-effe8152833a",
7
- "last_activity": "2026-04-04T21:23:16.469Z",
8
- "last_seen_ts": "1775337770.532239"
9
- },
10
- "D02U25VN72R:1775337821.126619": {
11
- "channel_id": "D02U25VN72R",
12
- "thread_ts": "1775337821.126619",
13
- "task_id": "83555195-3f8d-4748-b628-2d6077bfa5a4",
14
- "session_id": "task-83555195-3f8d-4748-b628-2d6077bfa5a4",
15
- "last_activity": "2026-04-04T21:26:30.938Z",
16
- "last_seen_ts": "1775337970.800449"
17
- }
18
- }