create-walle 0.9.13 → 0.9.15
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/README.md +8 -3
- package/bin/create-walle.js +232 -32
- package/bin/mcp-inject.js +18 -53
- package/package.json +3 -1
- package/template/claude-task-manager/api-prompts.js +11 -2
- package/template/claude-task-manager/approval-agent.js +7 -0
- package/template/claude-task-manager/db.js +94 -75
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +242 -0
- package/template/claude-task-manager/docs/session-tooltip-freshness-design.md +224 -0
- package/template/claude-task-manager/docs/session-ux-issue-review-2026-05-01.md +369 -0
- package/template/claude-task-manager/fuzzy-utils.js +10 -2
- package/template/claude-task-manager/git-utils.js +140 -10
- package/template/claude-task-manager/lib/agent-capabilities.js +1 -1
- package/template/claude-task-manager/lib/agent-presets.js +38 -5
- package/template/claude-task-manager/lib/codex-terminal-final.js +53 -0
- package/template/claude-task-manager/lib/ctm-session-context-api.js +222 -0
- package/template/claude-task-manager/lib/session-diagnostics.js +56 -0
- package/template/claude-task-manager/lib/session-history.js +309 -16
- package/template/claude-task-manager/lib/session-standup.js +409 -0
- package/template/claude-task-manager/lib/session-stream.js +253 -20
- package/template/claude-task-manager/lib/standup-attention.js +200 -0
- package/template/claude-task-manager/lib/status-hooks.js +8 -2
- package/template/claude-task-manager/lib/update-telemetry.js +114 -0
- package/template/claude-task-manager/lib/walle-ctm-history.js +49 -6
- package/template/claude-task-manager/lib/walle-default-model.js +55 -0
- package/template/claude-task-manager/lib/walle-mcp-auto-config.js +66 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +86 -19
- package/template/claude-task-manager/lib/walle-transcript.js +1 -3
- package/template/claude-task-manager/lib/worktree-cwd.js +82 -0
- package/template/claude-task-manager/package.json +1 -0
- package/template/claude-task-manager/providers/codex-mcp.js +104 -0
- package/template/claude-task-manager/providers/index.js +2 -0
- package/template/claude-task-manager/public/css/setup.css +2 -1
- package/template/claude-task-manager/public/css/walle.css +71 -0
- package/template/claude-task-manager/public/index.html +2388 -429
- package/template/claude-task-manager/public/js/message-renderer.js +314 -35
- package/template/claude-task-manager/public/js/session-search-utils.js +185 -3
- package/template/claude-task-manager/public/js/session-status-precedence.js +125 -0
- package/template/claude-task-manager/public/js/setup.js +62 -19
- package/template/claude-task-manager/public/js/stream-view.js +396 -55
- package/template/claude-task-manager/public/js/terminal-restore-state.js +57 -0
- package/template/claude-task-manager/public/js/walle-session.js +234 -26
- package/template/claude-task-manager/public/js/walle.js +143 -2
- package/template/claude-task-manager/server.js +1402 -433
- package/template/claude-task-manager/session-integrity.js +77 -28
- package/template/claude-task-manager/workers/approval-widget-validator.js +15 -5
- package/template/claude-task-manager/workers/scrollback-worker.js +5 -6
- package/template/claude-task-manager/workers/state-detectors/codex.js +6 -0
- package/template/package.json +1 -1
- package/template/wall-e/agent-runners/claude-code.js +2 -0
- package/template/wall-e/agent.js +63 -8
- package/template/wall-e/api-walle.js +330 -52
- package/template/wall-e/brain.js +291 -42
- package/template/wall-e/chat.js +172 -15
- package/template/wall-e/coding/compaction-service.js +19 -5
- package/template/wall-e/coding/stream-processor.js +22 -2
- package/template/wall-e/coding/workspace-replay.js +1 -4
- package/template/wall-e/coding-orchestrator.js +250 -80
- package/template/wall-e/compat.js +0 -28
- package/template/wall-e/context/context-builder.js +3 -1
- package/template/wall-e/embeddings.js +2 -7
- package/template/wall-e/eval/agent-runner.js +30 -9
- package/template/wall-e/eval/benchmark-generator.js +21 -1
- package/template/wall-e/eval/benchmarks/chat-eval.json +66 -6
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -596
- package/template/wall-e/eval/cc-replay.js +1 -0
- package/template/wall-e/eval/codex-cli-baseline.js +633 -0
- package/template/wall-e/eval/debug-agent003.js +1 -0
- package/template/wall-e/eval/eval-orchestrator.js +3 -3
- package/template/wall-e/eval/run-agent-benchmarks.js +11 -3
- package/template/wall-e/eval/run-codex-cli-baseline.js +177 -0
- package/template/wall-e/eval/run-model-comparison.js +1 -0
- package/template/wall-e/eval/swebench-adapter.js +1 -0
- package/template/wall-e/evaluation/quorum-evaluator.js +0 -1
- package/template/wall-e/extraction/knowledge-extractor.js +1 -2
- package/template/wall-e/lib/mcp-integration.js +336 -0
- package/template/wall-e/llm/ollama.js +47 -8
- package/template/wall-e/llm/ollama.plugin.json +1 -1
- package/template/wall-e/llm/tool-adapter.js +1 -0
- package/template/wall-e/loops/ingest.js +42 -8
- package/template/wall-e/loops/initiative.js +87 -2
- package/template/wall-e/mcp-server.js +872 -19
- package/template/wall-e/memory/ctm-context-client.js +230 -0
- package/template/wall-e/memory/ctm-session-context.js +1376 -0
- package/template/wall-e/prompts/coding/memory-protocol.md +6 -0
- package/template/wall-e/server.js +30 -1
- package/template/wall-e/skills/_bundled/memory-search/SKILL.md +8 -0
- package/template/wall-e/skills/_bundled/scan-ctm-sessions/SKILL.md +20 -0
- package/template/wall-e/skills/_bundled/scan-ctm-sessions/run.js +43 -0
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +471 -188
- package/template/wall-e/skills/skill-planner.js +86 -4
- package/template/wall-e/slack/socket-mode-listener.js +276 -0
- package/template/wall-e/telemetry.js +70 -2
- package/template/wall-e/tools/builtin-middleware.js +55 -2
- package/template/wall-e/tools/shell-policy.js +1 -1
- package/template/wall-e/tools/slack-owner.js +104 -0
- package/template/website/index.html +4 -4
- package/template/builder-journal.md +0 -17
package/template/wall-e/brain.js
CHANGED
|
@@ -431,6 +431,7 @@ function _detectUpgrade(previousSchemaVersion) {
|
|
|
431
431
|
migrations_applied: Math.max(0, SCHEMA_VERSION - previousSchemaVersion),
|
|
432
432
|
first_tracked: true,
|
|
433
433
|
});
|
|
434
|
+
telemetry.trackFunnelStep?.('upgrade_completed');
|
|
434
435
|
setKv('last_upgrade_at', new Date().toISOString());
|
|
435
436
|
} else {
|
|
436
437
|
setKv('first_installed_at', new Date().toISOString());
|
|
@@ -444,6 +445,7 @@ function _detectUpgrade(previousSchemaVersion) {
|
|
|
444
445
|
schema_to: SCHEMA_VERSION,
|
|
445
446
|
migrations_applied: SCHEMA_VERSION - previousSchemaVersion,
|
|
446
447
|
});
|
|
448
|
+
telemetry.trackFunnelStep?.('upgrade_completed');
|
|
447
449
|
setKv('installed_version', pkgVersion);
|
|
448
450
|
setKv('last_upgrade_at', new Date().toISOString());
|
|
449
451
|
}
|
|
@@ -495,6 +497,27 @@ function getDbPath() {
|
|
|
495
497
|
return currentDbPath || DEFAULT_DB_PATH;
|
|
496
498
|
}
|
|
497
499
|
|
|
500
|
+
const _VALID_CHECKPOINT_MODES = new Set(['PASSIVE', 'FULL', 'RESTART', 'TRUNCATE']);
|
|
501
|
+
function checkpointWalOrThrow(mode) {
|
|
502
|
+
const d = getDb();
|
|
503
|
+
const m = (mode || 'PASSIVE').toUpperCase();
|
|
504
|
+
if (!_VALID_CHECKPOINT_MODES.has(m)) throw new Error(`Invalid WAL checkpoint mode: ${mode}`);
|
|
505
|
+
const rows = d.pragma(`wal_checkpoint(${m})`);
|
|
506
|
+
const row = Array.isArray(rows) ? rows[0] : rows;
|
|
507
|
+
const busy = Number(row?.busy ?? row?.[0] ?? 0);
|
|
508
|
+
if (busy > 0) {
|
|
509
|
+
const log = row?.log ?? row?.[1] ?? 'unknown';
|
|
510
|
+
const checkpointed = row?.checkpointed ?? row?.[2] ?? 'unknown';
|
|
511
|
+
throw new Error(`WAL checkpoint ${m} could not complete: busy=${busy}, log=${log}, checkpointed=${checkpointed}`);
|
|
512
|
+
}
|
|
513
|
+
return rows;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function runImmediateTransaction(d, fn, ...args) {
|
|
517
|
+
const txn = d.transaction(fn);
|
|
518
|
+
return typeof txn.immediate === 'function' ? txn.immediate(...args) : txn(...args);
|
|
519
|
+
}
|
|
520
|
+
|
|
498
521
|
function initDb(dbPath) {
|
|
499
522
|
dbPath = dbPath || DEFAULT_DB_PATH;
|
|
500
523
|
currentDbPath = dbPath;
|
|
@@ -936,6 +959,21 @@ function createTables() {
|
|
|
936
959
|
);
|
|
937
960
|
CREATE INDEX IF NOT EXISTS idx_slack_threads_active ON slack_threads(last_activity);
|
|
938
961
|
|
|
962
|
+
CREATE TABLE IF NOT EXISTS slack_inbound_events (
|
|
963
|
+
id TEXT PRIMARY KEY,
|
|
964
|
+
channel_id TEXT NOT NULL,
|
|
965
|
+
message_ts TEXT NOT NULL,
|
|
966
|
+
thread_ts TEXT,
|
|
967
|
+
source TEXT,
|
|
968
|
+
status TEXT DEFAULT 'claimed',
|
|
969
|
+
payload_hash TEXT,
|
|
970
|
+
error TEXT,
|
|
971
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
972
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
973
|
+
);
|
|
974
|
+
CREATE INDEX IF NOT EXISTS idx_slack_inbound_events_updated ON slack_inbound_events(updated_at);
|
|
975
|
+
CREATE INDEX IF NOT EXISTS idx_slack_inbound_events_thread ON slack_inbound_events(channel_id, thread_ts);
|
|
976
|
+
|
|
939
977
|
-- Model management tables
|
|
940
978
|
CREATE TABLE IF NOT EXISTS model_providers (
|
|
941
979
|
id TEXT PRIMARY KEY,
|
|
@@ -1397,11 +1435,27 @@ function listMemories({ source, since, extractionStatus, limit } = {}) {
|
|
|
1397
1435
|
return getDb().prepare(sql).all(...params);
|
|
1398
1436
|
}
|
|
1399
1437
|
|
|
1400
|
-
function searchMemories({ query, limit } = {}) {
|
|
1438
|
+
function searchMemories({ query, limit, source, memory_type, since, until } = {}) {
|
|
1401
1439
|
if (!query || !query.trim()) return [];
|
|
1402
1440
|
const terms = query.trim().split(/\s+/).filter(Boolean);
|
|
1403
1441
|
const conditions = terms.map(() => "content LIKE ? ESCAPE '\\'");
|
|
1404
1442
|
const params = terms.map(t => '%' + t.replace(/[%_\\]/g, '\\$&') + '%');
|
|
1443
|
+
if (source) {
|
|
1444
|
+
conditions.push('source = ?');
|
|
1445
|
+
params.push(source);
|
|
1446
|
+
}
|
|
1447
|
+
if (memory_type) {
|
|
1448
|
+
conditions.push('memory_type = ?');
|
|
1449
|
+
params.push(memory_type);
|
|
1450
|
+
}
|
|
1451
|
+
if (since) {
|
|
1452
|
+
conditions.push('timestamp >= ?');
|
|
1453
|
+
params.push(since);
|
|
1454
|
+
}
|
|
1455
|
+
if (until) {
|
|
1456
|
+
conditions.push('timestamp <= ?');
|
|
1457
|
+
params.push(until);
|
|
1458
|
+
}
|
|
1405
1459
|
const lim = Math.min(Math.max(limit || 50, 1), 200);
|
|
1406
1460
|
params.push(lim);
|
|
1407
1461
|
return getDb().prepare(
|
|
@@ -1633,7 +1687,7 @@ let backupIntervalId = null;
|
|
|
1633
1687
|
|
|
1634
1688
|
function createBackup(label) {
|
|
1635
1689
|
getDb(); // throws if not initialized
|
|
1636
|
-
|
|
1690
|
+
checkpointWalOrThrow('TRUNCATE');
|
|
1637
1691
|
|
|
1638
1692
|
// Use backup dir relative to the current DB, not the module-level constant.
|
|
1639
1693
|
// This prevents test runs from writing backups to the production backup dir.
|
|
@@ -1842,18 +1896,20 @@ function _editDistance(a, b) {
|
|
|
1842
1896
|
|
|
1843
1897
|
function mergeEntities(keepId, removeId) {
|
|
1844
1898
|
const db = getDb();
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1899
|
+
return runImmediateTransaction(db, (targetKeepId, targetRemoveId) => {
|
|
1900
|
+
const keep = db.prepare('SELECT * FROM entities WHERE id = ?').get(targetKeepId);
|
|
1901
|
+
const remove = db.prepare('SELECT * FROM entities WHERE id = ?').get(targetRemoveId);
|
|
1902
|
+
if (!keep || !remove) throw new Error('Both entities must exist');
|
|
1903
|
+
let keepAliases = [], removeAliases = [];
|
|
1904
|
+
try { keepAliases = JSON.parse(keep.aliases || '[]'); } catch {}
|
|
1905
|
+
try { removeAliases = JSON.parse(remove.aliases || '[]'); } catch {}
|
|
1906
|
+
const mergedAliases = [...new Set([...keepAliases, ...removeAliases, remove.canonical_name])];
|
|
1907
|
+
db.prepare("UPDATE entities SET aliases = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(mergedAliases), targetKeepId);
|
|
1908
|
+
db.prepare('UPDATE knowledge SET subject_entity_id = ? WHERE subject_entity_id = ?').run(targetKeepId, targetRemoveId);
|
|
1909
|
+
db.prepare('UPDATE knowledge SET object_entity_id = ? WHERE object_entity_id = ?').run(targetKeepId, targetRemoveId);
|
|
1910
|
+
db.prepare('DELETE FROM entities WHERE id = ?').run(targetRemoveId);
|
|
1911
|
+
return { merged: true, kept: targetKeepId, removed: targetRemoveId, aliases: mergedAliases };
|
|
1912
|
+
}, keepId, removeId);
|
|
1857
1913
|
}
|
|
1858
1914
|
|
|
1859
1915
|
function listEntities({ entity_type, limit } = {}) {
|
|
@@ -2247,14 +2303,16 @@ function searchChatMessages({ query, limit } = {}) {
|
|
|
2247
2303
|
|
|
2248
2304
|
function deleteChatMessages({ session_id, user_content, assistant_content }) {
|
|
2249
2305
|
const db = getDb();
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
.
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
.
|
|
2257
|
-
|
|
2306
|
+
return runImmediateTransaction(db, (targetSessionId, userContent, assistantContent) => {
|
|
2307
|
+
if (userContent) {
|
|
2308
|
+
db.prepare('DELETE FROM chat_messages WHERE session_id = ? AND role = ? AND content = ?')
|
|
2309
|
+
.run(targetSessionId || 'default', 'user', userContent);
|
|
2310
|
+
}
|
|
2311
|
+
if (assistantContent) {
|
|
2312
|
+
db.prepare('DELETE FROM chat_messages WHERE session_id = ? AND role = ? AND content = ?')
|
|
2313
|
+
.run(targetSessionId || 'default', 'assistant', assistantContent);
|
|
2314
|
+
}
|
|
2315
|
+
}, session_id, user_content, assistant_content);
|
|
2258
2316
|
}
|
|
2259
2317
|
|
|
2260
2318
|
function clearChatSession(session_id) {
|
|
@@ -2379,11 +2437,14 @@ function updateSkillStats(skillId, success) {
|
|
|
2379
2437
|
}
|
|
2380
2438
|
|
|
2381
2439
|
function deleteSkill(id) {
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2440
|
+
const db = getDb();
|
|
2441
|
+
return runImmediateTransaction(db, (skillId) => {
|
|
2442
|
+
const skill = db.prepare('SELECT * FROM skills WHERE id = ?').get(skillId);
|
|
2443
|
+
if (!skill) throw new Error(`Skill not found: ${skillId}`);
|
|
2444
|
+
db.prepare('DELETE FROM skill_executions WHERE skill_id = ?').run(skillId);
|
|
2445
|
+
db.prepare('DELETE FROM skills WHERE id = ?').run(skillId);
|
|
2446
|
+
return skill;
|
|
2447
|
+
}, id);
|
|
2387
2448
|
}
|
|
2388
2449
|
|
|
2389
2450
|
// ── Skill dispatch decision log (kv-backed) ──
|
|
@@ -2496,21 +2557,25 @@ function deleteAuthProfile(id) {
|
|
|
2496
2557
|
// ── Tasks ──
|
|
2497
2558
|
|
|
2498
2559
|
function insertTask({ title, description, priority, type, execution, script, schedule, due_at, next_run_at, skill, skill_config, source, source_ref }) {
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2560
|
+
const db = getDb();
|
|
2561
|
+
return runImmediateTransaction(db, (task) => {
|
|
2562
|
+
const taskSource = task.source || 'system';
|
|
2563
|
+
// Dedup: skip if a task with the same title+source+source_ref already exists and isn't archived.
|
|
2564
|
+
if (task.title && taskSource) {
|
|
2565
|
+
const existing = task.source_ref
|
|
2566
|
+
? db.prepare(`SELECT id, status FROM tasks WHERE title = ? AND source = ? AND source_ref = ? AND status != 'archived' LIMIT 1`).get(task.title, taskSource, task.source_ref)
|
|
2567
|
+
: db.prepare(`SELECT id, status FROM tasks WHERE title = ? AND source = ? AND status NOT IN ('archived','completed') LIMIT 1`).get(task.title, taskSource);
|
|
2568
|
+
if (existing) return { id: existing.id, deduplicated: true };
|
|
2569
|
+
}
|
|
2570
|
+
const id = uuidv4();
|
|
2571
|
+
db.prepare(`
|
|
2572
|
+
INSERT INTO tasks (id, title, description, priority, type, execution, script, schedule, due_at, next_run_at, skill, skill_config, source, source_ref)
|
|
2573
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2574
|
+
`).run(id, task.title, task.description || null, task.priority || 'normal', task.type || 'once',
|
|
2575
|
+
task.execution || 'chat', task.script || null, task.schedule || null, task.due_at || null, task.next_run_at || task.due_at || null,
|
|
2576
|
+
task.skill || null, task.skill_config || null, taskSource, task.source_ref || null);
|
|
2577
|
+
return { id };
|
|
2578
|
+
}, { title, description, priority, type, execution, script, schedule, due_at, next_run_at, skill, skill_config, source, source_ref });
|
|
2514
2579
|
}
|
|
2515
2580
|
|
|
2516
2581
|
function getTask(id) {
|
|
@@ -2710,6 +2775,48 @@ function deleteExpiredSlackThreads(watchDurationMs) {
|
|
|
2710
2775
|
return getDb().prepare('DELETE FROM slack_threads WHERE last_activity < ?').run(cutoff).changes;
|
|
2711
2776
|
}
|
|
2712
2777
|
|
|
2778
|
+
// -- Slack Inbound Event Ledger --
|
|
2779
|
+
|
|
2780
|
+
function slackInboundEventId(channelId, messageTs) {
|
|
2781
|
+
return `${channelId}:${messageTs}`;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
function claimSlackInboundEvent({ channelId, messageTs, threadTs, source, payloadHash } = {}) {
|
|
2785
|
+
if (!channelId || !messageTs) throw new Error('Slack inbound event requires channelId and messageTs');
|
|
2786
|
+
const id = slackInboundEventId(channelId, messageTs);
|
|
2787
|
+
const now = new Date().toISOString();
|
|
2788
|
+
const result = getDb().prepare(`
|
|
2789
|
+
INSERT OR IGNORE INTO slack_inbound_events
|
|
2790
|
+
(id, channel_id, message_ts, thread_ts, source, status, payload_hash, updated_at)
|
|
2791
|
+
VALUES (?, ?, ?, ?, ?, 'claimed', ?, ?)
|
|
2792
|
+
`).run(id, channelId, messageTs, threadTs || null, source || null, payloadHash || null, now);
|
|
2793
|
+
|
|
2794
|
+
if (result.changes > 0) return { id, claimed: true };
|
|
2795
|
+
const event = getDb().prepare('SELECT * FROM slack_inbound_events WHERE id = ?').get(id);
|
|
2796
|
+
return { id, claimed: false, event };
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
function completeSlackInboundEvent(id, { status = 'completed', error = null } = {}) {
|
|
2800
|
+
if (!id) return 0;
|
|
2801
|
+
const now = new Date().toISOString();
|
|
2802
|
+
const result = getDb().prepare(`
|
|
2803
|
+
UPDATE slack_inbound_events
|
|
2804
|
+
SET status = ?, error = ?, updated_at = ?
|
|
2805
|
+
WHERE id = ?
|
|
2806
|
+
`).run(status, error || null, now, id);
|
|
2807
|
+
return result.changes;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
function releaseSlackInboundEvent(id) {
|
|
2811
|
+
if (!id) return 0;
|
|
2812
|
+
return getDb().prepare('DELETE FROM slack_inbound_events WHERE id = ?').run(id).changes;
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
function pruneSlackInboundEvents(ttlMs = 48 * 60 * 60 * 1000) {
|
|
2816
|
+
const cutoff = new Date(Date.now() - ttlMs).toISOString();
|
|
2817
|
+
return getDb().prepare('DELETE FROM slack_inbound_events WHERE updated_at < ?').run(cutoff).changes;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2713
2820
|
// -- Model Provider CRUD --
|
|
2714
2821
|
|
|
2715
2822
|
function upsertModelProvider({ id, name, type, baseUrl, apiKeyEncrypted, customHeaders, enabled }) {
|
|
@@ -2776,6 +2883,121 @@ function getProviderAuthMethod(type) {
|
|
|
2776
2883
|
return row?.auth_method || null;
|
|
2777
2884
|
}
|
|
2778
2885
|
|
|
2886
|
+
function saveSetupProvider({
|
|
2887
|
+
type,
|
|
2888
|
+
name,
|
|
2889
|
+
id,
|
|
2890
|
+
baseUrl,
|
|
2891
|
+
apiKeyEncrypted,
|
|
2892
|
+
customHeaders,
|
|
2893
|
+
enabled = true,
|
|
2894
|
+
model,
|
|
2895
|
+
setDefault = false,
|
|
2896
|
+
authMethod,
|
|
2897
|
+
preserveExistingKey = true,
|
|
2898
|
+
registerModel = true,
|
|
2899
|
+
}) {
|
|
2900
|
+
if (!type) throw new Error('Provider type required');
|
|
2901
|
+
if (authMethod && !VALID_AUTH_METHODS.has(authMethod)) {
|
|
2902
|
+
throw new Error(`Invalid auth_method: ${authMethod}. Must be one of: ${[...VALID_AUTH_METHODS].join(', ')}`);
|
|
2903
|
+
}
|
|
2904
|
+
const providerId = id || `${type}-default`;
|
|
2905
|
+
const displayName = name || type;
|
|
2906
|
+
const db = getDb();
|
|
2907
|
+
return runImmediateTransaction(db, () => {
|
|
2908
|
+
const currentDefaultProvider = getKv('walle_provider') || process.env.WALLE_PROVIDER || 'anthropic';
|
|
2909
|
+
const syncDefaultModel = !!(model && currentDefaultProvider === type && !setDefault);
|
|
2910
|
+
let storedKey = apiKeyEncrypted || null;
|
|
2911
|
+
if (!storedKey && preserveExistingKey) {
|
|
2912
|
+
const existingProvider = getModelProviderWithKey(providerId);
|
|
2913
|
+
storedKey = existingProvider?.api_key_encrypted || null;
|
|
2914
|
+
if (!storedKey) {
|
|
2915
|
+
const row = db.prepare(
|
|
2916
|
+
'SELECT api_key_encrypted FROM model_providers WHERE type = ? AND enabled = 1 AND api_key_encrypted IS NOT NULL ORDER BY updated_at DESC LIMIT 1'
|
|
2917
|
+
).get(type);
|
|
2918
|
+
storedKey = row?.api_key_encrypted || null;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
upsertModelProvider({
|
|
2923
|
+
id: providerId,
|
|
2924
|
+
name: displayName,
|
|
2925
|
+
type,
|
|
2926
|
+
baseUrl: baseUrl || null,
|
|
2927
|
+
apiKeyEncrypted: storedKey,
|
|
2928
|
+
customHeaders: customHeaders || null,
|
|
2929
|
+
enabled,
|
|
2930
|
+
});
|
|
2931
|
+
if (authMethod) setProviderAuthMethod(type, authMethod);
|
|
2932
|
+
if (model) {
|
|
2933
|
+
setKv(`walle_model_${type}`, model);
|
|
2934
|
+
if (syncDefaultModel) setKv('walle_model', model);
|
|
2935
|
+
if (registerModel) {
|
|
2936
|
+
upsertModelRegistryEntry({
|
|
2937
|
+
id: `${providerId}:${model}`,
|
|
2938
|
+
providerId,
|
|
2939
|
+
modelId: model,
|
|
2940
|
+
displayName: model,
|
|
2941
|
+
enabled: true,
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
if (setDefault) {
|
|
2946
|
+
setKv('walle_provider', type);
|
|
2947
|
+
if (model) setKv('walle_model', model);
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
return {
|
|
2951
|
+
provider_id: providerId,
|
|
2952
|
+
type,
|
|
2953
|
+
default_provider: getKv('walle_provider') || null,
|
|
2954
|
+
default_model: getKv('walle_model') || null,
|
|
2955
|
+
synced_default_model: syncDefaultModel,
|
|
2956
|
+
registry_id: model && registerModel ? `${providerId}:${model}` : null,
|
|
2957
|
+
};
|
|
2958
|
+
});
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
function setDefaultProviderSelection({ type, requestedModel, targetModel }) {
|
|
2962
|
+
if (!type) throw new Error('Provider type required');
|
|
2963
|
+
const db = getDb();
|
|
2964
|
+
return runImmediateTransaction(db, () => {
|
|
2965
|
+
setKv('walle_provider', type);
|
|
2966
|
+
if (requestedModel) setKv(`walle_model_${type}`, requestedModel);
|
|
2967
|
+
setKv('walle_model', targetModel || '');
|
|
2968
|
+
return {
|
|
2969
|
+
type,
|
|
2970
|
+
default_provider: getKv('walle_provider') || null,
|
|
2971
|
+
default_model: getKv('walle_model') || '',
|
|
2972
|
+
};
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
function disableModelProviderByType(type) {
|
|
2977
|
+
if (!type) throw new Error('Provider type required');
|
|
2978
|
+
const db = getDb();
|
|
2979
|
+
return runImmediateTransaction(db, () => {
|
|
2980
|
+
const changes = db.prepare('UPDATE model_providers SET enabled = 0, updated_at = datetime(\'now\') WHERE type = ?').run(type).changes;
|
|
2981
|
+
let fallback = null;
|
|
2982
|
+
if (getKv('walle_provider') === type) {
|
|
2983
|
+
const enabled = listEnabledProviders();
|
|
2984
|
+
if (enabled.length > 0) {
|
|
2985
|
+
fallback = enabled[0];
|
|
2986
|
+
setKv('walle_provider', fallback.type);
|
|
2987
|
+
const fallbackModel = getKv(`walle_model_${fallback.type}`);
|
|
2988
|
+
if (fallbackModel) setKv('walle_model', fallbackModel);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
return {
|
|
2992
|
+
type,
|
|
2993
|
+
disabled: changes,
|
|
2994
|
+
fallback_provider: fallback?.type || null,
|
|
2995
|
+
default_provider: getKv('walle_provider') || null,
|
|
2996
|
+
default_model: getKv('walle_model') || null,
|
|
2997
|
+
};
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
|
|
2779
3001
|
// -- Model Registry CRUD --
|
|
2780
3002
|
|
|
2781
3003
|
function upsertModelRegistryEntry({ id, providerId, modelId, displayName, capabilities, costPer1mInput, costPer1mOutput, maxContextTokens, maxOutputTokens, speedTier, enabled, isFineTuned }) {
|
|
@@ -2955,6 +3177,24 @@ function setModelDefault(taskType, modelRegistryId, strategy, quorumSize) {
|
|
|
2955
3177
|
`).run(taskType, modelRegistryId || null, strategy || 'single', quorumSize || 1);
|
|
2956
3178
|
}
|
|
2957
3179
|
|
|
3180
|
+
function setTaskModelDefault({ taskType, modelRegistryId, strategy, quorumSize }) {
|
|
3181
|
+
if (!taskType) throw new Error('Default requires taskType');
|
|
3182
|
+
const db = getDb();
|
|
3183
|
+
return runImmediateTransaction(db, () => {
|
|
3184
|
+
if (modelRegistryId) {
|
|
3185
|
+
const exists = db.prepare('SELECT 1 FROM model_registry WHERE id = ?').get(modelRegistryId);
|
|
3186
|
+
if (!exists) throw new Error('model_registry_id does not match any registered model');
|
|
3187
|
+
}
|
|
3188
|
+
setModelDefault(taskType, modelRegistryId, strategy, quorumSize);
|
|
3189
|
+
return {
|
|
3190
|
+
task_type: taskType,
|
|
3191
|
+
model_registry_id: modelRegistryId || null,
|
|
3192
|
+
strategy: strategy || 'single',
|
|
3193
|
+
quorum_size: quorumSize || 1,
|
|
3194
|
+
};
|
|
3195
|
+
});
|
|
3196
|
+
}
|
|
3197
|
+
|
|
2958
3198
|
function getModelDefault(taskType) {
|
|
2959
3199
|
return getDb().prepare('SELECT * FROM model_defaults WHERE task_type = ?').get(taskType) || null;
|
|
2960
3200
|
}
|
|
@@ -3810,6 +4050,7 @@ module.exports = {
|
|
|
3810
4050
|
saveChatBranches,
|
|
3811
4051
|
loadChatBranches,
|
|
3812
4052
|
// Backup
|
|
4053
|
+
checkpointWalOrThrow,
|
|
3813
4054
|
createBackup,
|
|
3814
4055
|
listBackups,
|
|
3815
4056
|
deleteBackup,
|
|
@@ -3861,6 +4102,10 @@ module.exports = {
|
|
|
3861
4102
|
listActiveSlackThreads,
|
|
3862
4103
|
updateSlackThread,
|
|
3863
4104
|
deleteExpiredSlackThreads,
|
|
4105
|
+
claimSlackInboundEvent,
|
|
4106
|
+
completeSlackInboundEvent,
|
|
4107
|
+
releaseSlackInboundEvent,
|
|
4108
|
+
pruneSlackInboundEvents,
|
|
3864
4109
|
// Model Providers
|
|
3865
4110
|
upsertModelProvider,
|
|
3866
4111
|
getModelProvider,
|
|
@@ -3870,6 +4115,9 @@ module.exports = {
|
|
|
3870
4115
|
getProviderByType,
|
|
3871
4116
|
setProviderAuthMethod,
|
|
3872
4117
|
getProviderAuthMethod,
|
|
4118
|
+
saveSetupProvider,
|
|
4119
|
+
setDefaultProviderSelection,
|
|
4120
|
+
disableModelProviderByType,
|
|
3873
4121
|
VALID_AUTH_METHODS,
|
|
3874
4122
|
SCHEMA_VERSION,
|
|
3875
4123
|
deleteModelProvider,
|
|
@@ -3888,6 +4136,7 @@ module.exports = {
|
|
|
3888
4136
|
getAgentRunnerScorecard,
|
|
3889
4137
|
// Model Defaults
|
|
3890
4138
|
setModelDefault,
|
|
4139
|
+
setTaskModelDefault,
|
|
3891
4140
|
getModelDefault,
|
|
3892
4141
|
listModelDefaults,
|
|
3893
4142
|
// Benchmark Evaluations
|