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.
- package/README.md +3 -3
- package/package.json +2 -2
- package/template/bin/dev.sh +7 -1
- package/template/bin/setup.js +53 -9
- package/template/bin/sync-images.js +53 -0
- package/template/builder-journal.md +17 -0
- package/template/claude-task-manager/api-prompts.js +98 -13
- package/template/claude-task-manager/api-reviews.js +82 -5
- package/template/claude-task-manager/db.js +32 -5
- package/template/claude-task-manager/docs/session-capture-foundation-design.md +1273 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +696 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +49 -1
- package/template/claude-task-manager/lib/session-capture.js +421 -0
- package/template/claude-task-manager/lib/session-history.js +135 -15
- package/template/claude-task-manager/lib/session-jobs.js +10 -5
- package/template/claude-task-manager/lib/session-stream.js +87 -19
- package/template/claude-task-manager/lib/setup-provider-config.js +115 -0
- package/template/claude-task-manager/lib/walle-ctm-history.js +72 -0
- package/template/claude-task-manager/lib/walle-session-context.js +61 -0
- package/template/claude-task-manager/lib/walle-transcript.js +176 -0
- package/template/claude-task-manager/public/css/setup.css +35 -8
- package/template/claude-task-manager/public/css/walle-session.css +56 -0
- package/template/claude-task-manager/public/css/walle.css +120 -0
- package/template/claude-task-manager/public/index.html +814 -181
- package/template/claude-task-manager/public/js/message-renderer.js +148 -19
- package/template/claude-task-manager/public/js/reviews.js +120 -62
- package/template/claude-task-manager/public/js/setup.js +75 -31
- package/template/claude-task-manager/public/js/stream-view.js +115 -55
- package/template/claude-task-manager/public/js/walle-session.js +84 -2
- package/template/claude-task-manager/public/js/walle.js +308 -54
- package/template/claude-task-manager/server.js +1092 -146
- package/template/claude-task-manager/session-integrity.js +181 -54
- package/template/claude-task-manager/session-utils.js +123 -41
- package/template/claude-task-manager/workers/state-detectors/codex.js +5 -2
- package/template/package.json +1 -1
- package/template/wall-e/adapters/ctm.js +39 -18
- package/template/wall-e/agent-runners/contract.js +17 -0
- package/template/wall-e/agent-runners/index.js +22 -0
- package/template/wall-e/agent-runtime/harness.js +212 -0
- package/template/wall-e/agent-runtime/index.js +8 -0
- package/template/wall-e/agent-runtime/registry.js +67 -0
- package/template/wall-e/agent-runtime/session-store.js +179 -0
- package/template/wall-e/agent-runtime/spawn.js +208 -0
- package/template/wall-e/api-walle.js +174 -7
- package/template/wall-e/brain.js +266 -28
- package/template/wall-e/channels/policy.js +88 -0
- package/template/wall-e/channels/registry.js +15 -1
- package/template/wall-e/channels/reply-dispatcher.js +70 -0
- package/template/wall-e/channels/session-bindings.js +51 -0
- package/template/wall-e/chat/code-review-context.js +29 -0
- package/template/wall-e/chat.js +188 -42
- package/template/wall-e/coding/acp-adapter.js +188 -0
- package/template/wall-e/coding/agent-catalog.js +129 -0
- package/template/wall-e/coding/compaction-service.js +247 -0
- package/template/wall-e/coding/execution-trace.js +3 -0
- package/template/wall-e/coding/instruction-service.js +224 -0
- package/template/wall-e/coding/model-message.js +67 -0
- package/template/wall-e/coding/permission-rules-store.js +111 -0
- package/template/wall-e/coding/permission-service.js +266 -0
- package/template/wall-e/coding/prompt-bundle.js +67 -0
- package/template/wall-e/coding/prompt-runtime.js +243 -0
- package/template/wall-e/coding/provider-transform.js +188 -0
- package/template/wall-e/coding/runtime-mode.js +132 -0
- package/template/wall-e/coding/snapshot-service.js +155 -0
- package/template/wall-e/coding/stream-processor.js +268 -0
- package/template/wall-e/coding/task-tool.js +255 -0
- package/template/wall-e/coding/tool-registry.js +361 -0
- package/template/wall-e/coding/transcript-writer.js +143 -0
- package/template/wall-e/coding/workspace-replay.js +324 -0
- package/template/wall-e/coding-context.js +4 -22
- package/template/wall-e/coding-orchestrator.js +307 -18
- package/template/wall-e/coding-prompts.js +44 -3
- package/template/wall-e/context/context-builder.js +43 -1
- package/template/wall-e/context/topic-matcher.js +1 -1
- package/template/wall-e/eval/agent-runner.js +59 -13
- package/template/wall-e/eval/benchmarks/memory-retrieval.json +155 -57
- package/template/wall-e/eval/benchmarks.js +100 -16
- package/template/wall-e/eval/eval-orchestrator.js +218 -8
- package/template/wall-e/eval/harvester.js +62 -5
- package/template/wall-e/eval/head-to-head.js +23 -2
- package/template/wall-e/eval/humaneval-adapter.js +30 -5
- package/template/wall-e/eval/livecodebench-adapter.js +29 -5
- package/template/wall-e/eval/manifest.js +186 -0
- package/template/wall-e/eval/run-agent-benchmarks.js +66 -2
- package/template/wall-e/eval/session-retrieval-benchmark.js +150 -0
- package/template/wall-e/eval/session-transcripts.js +57 -4
- package/template/wall-e/eval/swebench-adapter.js +109 -3
- package/template/wall-e/evaluation/agent-router.js +53 -1
- package/template/wall-e/evaluation/coding-quorum.js +48 -1
- package/template/wall-e/evaluation/router.js +4 -2
- package/template/wall-e/evaluation/tier-selector.js +11 -1
- package/template/wall-e/extraction/contradiction.js +2 -2
- package/template/wall-e/extraction/indexer.js +2 -1
- package/template/wall-e/extraction/knowledge-extractor.js +2 -2
- package/template/wall-e/hooks/cli.js +92 -0
- package/template/wall-e/hooks/discovery.js +119 -0
- package/template/wall-e/hooks/index.js +7 -0
- package/template/wall-e/hooks/manifest.js +55 -0
- package/template/wall-e/hooks/runtime.js +84 -0
- package/template/wall-e/hooks/session-memory.js +225 -0
- package/template/wall-e/http/auth.js +6 -2
- package/template/wall-e/http/chat-api.js +54 -8
- package/template/wall-e/integrations/claude-plugin/hooks/hooks.json +27 -0
- package/template/wall-e/integrations/claude-plugin/hooks/walle-precompact-hook.sh +5 -0
- package/template/wall-e/integrations/claude-plugin/hooks/walle-stop-hook.sh +5 -0
- package/template/wall-e/integrations/codex-plugin/hooks/walle-hook.sh +7 -0
- package/template/wall-e/integrations/codex-plugin/hooks.json +37 -0
- package/template/wall-e/listening/calendar.js +3 -1
- package/template/wall-e/llm/client.js +64 -10
- package/template/wall-e/llm/google.js +39 -5
- package/template/wall-e/llm/ollama.js +1 -1
- package/template/wall-e/llm/ollama.plugin.json +1 -1
- package/template/wall-e/llm/provider-availability.js +10 -0
- package/template/wall-e/llm/provider-error.js +269 -0
- package/template/wall-e/llm/tool-adapter.js +48 -12
- package/template/wall-e/loops/boot.js +2 -1
- package/template/wall-e/loops/initiative.js +2 -2
- package/template/wall-e/loops/tasks.js +8 -47
- package/template/wall-e/loops/workspace-prompts.js +20 -0
- package/template/wall-e/mcp-server.js +442 -1
- package/template/wall-e/memory/session-ingest-service.js +159 -0
- package/template/wall-e/memory/source-indexer.js +289 -0
- package/template/wall-e/plugins/discovery.js +83 -0
- package/template/wall-e/plugins/manifest-loader.js +50 -10
- package/template/wall-e/plugins/manifest-schema.js +69 -0
- package/template/wall-e/plugins/model-catalog.js +55 -0
- package/template/wall-e/prompts/coding/base.txt +2 -0
- package/template/wall-e/prompts/coding/deepseek.txt +1 -0
- package/template/wall-e/prompts/coding/memory-protocol.md +9 -0
- package/template/wall-e/prompts/coding/plan.txt +1 -0
- package/template/wall-e/runtime/execution-trace.js +220 -0
- package/template/wall-e/security/audit.js +266 -0
- package/template/wall-e/security/ssrf.js +236 -0
- package/template/wall-e/session-files.js +303 -0
- package/template/wall-e/skills/_bundled/slack-backfill/SKILL.md +3 -0
- package/template/wall-e/skills/_bundled/slack-sync/SKILL.md +3 -0
- package/template/wall-e/skills/internal-skill-registry.js +2 -2
- package/template/wall-e/skills/script-skill-runner.js +143 -0
- package/template/wall-e/skills/skill-executor.js +5 -6
- package/template/wall-e/skills/skill-fallback.js +3 -1
- package/template/wall-e/skills/skill-harness-registry.js +7 -8
- package/template/wall-e/skills/skill-planner.js +52 -4
- package/template/wall-e/skills/slack-ingest.js +11 -3
- package/template/wall-e/sources/base.js +90 -0
- package/template/wall-e/sources/builtin.js +33 -0
- package/template/wall-e/sources/claude-code-jsonl.js +78 -0
- package/template/wall-e/sources/codex-jsonl.js +125 -0
- package/template/wall-e/sources/coding-session-utils.js +117 -0
- package/template/wall-e/sources/contract-suite.js +59 -0
- package/template/wall-e/sources/gemini-jsonl.js +85 -0
- package/template/wall-e/sources/index.js +9 -0
- package/template/wall-e/sources/jsonl-utils.js +181 -0
- package/template/wall-e/sources/record-types.js +252 -0
- package/template/wall-e/sources/registry.js +92 -0
- package/template/wall-e/sources/transforms.js +100 -0
- package/template/wall-e/sources/walle-jsonl.js +108 -0
- package/template/wall-e/tools/coding-middleware.js +31 -1
- package/template/wall-e/tools/file-tracker.js +25 -1
- package/template/wall-e/tools/local-tools.js +75 -47
- package/template/wall-e/tools/session-sharing.js +68 -1
- package/template/wall-e/tools/shell-analyzer.js +1 -1
- package/template/wall-e/tools/shell-policy.js +47 -0
- package/template/wall-e/tools/snapshot.js +42 -0
- package/template/wall-e/training/harvester.js +62 -5
- package/template/wall-e/utils/repair.js +253 -1
- package/template/website/index.html +3 -3
- package/template/wall-e/skills/_bundled/slack-mentions/.watched-threads.json +0 -18
|
@@ -46,7 +46,8 @@ function initSetupTabs() {
|
|
|
46
46
|
function _keyInputIdFor(type) {
|
|
47
47
|
return type === 'anthropic' ? 'setup-api-key'
|
|
48
48
|
: type === 'openai' ? 'setup-openai-key'
|
|
49
|
-
: type === 'google' ? 'setup-google-key'
|
|
49
|
+
: type === 'google' ? 'setup-google-key'
|
|
50
|
+
: type === 'deepseek' ? 'setup-deepseek-key' : null;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
function initProviderPicker() {
|
|
@@ -71,20 +72,32 @@ function initProviderPicker() {
|
|
|
71
72
|
document.querySelectorAll('#setup-panel button[data-default-btn]').forEach(function(btn) {
|
|
72
73
|
btn.addEventListener('click', async function() {
|
|
73
74
|
var type = btn.getAttribute('data-default-btn');
|
|
75
|
+
var modelEl = document.getElementById(_selectIdFor(type));
|
|
76
|
+
var previousDefault = document.querySelector('#setup-panel [data-default-btn].is-default')?.getAttribute('data-default-btn') || null;
|
|
77
|
+
_renderDefaultBadge(type);
|
|
78
|
+
btn.classList.add('is-saving');
|
|
79
|
+
document.querySelectorAll('#setup-panel button[data-default-btn]').forEach(function(b) { b.disabled = true; });
|
|
74
80
|
try {
|
|
75
81
|
var r = await fetch('/api/setup/set-default', {
|
|
76
82
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
77
|
-
body: JSON.stringify({ type: type }),
|
|
83
|
+
body: JSON.stringify({ type: type, model: modelEl ? modelEl.value : '' }),
|
|
78
84
|
});
|
|
79
85
|
var d = await r.json();
|
|
80
86
|
if (d.ok) {
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
if (modelEl && modelEl.value) _rememberProviderModel(type, modelEl.value);
|
|
88
|
+
setupToast(type + ' set as default' + (d.live_updated ? '' : ' (restart scheduled)'));
|
|
83
89
|
loadHealthDashboard();
|
|
84
90
|
} else {
|
|
91
|
+
_renderDefaultBadge(previousDefault);
|
|
85
92
|
setupToast(d.error || 'Failed to set default', 'error');
|
|
86
93
|
}
|
|
87
|
-
} catch (e) {
|
|
94
|
+
} catch (e) {
|
|
95
|
+
_renderDefaultBadge(previousDefault);
|
|
96
|
+
setupToast(e.message, 'error');
|
|
97
|
+
} finally {
|
|
98
|
+
btn.classList.remove('is-saving');
|
|
99
|
+
document.querySelectorAll('#setup-panel button[data-default-btn]').forEach(function(b) { b.disabled = false; });
|
|
100
|
+
}
|
|
88
101
|
});
|
|
89
102
|
});
|
|
90
103
|
|
|
@@ -130,8 +143,8 @@ function initProviderPicker() {
|
|
|
130
143
|
});
|
|
131
144
|
|
|
132
145
|
// API key input → debounced model refresh (preserves prior behavior)
|
|
133
|
-
['setup-api-key', 'setup-openai-key', 'setup-google-key'].forEach(function(id) {
|
|
134
|
-
var providerByInput = { 'setup-api-key': 'anthropic', 'setup-openai-key': 'openai', 'setup-google-key': 'google' };
|
|
146
|
+
['setup-api-key', 'setup-openai-key', 'setup-google-key', 'setup-deepseek-key'].forEach(function(id) {
|
|
147
|
+
var providerByInput = { 'setup-api-key': 'anthropic', 'setup-openai-key': 'openai', 'setup-google-key': 'google', 'setup-deepseek-key': 'deepseek' };
|
|
135
148
|
var input = document.getElementById(id);
|
|
136
149
|
if (!input) return;
|
|
137
150
|
input.addEventListener('input', function() { scheduleKeyChange(providerByInput[id], id); });
|
|
@@ -214,6 +227,7 @@ async function saveProviderCard(type) {
|
|
|
214
227
|
});
|
|
215
228
|
var d = await r.json();
|
|
216
229
|
if (d.ok) {
|
|
230
|
+
if (model) _applyProviderModelSelection(type, model);
|
|
217
231
|
if (resultEl) { resultEl.className = 'test-result ok'; resultEl.textContent = '✓ Saved'; }
|
|
218
232
|
setTimeout(function() { if (resultEl) { resultEl.style.display = 'none'; } }, 4000);
|
|
219
233
|
loadHealthDashboard();
|
|
@@ -229,6 +243,7 @@ async function saveProviderCard(type) {
|
|
|
229
243
|
// Cache: { '<provider>:<keyFingerprint>': [{id,name,capabilities}, ...] }
|
|
230
244
|
var _modelsCache = {};
|
|
231
245
|
var _keyDebounceTimers = {};
|
|
246
|
+
var _providerModelPrefs = {};
|
|
232
247
|
|
|
233
248
|
function _keyFingerprint(key) {
|
|
234
249
|
if (!key) return 'env';
|
|
@@ -241,9 +256,49 @@ function _selectIdFor(provider) {
|
|
|
241
256
|
return provider === 'openai' ? 'setup-openai-model'
|
|
242
257
|
: provider === 'google' ? 'setup-google-model'
|
|
243
258
|
: provider === 'ollama' ? 'setup-ollama-model'
|
|
259
|
+
: provider === 'deepseek' ? 'setup-deepseek-model'
|
|
244
260
|
: 'setup-walle-model';
|
|
245
261
|
}
|
|
246
262
|
|
|
263
|
+
function _rememberProviderModel(provider, model) {
|
|
264
|
+
if (!provider) return;
|
|
265
|
+
if (model) _providerModelPrefs[provider] = model;
|
|
266
|
+
else delete _providerModelPrefs[provider];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function _preferredModelFor(provider, sel) {
|
|
270
|
+
return _providerModelPrefs[provider] || (sel ? sel.value : '');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function _applyProviderModelSelection(provider, model) {
|
|
274
|
+
if (!model) return;
|
|
275
|
+
_rememberProviderModel(provider, model);
|
|
276
|
+
var sel = document.getElementById(_selectIdFor(provider));
|
|
277
|
+
if (!sel) return;
|
|
278
|
+
for (var i = 0; i < sel.options.length; i++) {
|
|
279
|
+
if (sel.options[i].value === model) {
|
|
280
|
+
sel.value = model;
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
var opt = document.createElement('option');
|
|
285
|
+
opt.value = model;
|
|
286
|
+
opt.textContent = model + ' (custom)';
|
|
287
|
+
sel.insertBefore(opt, sel.firstChild);
|
|
288
|
+
sel.value = model;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function initProviderModelPreferenceTracking() {
|
|
292
|
+
['anthropic', 'openai', 'google', 'ollama', 'deepseek'].forEach(function(provider) {
|
|
293
|
+
var sel = document.getElementById(_selectIdFor(provider));
|
|
294
|
+
if (!sel || sel.dataset.modelPrefBound === '1') return;
|
|
295
|
+
sel.dataset.modelPrefBound = '1';
|
|
296
|
+
sel.addEventListener('change', function() {
|
|
297
|
+
_rememberProviderModel(provider, sel.value);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
247
302
|
async function loadModels(provider, opts) {
|
|
248
303
|
opts = opts || {};
|
|
249
304
|
var sel = document.getElementById(_selectIdFor(provider));
|
|
@@ -251,7 +306,7 @@ async function loadModels(provider, opts) {
|
|
|
251
306
|
var keyOverride = (opts.apiKey || '').trim();
|
|
252
307
|
var cacheKey = provider + ':' + _keyFingerprint(keyOverride);
|
|
253
308
|
if (_modelsCache[cacheKey] && !opts.force) {
|
|
254
|
-
_renderModelOptions(sel, _modelsCache[cacheKey]);
|
|
309
|
+
_renderModelOptions(sel, _modelsCache[cacheKey], _preferredModelFor(provider, sel));
|
|
255
310
|
return;
|
|
256
311
|
}
|
|
257
312
|
// Don't capture sel.value here — loadStatus may set the saved model AFTER this
|
|
@@ -273,7 +328,7 @@ async function loadModels(provider, opts) {
|
|
|
273
328
|
var models = (d && Array.isArray(d.models)) ? d.models : [];
|
|
274
329
|
if (models.length > 0) {
|
|
275
330
|
_modelsCache[cacheKey] = models;
|
|
276
|
-
_renderModelOptions(sel, models);
|
|
331
|
+
_renderModelOptions(sel, models, _preferredModelFor(provider, sel));
|
|
277
332
|
}
|
|
278
333
|
} catch { /* keep existing options on error */ }
|
|
279
334
|
sel.disabled = false;
|
|
@@ -370,21 +425,19 @@ async function loadStatus() {
|
|
|
370
425
|
document.getElementById('setup-api-key').placeholder = '\u2022\u2022\u2022\u2022\u2022 (configured)';
|
|
371
426
|
}
|
|
372
427
|
if (d.walle_model) {
|
|
373
|
-
var selId = activeProvider
|
|
374
|
-
: activeProvider === 'google' ? 'setup-google-model'
|
|
375
|
-
: activeProvider === 'ollama' ? 'setup-ollama-model'
|
|
376
|
-
: 'setup-walle-model';
|
|
428
|
+
var selId = _selectIdFor(activeProvider);
|
|
377
429
|
var sel = document.getElementById(selId);
|
|
378
430
|
if (sel) {
|
|
431
|
+
var preferredModel = (activeProvider && _providerModelPrefs[activeProvider]) || d.walle_model;
|
|
379
432
|
var found = false;
|
|
380
433
|
for (var i = 0; i < sel.options.length; i++) {
|
|
381
|
-
if (sel.options[i].value ===
|
|
434
|
+
if (sel.options[i].value === preferredModel) { sel.value = preferredModel; found = true; break; }
|
|
382
435
|
}
|
|
383
436
|
if (!found) {
|
|
384
437
|
var opt = document.createElement('option');
|
|
385
|
-
opt.value =
|
|
438
|
+
opt.value = preferredModel; opt.textContent = preferredModel;
|
|
386
439
|
sel.insertBefore(opt, sel.firstChild);
|
|
387
|
-
sel.value =
|
|
440
|
+
sel.value = preferredModel;
|
|
388
441
|
}
|
|
389
442
|
}
|
|
390
443
|
}
|
|
@@ -446,6 +499,9 @@ async function saveConfig() {
|
|
|
446
499
|
modelVal = document.getElementById('setup-google-model').value;
|
|
447
500
|
} else if (_selectedProvider === 'ollama') {
|
|
448
501
|
modelVal = document.getElementById('setup-ollama-model').value;
|
|
502
|
+
} else if (_selectedProvider === 'deepseek') {
|
|
503
|
+
apiVal = document.getElementById('setup-deepseek-key').value.trim();
|
|
504
|
+
modelVal = document.getElementById('setup-deepseek-model').value;
|
|
449
505
|
}
|
|
450
506
|
|
|
451
507
|
if (!ownerVal) {
|
|
@@ -1589,6 +1645,7 @@ SETUP.init = function() {
|
|
|
1589
1645
|
});
|
|
1590
1646
|
initSetupTabs();
|
|
1591
1647
|
initProviderPicker();
|
|
1648
|
+
initProviderModelPreferenceTracking();
|
|
1592
1649
|
initPcardAccordion();
|
|
1593
1650
|
// Inject the brand-mark SVG for each provider card (anthropic / openai /
|
|
1594
1651
|
// google / ollama / mlx). The card headers carry data-pcard-header="<key>";
|
|
@@ -1792,21 +1849,8 @@ async function loadProviderCardStates() {
|
|
|
1792
1849
|
}
|
|
1793
1850
|
}
|
|
1794
1851
|
// Pre-fill model dropdown if backend has a per-provider model
|
|
1795
|
-
if (p.model)
|
|
1796
|
-
|
|
1797
|
-
if (sel) {
|
|
1798
|
-
var found = false;
|
|
1799
|
-
for (var j = 0; j < sel.options.length; j++) {
|
|
1800
|
-
if (sel.options[j].value === p.model) { sel.value = p.model; found = true; break; }
|
|
1801
|
-
}
|
|
1802
|
-
if (!found) {
|
|
1803
|
-
var opt = document.createElement('option');
|
|
1804
|
-
opt.value = p.model; opt.textContent = p.model + ' (custom)';
|
|
1805
|
-
sel.insertBefore(opt, sel.firstChild);
|
|
1806
|
-
sel.value = p.model;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1852
|
+
if (p.model) _applyProviderModelSelection(p.type, p.model);
|
|
1853
|
+
else _rememberProviderModel(p.type, '');
|
|
1810
1854
|
}
|
|
1811
1855
|
// Conditional Devbox row: only show under Anthropic if the file exists
|
|
1812
1856
|
// server-side. We probe via a lightweight HEAD-style check: ask the
|
|
@@ -340,32 +340,58 @@ function _mergeConsecutiveToolGroups(container, newEl) {
|
|
|
340
340
|
const newItems = newEl.querySelector('.thought-group-items');
|
|
341
341
|
if (!targetItems || !newItems) return false;
|
|
342
342
|
while (newItems.firstChild) targetItems.appendChild(newItems.firstChild);
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const stepCount = targetItems.children.length;
|
|
347
|
-
countEl.textContent = '— ' + stepCount + ' step' + (stepCount === 1 ? '' : 's');
|
|
348
|
-
}
|
|
349
|
-
// Extend the time range to first–last
|
|
343
|
+
// Extend the time range to first–last, then let the shared renderer
|
|
344
|
+
// recompute count + preview. Keeping all item parentUuids on the nested
|
|
345
|
+
// rows prevents a later update from replacing the whole merged group.
|
|
350
346
|
const newLast = newEl.dataset.lastTime || '';
|
|
351
347
|
if (newLast) lastChild.dataset.lastTime = newLast;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
348
|
+
if (typeof MR !== 'undefined' && typeof MR.refreshConversationActivityGroup === 'function') {
|
|
349
|
+
MR.refreshConversationActivityGroup(lastChild);
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function _assignConversationParentUuid(el, parentUuid) {
|
|
355
|
+
if (!el || !parentUuid) return;
|
|
356
|
+
if (el.classList && el.classList.contains('conv-tool-group')) {
|
|
357
|
+
const items = el.querySelector('.thought-group-items');
|
|
358
|
+
const row = items && (items.lastElementChild || (items.children && items.children[items.children.length - 1]));
|
|
359
|
+
if (row && row.dataset && !row.dataset.parentUuid) row.dataset.parentUuid = parentUuid;
|
|
360
|
+
} else if (el.dataset) {
|
|
361
|
+
el.dataset.parentUuid = parentUuid;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function _replaceConversationParentEvent(existing, newEl) {
|
|
366
|
+
if (!existing) return false;
|
|
367
|
+
const existingGroup = existing.closest ? existing.closest('.conv-tool-group') : null;
|
|
368
|
+
if (existingGroup && newEl && newEl.classList && newEl.classList.contains('conv-tool-group')) {
|
|
369
|
+
const targetRow = existing.closest('.tool-activity-item') || existing;
|
|
370
|
+
const targetParent = targetRow.parentNode;
|
|
371
|
+
const newItems = newEl.querySelector('.thought-group-items');
|
|
372
|
+
if (!targetParent || !newItems) return false;
|
|
373
|
+
const rows = Array.from(newItems.children || []);
|
|
374
|
+
for (const row of rows) targetParent.insertBefore(row, targetRow);
|
|
375
|
+
targetParent.removeChild(targetRow);
|
|
376
|
+
if (newEl.dataset.lastTime) existingGroup.dataset.lastTime = newEl.dataset.lastTime;
|
|
377
|
+
if (typeof MR !== 'undefined' && typeof MR.refreshConversationActivityGroup === 'function') {
|
|
378
|
+
MR.refreshConversationActivityGroup(existingGroup);
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
357
381
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
382
|
+
if (existingGroup && !newEl) {
|
|
383
|
+
const targetRow = existing.closest('.tool-activity-item') || existing;
|
|
384
|
+
const targetParent = targetRow.parentNode;
|
|
385
|
+
if (targetParent) targetParent.removeChild(targetRow);
|
|
386
|
+
const remaining = existingGroup.querySelector('.thought-group-items')?.children?.length || 0;
|
|
387
|
+
if (remaining === 0) existingGroup.remove();
|
|
388
|
+
else if (typeof MR !== 'undefined' && typeof MR.refreshConversationActivityGroup === 'function') {
|
|
389
|
+
MR.refreshConversationActivityGroup(existingGroup);
|
|
390
|
+
}
|
|
391
|
+
return true;
|
|
366
392
|
}
|
|
367
|
-
|
|
368
|
-
|
|
393
|
+
if (newEl) existing.replaceWith(newEl);
|
|
394
|
+
else existing.remove();
|
|
369
395
|
return true;
|
|
370
396
|
}
|
|
371
397
|
|
|
@@ -379,6 +405,7 @@ function populateConversationView(container, events) {
|
|
|
379
405
|
for (const evt of events) {
|
|
380
406
|
const el = renderConversationEvent(evt);
|
|
381
407
|
if (!el) continue;
|
|
408
|
+
if (evt.data?.parentUuid) _assignConversationParentUuid(el, evt.data.parentUuid);
|
|
382
409
|
if (_mergeConsecutiveToolGroups(container, el)) continue;
|
|
383
410
|
container.appendChild(el);
|
|
384
411
|
}
|
|
@@ -671,10 +698,10 @@ function _applyStreamEvent(sessionId, container, msg) {
|
|
|
671
698
|
if (existing) {
|
|
672
699
|
const newEl = renderConversationEvent(msg);
|
|
673
700
|
if (newEl) {
|
|
674
|
-
newEl
|
|
675
|
-
existing
|
|
701
|
+
_assignConversationParentUuid(newEl, parentUuid);
|
|
702
|
+
_replaceConversationParentEvent(existing, newEl);
|
|
676
703
|
} else {
|
|
677
|
-
existing
|
|
704
|
+
_replaceConversationParentEvent(existing, null); // Event became empty after update
|
|
678
705
|
}
|
|
679
706
|
return;
|
|
680
707
|
}
|
|
@@ -690,8 +717,8 @@ function _applyStreamEvent(sessionId, container, msg) {
|
|
|
690
717
|
if (existing) {
|
|
691
718
|
const newEl = renderConversationEvent(msg);
|
|
692
719
|
if (newEl) {
|
|
693
|
-
newEl
|
|
694
|
-
existing
|
|
720
|
+
_assignConversationParentUuid(newEl, parentUuid);
|
|
721
|
+
_replaceConversationParentEvent(existing, newEl);
|
|
695
722
|
}
|
|
696
723
|
return;
|
|
697
724
|
}
|
|
@@ -701,7 +728,7 @@ function _applyStreamEvent(sessionId, container, msg) {
|
|
|
701
728
|
if (!el) return; // Skip empty events
|
|
702
729
|
_removeConversationState(container);
|
|
703
730
|
if (parentUuid) {
|
|
704
|
-
el
|
|
731
|
+
_assignConversationParentUuid(el, parentUuid);
|
|
705
732
|
if (seen) seen.add(parentUuid);
|
|
706
733
|
}
|
|
707
734
|
// If this is a tool-only thought-group AND the last rendered child is also
|
|
@@ -920,7 +947,8 @@ async function _loadOlder(sessionId, convView) {
|
|
|
920
947
|
for (const evt of events) {
|
|
921
948
|
const el = renderConversationEvent(evt);
|
|
922
949
|
if (el) {
|
|
923
|
-
if (evt.data?.parentUuid) el
|
|
950
|
+
if (evt.data?.parentUuid) _assignConversationParentUuid(el, evt.data.parentUuid);
|
|
951
|
+
if (_mergeConsecutiveToolGroups(frag, el)) continue;
|
|
924
952
|
frag.appendChild(el);
|
|
925
953
|
}
|
|
926
954
|
}
|
|
@@ -957,18 +985,6 @@ async function _primeConversationView(sessionId, convView) {
|
|
|
957
985
|
const events = _messagesToEvents(page.messages);
|
|
958
986
|
populateConversationView(convView, events);
|
|
959
987
|
|
|
960
|
-
// Assign data-parent-uuid on each primed row so live stream-events
|
|
961
|
-
// that cover the same turn can find + replace instead of duplicate.
|
|
962
|
-
// populateConversationView appends children in order; we walk in
|
|
963
|
-
// lockstep skipping events that rendered to null (empty).
|
|
964
|
-
let domIdx = 0;
|
|
965
|
-
for (const e of events) {
|
|
966
|
-
const child = convView.children[domIdx];
|
|
967
|
-
if (!child) break;
|
|
968
|
-
if (e.data?.parentUuid) child.dataset.parentUuid = e.data.parentUuid;
|
|
969
|
-
domIdx++;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
988
|
// Seed the parentUuid dedup set from the primed history so live
|
|
973
989
|
// stream-events that cover the same turns don't re-append.
|
|
974
990
|
const seen = new Set();
|
|
@@ -1011,24 +1027,62 @@ function _resetPrimingState(sessionId) {
|
|
|
1011
1027
|
|
|
1012
1028
|
// --- Tooltip hover binding ---
|
|
1013
1029
|
|
|
1030
|
+
function _streamTooltipContains(parent, child) {
|
|
1031
|
+
if (!parent || !child) return false;
|
|
1032
|
+
if (typeof parent.contains === 'function') return parent.contains(child);
|
|
1033
|
+
for (let node = child; node; node = node.parentNode) {
|
|
1034
|
+
if (node === parent) return true;
|
|
1035
|
+
}
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function _streamTooltipClosestSessionItem(target, root) {
|
|
1040
|
+
let item = null;
|
|
1041
|
+
if (target && typeof target.closest === 'function') {
|
|
1042
|
+
item = target.closest('.session-item');
|
|
1043
|
+
} else {
|
|
1044
|
+
for (let node = target; node; node = node.parentNode) {
|
|
1045
|
+
const classes = String(node.className || '').split(/\s+/);
|
|
1046
|
+
if (classes.includes('session-item')) {
|
|
1047
|
+
item = node;
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (!item) return null;
|
|
1053
|
+
if (root && item !== root && !_streamTooltipContains(root, item)) return null;
|
|
1054
|
+
return item;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function _scheduleStreamTooltip(item) {
|
|
1058
|
+
const id = item?.dataset?.sessionId;
|
|
1059
|
+
if (!id) return;
|
|
1060
|
+
_streamState.tooltipTimer = setTimeout(() => showStreamTooltip(id, item), 500);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function _handleStreamTooltipMouseOver(e, list) {
|
|
1064
|
+
const item = _streamTooltipClosestSessionItem(e.target, list);
|
|
1065
|
+
if (!item) return;
|
|
1066
|
+
if (e.relatedTarget && _streamTooltipContains(item, e.relatedTarget)) return;
|
|
1067
|
+
hideStreamTooltip();
|
|
1068
|
+
_scheduleStreamTooltip(item);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function _handleStreamTooltipMouseOut(e, list) {
|
|
1072
|
+
const item = _streamTooltipClosestSessionItem(e.target, list);
|
|
1073
|
+
if (!item) return;
|
|
1074
|
+
if (e.relatedTarget && _streamTooltipContains(item, e.relatedTarget)) return;
|
|
1075
|
+
hideStreamTooltip();
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1014
1078
|
function bindStreamTooltips() {
|
|
1015
1079
|
const list = document.getElementById('session-list');
|
|
1016
1080
|
if (!list) return;
|
|
1081
|
+
if (list.__streamTooltipBound) return;
|
|
1082
|
+
list.__streamTooltipBound = true;
|
|
1017
1083
|
|
|
1018
|
-
list.addEventListener('
|
|
1019
|
-
|
|
1020
|
-
hideStreamTooltip();
|
|
1021
|
-
const item = e.target.closest('.session-item');
|
|
1022
|
-
if (!item) return;
|
|
1023
|
-
const id = item.dataset.sessionId;
|
|
1024
|
-
if (!id) return;
|
|
1025
|
-
_streamState.tooltipTimer = setTimeout(() => showStreamTooltip(id, item), 500);
|
|
1026
|
-
}, true);
|
|
1027
|
-
|
|
1028
|
-
list.addEventListener('mouseleave', () => {
|
|
1029
|
-
// Always dismiss — don't gate on closest('.session-item')
|
|
1030
|
-
hideStreamTooltip();
|
|
1031
|
-
}, true);
|
|
1084
|
+
list.addEventListener('mouseover', (e) => _handleStreamTooltipMouseOver(e, list));
|
|
1085
|
+
list.addEventListener('mouseout', (e) => _handleStreamTooltipMouseOut(e, list));
|
|
1032
1086
|
}
|
|
1033
1087
|
|
|
1034
1088
|
// --- Init ---
|
|
@@ -1061,6 +1115,12 @@ if (typeof module !== 'undefined' && module.exports) {
|
|
|
1061
1115
|
_fetchConversationPage,
|
|
1062
1116
|
renderConversationEvent,
|
|
1063
1117
|
populateConversationView,
|
|
1118
|
+
_mergeConsecutiveToolGroups,
|
|
1064
1119
|
_streamState,
|
|
1120
|
+
bindStreamTooltips,
|
|
1121
|
+
_handleStreamTooltipMouseOver,
|
|
1122
|
+
_handleStreamTooltipMouseOut,
|
|
1123
|
+
_streamTooltipClosestSessionItem,
|
|
1124
|
+
_streamTooltipContains,
|
|
1065
1125
|
};
|
|
1066
1126
|
}
|
|
@@ -435,6 +435,25 @@ window.WalleSession = (function() {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// ---------- sendMessage ----------
|
|
438
|
+
function resolveContextSessionId(id) {
|
|
439
|
+
var preferred = state.lastActiveWorkSessionId || state._savedActiveSession || '';
|
|
440
|
+
if (preferred && preferred !== id) {
|
|
441
|
+
var preferredSession = state.sessions.get(preferred);
|
|
442
|
+
if (preferredSession && preferredSession.meta?.type !== 'walle') return preferred;
|
|
443
|
+
}
|
|
444
|
+
var idx = state.tabOrder.indexOf(id);
|
|
445
|
+
var order = idx >= 0
|
|
446
|
+
? state.tabOrder.slice(0, idx).reverse().concat(state.tabOrder.slice(idx + 1).reverse())
|
|
447
|
+
: state.tabOrder.slice().reverse();
|
|
448
|
+
for (var i = 0; i < order.length; i++) {
|
|
449
|
+
var sid = order[i];
|
|
450
|
+
if (sid === id) continue;
|
|
451
|
+
var s = state.sessions.get(sid);
|
|
452
|
+
if (s && s.meta?.type !== 'walle') return sid;
|
|
453
|
+
}
|
|
454
|
+
return '';
|
|
455
|
+
}
|
|
456
|
+
|
|
438
457
|
function sendMessage(id) {
|
|
439
458
|
var container = document.getElementById('walle-session-' + id);
|
|
440
459
|
if (!container) return;
|
|
@@ -458,7 +477,7 @@ window.WalleSession = (function() {
|
|
|
458
477
|
|
|
459
478
|
// Send via WebSocket (include model if user selected one)
|
|
460
479
|
var model = ws.selectedModel || '';
|
|
461
|
-
send({ type: 'walle-message', id: id, text: text, model: model });
|
|
480
|
+
send({ type: 'walle-message', id: id, text: text, model: model, contextSessionId: resolveContextSessionId(id) });
|
|
462
481
|
|
|
463
482
|
// Clear input
|
|
464
483
|
textarea.value = '';
|
|
@@ -632,6 +651,67 @@ window.WalleSession = (function() {
|
|
|
632
651
|
return text;
|
|
633
652
|
}
|
|
634
653
|
|
|
654
|
+
function normalizeProviderIssue(source) {
|
|
655
|
+
if (!source || typeof source !== 'object') return null;
|
|
656
|
+
var issue = source.providerError || source.provider_error || source;
|
|
657
|
+
if (!issue || typeof issue !== 'object') return null;
|
|
658
|
+
if (!issue.title && !issue.rawMessage && !issue.userMessage && issue.code !== 'AI_PROVIDER_ERROR') return null;
|
|
659
|
+
return {
|
|
660
|
+
title: issue.title || 'AI provider failed',
|
|
661
|
+
message: issue.userMessage || issue.message || source.message || source.error || 'Wall-E could not get a response from the configured AI provider.',
|
|
662
|
+
rawMessage: issue.rawMessage || '',
|
|
663
|
+
provider: issue.provider || '',
|
|
664
|
+
model: issue.model || '',
|
|
665
|
+
status: issue.status || '',
|
|
666
|
+
retryAfter: issue.retryAfter || '',
|
|
667
|
+
actionUrl: issue.actionUrl || issue.action_url || '/setup.html',
|
|
668
|
+
actionLabel: issue.actionLabel || 'Open Setup'
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function appendProviderIssueNotice(notice, issue) {
|
|
673
|
+
notice.className += ' provider';
|
|
674
|
+
|
|
675
|
+
var title = document.createElement('div');
|
|
676
|
+
title.className = 'walle-error-title';
|
|
677
|
+
title.textContent = issue.title;
|
|
678
|
+
notice.appendChild(title);
|
|
679
|
+
|
|
680
|
+
var body = document.createElement('div');
|
|
681
|
+
body.className = 'walle-error-body';
|
|
682
|
+
body.textContent = issue.message;
|
|
683
|
+
notice.appendChild(body);
|
|
684
|
+
|
|
685
|
+
var detailLines = [];
|
|
686
|
+
if (issue.status) detailLines.push('HTTP/status: ' + issue.status);
|
|
687
|
+
if (issue.provider) detailLines.push('Provider: ' + issue.provider);
|
|
688
|
+
if (issue.model) detailLines.push('Model: ' + issue.model);
|
|
689
|
+
if (issue.retryAfter) detailLines.push('Retry after: ' + issue.retryAfter);
|
|
690
|
+
if (issue.rawMessage) detailLines.push('', issue.rawMessage);
|
|
691
|
+
if (detailLines.length) {
|
|
692
|
+
var details = document.createElement('details');
|
|
693
|
+
details.className = 'walle-error-details';
|
|
694
|
+
var summary = document.createElement('summary');
|
|
695
|
+
summary.textContent = 'Provider details';
|
|
696
|
+
details.appendChild(summary);
|
|
697
|
+
var pre = document.createElement('pre');
|
|
698
|
+
pre.textContent = detailLines.join('\n').trim();
|
|
699
|
+
details.appendChild(pre);
|
|
700
|
+
notice.appendChild(details);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (issue.actionUrl && /^\/setup/.test(issue.actionUrl) && typeof navTo === 'function') {
|
|
704
|
+
var actions = document.createElement('div');
|
|
705
|
+
actions.className = 'walle-error-actions';
|
|
706
|
+
var btn = document.createElement('button');
|
|
707
|
+
btn.className = 'walle-session-action-btn primary';
|
|
708
|
+
btn.textContent = issue.actionLabel || 'Open Setup';
|
|
709
|
+
btn.onclick = function() { navTo('setup'); };
|
|
710
|
+
actions.appendChild(btn);
|
|
711
|
+
notice.appendChild(actions);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
635
715
|
function handleError(msg) {
|
|
636
716
|
var id = msg.id;
|
|
637
717
|
var messagesArea = document.getElementById('walle-messages-' + id);
|
|
@@ -647,7 +727,9 @@ window.WalleSession = (function() {
|
|
|
647
727
|
|
|
648
728
|
var notice = document.createElement('div');
|
|
649
729
|
notice.className = 'walle-error-notice';
|
|
650
|
-
|
|
730
|
+
var providerIssue = normalizeProviderIssue(msg.providerError ? { providerError: msg.providerError } : null);
|
|
731
|
+
if (providerIssue) appendProviderIssueNotice(notice, providerIssue);
|
|
732
|
+
else notice.textContent = formatWalleError(msg.error);
|
|
651
733
|
messagesArea.appendChild(notice);
|
|
652
734
|
scrollToBottom(messagesArea);
|
|
653
735
|
updateSendButton(id, false);
|