create-walle 0.9.13 → 0.9.14
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 +6 -1
- package/bin/create-walle.js +195 -30
- package/bin/mcp-inject.js +18 -53
- package/package.json +3 -1
- package/template/claude-task-manager/approval-agent.js +7 -0
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +242 -0
- package/template/claude-task-manager/git-utils.js +111 -3
- package/template/claude-task-manager/lib/session-history.js +144 -16
- package/template/claude-task-manager/lib/session-standup.js +409 -0
- 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-default-model.js +55 -0
- package/template/claude-task-manager/lib/walle-mcp-auto-config.js +62 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +83 -19
- package/template/claude-task-manager/lib/worktree-cwd.js +82 -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 +5 -0
- package/template/claude-task-manager/public/index.html +1596 -283
- package/template/claude-task-manager/public/js/session-search-utils.js +171 -1
- package/template/claude-task-manager/public/js/setup.js +62 -19
- package/template/claude-task-manager/public/js/stream-view.js +55 -6
- package/template/claude-task-manager/public/js/walle-session.js +73 -16
- package/template/claude-task-manager/public/js/walle.js +34 -2
- package/template/claude-task-manager/server.js +780 -177
- package/template/claude-task-manager/session-integrity.js +58 -15
- package/template/claude-task-manager/workers/approval-widget-validator.js +15 -5
- package/template/claude-task-manager/workers/state-detectors/codex.js +6 -0
- package/template/package.json +1 -1
- package/template/wall-e/agent.js +36 -7
- package/template/wall-e/api-walle.js +72 -20
- package/template/wall-e/coding/stream-processor.js +22 -2
- package/template/wall-e/coding-orchestrator.js +26 -6
- package/template/wall-e/eval/agent-runner.js +16 -4
- package/template/wall-e/eval/benchmark-generator.js +21 -1
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -596
- package/template/wall-e/eval/codex-cli-baseline.js +633 -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/lib/mcp-integration.js +220 -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/mcp-server.js +272 -10
- package/template/wall-e/memory/ctm-session-context.js +910 -0
- package/template/wall-e/server.js +26 -1
- 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/skill-planner.js +52 -3
- 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 +2 -2
- package/template/builder-journal.md +0 -17
|
@@ -26,6 +26,104 @@
|
|
|
26
26
|
return ids;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function rememberRecentSessionIdentities(index, session) {
|
|
30
|
+
for (const id of getSearchableSessionIds(session)) index.set(id, session);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function findSessionIdentityMatch(index, session) {
|
|
34
|
+
if (!index || !session) return null;
|
|
35
|
+
for (const id of getSearchableSessionIds(session)) {
|
|
36
|
+
const existing = index.get(id);
|
|
37
|
+
if (existing) return existing;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createSessionIdentityIndex(sessions) {
|
|
43
|
+
const index = new Map();
|
|
44
|
+
for (const session of sessions || []) rememberRecentSessionIdentities(index, session);
|
|
45
|
+
return index;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isNewerTime(candidate, current) {
|
|
49
|
+
const candidateMs = Date.parse(String(candidate || ''));
|
|
50
|
+
if (!Number.isFinite(candidateMs)) return false;
|
|
51
|
+
const currentMs = Date.parse(String(current || ''));
|
|
52
|
+
return !Number.isFinite(currentMs) || candidateMs > currentMs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mergeRecentSessionMetadata(target, source) {
|
|
56
|
+
if (!target || !source) return target;
|
|
57
|
+
|
|
58
|
+
const sourceAgent = getRecentSessionAgentType(source);
|
|
59
|
+
const targetAgent = getRecentSessionAgentType(target);
|
|
60
|
+
if (sourceAgent && sourceAgent !== 'shell' && (!target.agent || targetAgent === 'shell')) {
|
|
61
|
+
target.agent = sourceAgent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const field of [
|
|
65
|
+
'agentType',
|
|
66
|
+
'provider',
|
|
67
|
+
'agentSessionId',
|
|
68
|
+
'provisionalId',
|
|
69
|
+
'cmd',
|
|
70
|
+
'launchCommand',
|
|
71
|
+
'cwd',
|
|
72
|
+
'project',
|
|
73
|
+
'projectEntry',
|
|
74
|
+
'firstMessage',
|
|
75
|
+
'title',
|
|
76
|
+
'aiTitle',
|
|
77
|
+
'displayTitle',
|
|
78
|
+
'gitBranch',
|
|
79
|
+
'model',
|
|
80
|
+
'modelProvider',
|
|
81
|
+
'timestamp',
|
|
82
|
+
'version',
|
|
83
|
+
'jsonlPath',
|
|
84
|
+
]) {
|
|
85
|
+
if (!target[field] && source[field]) target[field] = source[field];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isNewerTime(source.modifiedAt, target.modifiedAt)) target.modifiedAt = source.modifiedAt;
|
|
89
|
+
if (isNewerTime(source.fileModifiedAt, target.fileModifiedAt)) target.fileModifiedAt = source.fileModifiedAt;
|
|
90
|
+
if (source.isEmpty === false && target.isEmpty !== false) target.isEmpty = false;
|
|
91
|
+
if ((source.userMsgCount || 0) > (target.userMsgCount || 0)) target.userMsgCount = source.userMsgCount;
|
|
92
|
+
if ((source.fileSize || 0) > (target.fileSize || 0)) target.fileSize = source.fileSize;
|
|
93
|
+
if (source.starred) target.starred = true;
|
|
94
|
+
if (source.userRenamed) target.userRenamed = true;
|
|
95
|
+
return target;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function mergeRecentSessionCandidates(recentSessions, additionalSessions) {
|
|
99
|
+
const merged = (recentSessions || []).map((session) => ({ ...session }));
|
|
100
|
+
const index = createSessionIdentityIndex(merged);
|
|
101
|
+
for (const raw of additionalSessions || []) {
|
|
102
|
+
const candidate = { ...raw };
|
|
103
|
+
const existing = findSessionIdentityMatch(index, candidate);
|
|
104
|
+
if (existing) mergeRecentSessionMetadata(existing, candidate);
|
|
105
|
+
else merged.push(candidate);
|
|
106
|
+
rememberRecentSessionIdentities(index, existing || candidate);
|
|
107
|
+
}
|
|
108
|
+
return merged;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function dedupeSessionCandidates(sessions) {
|
|
112
|
+
const merged = [];
|
|
113
|
+
const index = new Map();
|
|
114
|
+
for (const raw of sessions || []) {
|
|
115
|
+
const candidate = { ...raw };
|
|
116
|
+
const existing = findSessionIdentityMatch(index, candidate);
|
|
117
|
+
if (existing) {
|
|
118
|
+
mergeRecentSessionMetadata(existing, candidate);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
merged.push(candidate);
|
|
122
|
+
rememberRecentSessionIdentities(index, candidate);
|
|
123
|
+
}
|
|
124
|
+
return merged;
|
|
125
|
+
}
|
|
126
|
+
|
|
29
127
|
function scoreSessionIdMatch(session, query) {
|
|
30
128
|
const normalizedQuery = normalizeSearchValue(query);
|
|
31
129
|
if (!normalizedQuery) return 0;
|
|
@@ -81,6 +179,68 @@
|
|
|
81
179
|
return value ? String(value).replace(/\/\.claude\/worktrees\/[^/]+$/, '') : value;
|
|
82
180
|
}
|
|
83
181
|
|
|
182
|
+
const AGENT_FILTER_LABELS = {
|
|
183
|
+
codex: 'Codex',
|
|
184
|
+
claude: 'Claude Code',
|
|
185
|
+
walle: 'Wall-E',
|
|
186
|
+
gemini: 'Gemini CLI',
|
|
187
|
+
opencode: 'OpenCode',
|
|
188
|
+
'claude-desktop': 'Claude Desktop',
|
|
189
|
+
shell: 'Shell / Other',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const AGENT_FILTER_ORDER = ['codex', 'claude', 'walle', 'gemini', 'opencode', 'claude-desktop', 'shell'];
|
|
193
|
+
|
|
194
|
+
function normalizeRecentAgentType(value) {
|
|
195
|
+
if (!value) return '';
|
|
196
|
+
const v = String(value).trim().toLowerCase().replace(/[_\s]+/g, '-');
|
|
197
|
+
if (!v) return '';
|
|
198
|
+
if (AGENT_FILTER_LABELS[v]) return v;
|
|
199
|
+
if (v === 'claude-code' || v === 'claude-cli') return 'claude';
|
|
200
|
+
if (v === 'anthropic') return 'claude';
|
|
201
|
+
if (v === 'claude-desktop-session' || v === 'desktop') return 'claude-desktop';
|
|
202
|
+
if (v === 'wall-e' || v === 'walle-session') return 'walle';
|
|
203
|
+
if (v === 'gemini-cli') return 'gemini';
|
|
204
|
+
if (v === 'open-code' || v === 'opencode-cli') return 'opencode';
|
|
205
|
+
if (v === 'terminal' || v === 'unknown' || v === 'other') return 'shell';
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function detectRecentAgentTypeFromText(value) {
|
|
210
|
+
if (!value) return '';
|
|
211
|
+
const v = String(value).toLowerCase();
|
|
212
|
+
if (v.includes('opencode') || v.includes('open-code')) return 'opencode';
|
|
213
|
+
if (v.includes('wall-e') || v.includes('walle')) return 'walle';
|
|
214
|
+
if (v.includes('codex')) return 'codex';
|
|
215
|
+
if (v.includes('gemini')) return 'gemini';
|
|
216
|
+
if (v.includes('claude')) return 'claude';
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getRecentSessionAgentType(session) {
|
|
221
|
+
const s = session || {};
|
|
222
|
+
for (const candidate of [s.agent, s.agentType, s.provider]) {
|
|
223
|
+
const explicit = normalizeRecentAgentType(candidate);
|
|
224
|
+
if (explicit) return explicit;
|
|
225
|
+
}
|
|
226
|
+
if (s.meta && (s.meta.type === 'walle' || s.meta.agentType === 'walle')) return 'walle';
|
|
227
|
+
if (s.type === 'walle') return 'walle';
|
|
228
|
+
|
|
229
|
+
const detected = detectRecentAgentTypeFromText(s.cmd || (s.meta && s.meta.cmd) || s.launchCommand || '');
|
|
230
|
+
return detected || 'shell';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function recentAgentFilterLabel(agentType) {
|
|
234
|
+
const normalized = normalizeRecentAgentType(agentType) || 'shell';
|
|
235
|
+
return AGENT_FILTER_LABELS[normalized] || AGENT_FILTER_LABELS.shell;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function recentAgentFilterPriority(agentType) {
|
|
239
|
+
const normalized = normalizeRecentAgentType(agentType) || 'shell';
|
|
240
|
+
const idx = AGENT_FILTER_ORDER.indexOf(normalized);
|
|
241
|
+
return idx >= 0 ? idx : AGENT_FILTER_ORDER.length;
|
|
242
|
+
}
|
|
243
|
+
|
|
84
244
|
function sessionPassesRecentSidebarFilters(session, filters) {
|
|
85
245
|
const s = session || {};
|
|
86
246
|
const f = filters || {};
|
|
@@ -93,7 +253,8 @@
|
|
|
93
253
|
const project = stripWorktreePath(f.project || '');
|
|
94
254
|
if (project && stripWorktreePath(s.project || s.cwd || '') !== project) return false;
|
|
95
255
|
|
|
96
|
-
|
|
256
|
+
const agent = normalizeRecentAgentType(f.agent || '');
|
|
257
|
+
if (agent && getRecentSessionAgentType(s) !== agent) return false;
|
|
97
258
|
return true;
|
|
98
259
|
}
|
|
99
260
|
|
|
@@ -103,9 +264,18 @@
|
|
|
103
264
|
|
|
104
265
|
return {
|
|
105
266
|
buildIdMatchResults,
|
|
267
|
+
createSessionIdentityIndex,
|
|
268
|
+
dedupeSessionCandidates,
|
|
106
269
|
filterRecentSessionsForSidebar,
|
|
270
|
+
findSessionIdentityMatch,
|
|
271
|
+
mergeRecentSessionMetadata,
|
|
272
|
+
mergeRecentSessionCandidates,
|
|
273
|
+
getRecentSessionAgentType,
|
|
107
274
|
getSearchableSessionIds,
|
|
108
275
|
normalizeSearchValue,
|
|
276
|
+
normalizeRecentAgentType,
|
|
277
|
+
recentAgentFilterLabel,
|
|
278
|
+
recentAgentFilterPriority,
|
|
109
279
|
scoreSessionIdMatch,
|
|
110
280
|
sessionPassesRecentSidebarFilters,
|
|
111
281
|
sessionMatchesRecentSearchQuery,
|
|
@@ -443,8 +443,12 @@ async function loadStatus() {
|
|
|
443
443
|
}
|
|
444
444
|
// Slack status now handled by loadConnectedServices()
|
|
445
445
|
if (d.version) {
|
|
446
|
-
|
|
447
|
-
|
|
446
|
+
if (typeof window.setAppVersion === 'function') {
|
|
447
|
+
window.setAppVersion(d.version, { product: 'create-walle' });
|
|
448
|
+
} else {
|
|
449
|
+
var vl = document.getElementById('setup-version-label');
|
|
450
|
+
if (vl) vl.textContent = 'CTM / Wall-E v' + d.version;
|
|
451
|
+
}
|
|
448
452
|
}
|
|
449
453
|
if (d.ctm_data_dir) document.getElementById('setup-ctm-data-dir').value = d.ctm_data_dir;
|
|
450
454
|
if (d.walle_data_dir) document.getElementById('setup-walle-data-dir').value = d.walle_data_dir;
|
|
@@ -1069,11 +1073,19 @@ async function saveDataDirs() {
|
|
|
1069
1073
|
}
|
|
1070
1074
|
|
|
1071
1075
|
// ── MCP Integrations ────────────────────────────────────────────────
|
|
1072
|
-
var MCP_ICONS = {
|
|
1076
|
+
var MCP_ICONS = {
|
|
1077
|
+
'Claude Code': '\uD83E\uDD16',
|
|
1078
|
+
'Claude Code Global': '\uD83E\uDD16',
|
|
1079
|
+
'Codex': '\u25CE',
|
|
1080
|
+
'Cursor': '\u2328\uFE0F',
|
|
1081
|
+
'Windsurf': '\uD83C\uDF0A',
|
|
1082
|
+
'Claude Desktop': '\uD83D\uDDA5\uFE0F',
|
|
1083
|
+
};
|
|
1073
1084
|
var MCP_STATUS_MAP = {
|
|
1074
1085
|
configured: { label: 'Connected', cls: 'badge-connected' },
|
|
1075
1086
|
not_configured: { label: 'Not configured', cls: 'badge-missing' },
|
|
1076
1087
|
wrong_port: { label: 'Wrong port', cls: 'badge-warn' },
|
|
1088
|
+
invalid_config: { label: 'Invalid config', cls: 'badge-warn' },
|
|
1077
1089
|
not_installed: { label: 'Not installed', cls: 'badge-dim' },
|
|
1078
1090
|
};
|
|
1079
1091
|
|
|
@@ -1084,6 +1096,14 @@ function mcpDimMsg(text) {
|
|
|
1084
1096
|
return d;
|
|
1085
1097
|
}
|
|
1086
1098
|
|
|
1099
|
+
function mcpActionText(item) {
|
|
1100
|
+
if (item.status === 'configured') return 'Ready for new agent sessions. Restart the AI tool if it was already open.';
|
|
1101
|
+
if (item.status === 'not_configured') return 'Repair Configs will add the wall-e MCP server entry.';
|
|
1102
|
+
if (item.status === 'wrong_port') return 'Repair Configs will update the wall-e entry to the current Wall-E port.';
|
|
1103
|
+
if (item.status === 'invalid_config') return 'Fix the JSON syntax in this file, then run Repair Configs again.';
|
|
1104
|
+
return '';
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1087
1107
|
function buildMcpRow(item) {
|
|
1088
1108
|
var info = MCP_STATUS_MAP[item.status] || { label: item.status, cls: 'badge-dim' };
|
|
1089
1109
|
var row = document.createElement('div');
|
|
@@ -1108,6 +1128,13 @@ function buildMcpRow(item) {
|
|
|
1108
1128
|
descDiv.textContent = item.configPath;
|
|
1109
1129
|
textWrap.appendChild(descDiv);
|
|
1110
1130
|
}
|
|
1131
|
+
var actionText = mcpActionText(item);
|
|
1132
|
+
if (actionText) {
|
|
1133
|
+
var actionDiv = document.createElement('div');
|
|
1134
|
+
actionDiv.className = 'integration-desc integration-action';
|
|
1135
|
+
actionDiv.textContent = actionText;
|
|
1136
|
+
textWrap.appendChild(actionDiv);
|
|
1137
|
+
}
|
|
1111
1138
|
|
|
1112
1139
|
infoDiv.appendChild(iconDiv);
|
|
1113
1140
|
infoDiv.appendChild(textWrap);
|
|
@@ -1123,25 +1150,38 @@ function buildMcpRow(item) {
|
|
|
1123
1150
|
|
|
1124
1151
|
async function loadMcpIntegrations() {
|
|
1125
1152
|
var container = document.getElementById('setup-mcp-integrations');
|
|
1153
|
+
var testBtn = document.getElementById('setup-mcp-test-btn');
|
|
1154
|
+
var fixBtn = document.getElementById('setup-mcp-fix-btn');
|
|
1126
1155
|
try {
|
|
1127
1156
|
var r = await fetch('/api/wall-e/mcp/integrations');
|
|
1128
1157
|
var d = await r.json();
|
|
1129
|
-
if (d.wallePort) {
|
|
1130
|
-
|
|
1158
|
+
if (d.wallePort) {
|
|
1159
|
+
_mcpPort = d.wallePort;
|
|
1160
|
+
['setup-mcp-port-display', 'setup-mcp-port-display-toml'].forEach(function(id) {
|
|
1161
|
+
var portEl = document.getElementById(id);
|
|
1162
|
+
if (portEl) portEl.textContent = d.wallePort;
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
if (!d.data || !d.data.length) {
|
|
1166
|
+
container.replaceChildren(mcpDimMsg('No AI tools detected.'));
|
|
1167
|
+
if (testBtn) testBtn.disabled = false;
|
|
1168
|
+
if (fixBtn) fixBtn.style.display = 'none';
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1131
1171
|
|
|
1132
|
-
var
|
|
1172
|
+
var needsFix = false;
|
|
1133
1173
|
var frag = document.createDocumentFragment();
|
|
1134
1174
|
for (var i = 0; i < d.data.length; i++) {
|
|
1135
1175
|
var item = d.data[i];
|
|
1136
|
-
if (item.status === '
|
|
1137
|
-
if (item.status === 'not_configured' || item.status === 'wrong_port') needsFix = true;
|
|
1176
|
+
if (item.status === 'not_configured' || item.status === 'wrong_port' || item.status === 'invalid_config') needsFix = true;
|
|
1138
1177
|
frag.appendChild(buildMcpRow(item));
|
|
1139
1178
|
}
|
|
1140
1179
|
container.replaceChildren(frag);
|
|
1141
|
-
|
|
1142
|
-
if (
|
|
1180
|
+
if (testBtn) testBtn.disabled = false;
|
|
1181
|
+
if (fixBtn) fixBtn.style.display = needsFix ? '' : 'none';
|
|
1143
1182
|
} catch {
|
|
1144
1183
|
container.replaceChildren(mcpDimMsg('Could not load integrations.'));
|
|
1184
|
+
if (testBtn) testBtn.disabled = false;
|
|
1145
1185
|
}
|
|
1146
1186
|
}
|
|
1147
1187
|
|
|
@@ -1151,17 +1191,15 @@ async function testMcpConnection() {
|
|
|
1151
1191
|
btn.disabled = true; btn.textContent = 'Testing\u2026';
|
|
1152
1192
|
resultEl.style.display = 'none';
|
|
1153
1193
|
try {
|
|
1154
|
-
var r = await fetch('
|
|
1155
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
1156
|
-
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'setup-wizard', version: '1.0' } } }),
|
|
1157
|
-
});
|
|
1194
|
+
var r = await fetch('/api/wall-e/mcp/test');
|
|
1158
1195
|
var d = await r.json();
|
|
1159
|
-
|
|
1196
|
+
var data = d.data || {};
|
|
1197
|
+
if (data.ok) {
|
|
1160
1198
|
resultEl.className = 'test-result ok'; resultEl.style.display = 'block';
|
|
1161
|
-
resultEl.textContent = '\u2713 MCP
|
|
1199
|
+
resultEl.textContent = '\u2713 Wall-E MCP endpoint is reachable with ' + (data.toolCount || 0) + ' tools.';
|
|
1162
1200
|
} else {
|
|
1163
1201
|
resultEl.className = 'test-result fail'; resultEl.style.display = 'block';
|
|
1164
|
-
resultEl.textContent = '\u2715
|
|
1202
|
+
resultEl.textContent = '\u2715 Wall-E MCP endpoint is not reachable on port ' + _mcpPort + ': ' + (data.error || 'unexpected response');
|
|
1165
1203
|
}
|
|
1166
1204
|
} catch (e) {
|
|
1167
1205
|
resultEl.className = 'test-result fail'; resultEl.style.display = 'block';
|
|
@@ -1177,13 +1215,18 @@ async function fixMcpConfigs() {
|
|
|
1177
1215
|
var r = await fetch('/api/wall-e/mcp/inject', { method: 'POST' });
|
|
1178
1216
|
var d = await r.json();
|
|
1179
1217
|
if (d.ok) {
|
|
1180
|
-
|
|
1218
|
+
var errors = (d.results || []).filter(function(x) { return x.action === 'error'; });
|
|
1219
|
+
if (errors.length) {
|
|
1220
|
+
setupToast('Some MCP configs need manual repair: ' + errors.map(function(x) { return x.tool; }).join(', '), 'error');
|
|
1221
|
+
} else {
|
|
1222
|
+
setupToast('MCP configs updated');
|
|
1223
|
+
}
|
|
1181
1224
|
loadMcpIntegrations();
|
|
1182
1225
|
} else {
|
|
1183
1226
|
setupToast(d.error || 'Fix failed', 'error');
|
|
1184
1227
|
}
|
|
1185
1228
|
} catch (e) { setupToast(e.message, 'error'); }
|
|
1186
|
-
btn.disabled = false; btn.textContent = '
|
|
1229
|
+
btn.disabled = false; btn.textContent = 'Repair Configs';
|
|
1187
1230
|
}
|
|
1188
1231
|
|
|
1189
1232
|
// ── Embeddings ──────────────────────────────────────────────────────
|
|
@@ -14,6 +14,8 @@ const _streamState = {
|
|
|
14
14
|
activeView: new Map(),
|
|
15
15
|
tooltipEl: null,
|
|
16
16
|
tooltipTimer: null,
|
|
17
|
+
_tooltipActiveSessionId: null,
|
|
18
|
+
_tooltipPendingSessionId: null,
|
|
17
19
|
_tooltipReqId: 0,
|
|
18
20
|
// Per-session priming bookkeeping.
|
|
19
21
|
// _primed: sessionId → true once _primeConversationView() has rendered
|
|
@@ -41,9 +43,13 @@ function initStreamTooltip() {
|
|
|
41
43
|
background: var(--surface-2, #1a1a2e); color: var(--fg, #e0e0e0);
|
|
42
44
|
border: 1px solid var(--border, #333); border-radius: 8px;
|
|
43
45
|
width: min(420px, calc(100vw - 24px)); max-height: min(520px, calc(100vh - 16px));
|
|
44
|
-
overflow: hidden; font-size: 12px; line-height: 1.45; pointer-events:
|
|
46
|
+
overflow: hidden; font-size: 12px; line-height: 1.45; pointer-events: auto;
|
|
47
|
+
user-select: text; cursor: text;
|
|
45
48
|
box-shadow: 0 10px 28px rgba(0,0,0,0.42), 0 0 0 1px rgba(255,255,255,0.02) inset;
|
|
46
49
|
`;
|
|
50
|
+
tooltip.tabIndex = -1;
|
|
51
|
+
tooltip.setAttribute('role', 'dialog');
|
|
52
|
+
tooltip.setAttribute('aria-label', 'Session summary');
|
|
47
53
|
document.body.appendChild(tooltip);
|
|
48
54
|
_streamState.tooltipEl = tooltip;
|
|
49
55
|
}
|
|
@@ -193,7 +199,7 @@ function _tooltipAppendProgress(container, progress) {
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
async function showStreamTooltip(sessionId, anchorEl) {
|
|
196
|
-
|
|
202
|
+
_clearPendingStreamTooltip(false);
|
|
197
203
|
const tooltip = _streamState.tooltipEl;
|
|
198
204
|
if (!tooltip) return;
|
|
199
205
|
|
|
@@ -273,6 +279,8 @@ async function showStreamTooltip(sessionId, anchorEl) {
|
|
|
273
279
|
|
|
274
280
|
tooltip.appendChild(content);
|
|
275
281
|
|
|
282
|
+
tooltip.dataset.sessionId = sessionId;
|
|
283
|
+
_streamState._tooltipActiveSessionId = sessionId;
|
|
276
284
|
tooltip.style.display = 'block';
|
|
277
285
|
|
|
278
286
|
// Position next to anchor (right of sidebar)
|
|
@@ -296,9 +304,13 @@ async function showStreamTooltip(sessionId, anchorEl) {
|
|
|
296
304
|
}
|
|
297
305
|
|
|
298
306
|
function hideStreamTooltip() {
|
|
299
|
-
|
|
307
|
+
_clearPendingStreamTooltip(false);
|
|
300
308
|
_streamState._tooltipReqId++; // Invalidate any in-flight fetch
|
|
301
|
-
|
|
309
|
+
_streamState._tooltipActiveSessionId = null;
|
|
310
|
+
if (_streamState.tooltipEl) {
|
|
311
|
+
_streamState.tooltipEl.style.display = 'none';
|
|
312
|
+
delete _streamState.tooltipEl.dataset.sessionId;
|
|
313
|
+
}
|
|
302
314
|
}
|
|
303
315
|
|
|
304
316
|
// --- Conversation View ---
|
|
@@ -442,6 +454,10 @@ function handleStreamMessage(msg) {
|
|
|
442
454
|
_primeConversationView(_domId, container);
|
|
443
455
|
}
|
|
444
456
|
} else if (msg.type === 'stream-event') {
|
|
457
|
+
const eventType = msg.data?.type || msg.role || msg.eventType || '';
|
|
458
|
+
if (eventType === 'user' && typeof window !== 'undefined' && typeof window._ctmRecordLivePromptPreview === 'function') {
|
|
459
|
+
window._ctmRecordLivePromptPreview(_domId, msg.data?.text || msg.text || '');
|
|
460
|
+
}
|
|
445
461
|
const container = document.querySelector(`.conversation-view[data-session-id="${CSS.escape(_domId)}"]`);
|
|
446
462
|
if (container) {
|
|
447
463
|
// If we haven't finished priming yet, queue the event and replay it later.
|
|
@@ -486,6 +502,7 @@ function applyStreamStatus(msg) {
|
|
|
486
502
|
if (msg.lastActivity && s.meta) {
|
|
487
503
|
s.meta.lastActivity = msg.lastActivity;
|
|
488
504
|
}
|
|
505
|
+
if (changed && typeof scheduleStandupRefresh === 'function') scheduleStandupRefresh();
|
|
489
506
|
return changed;
|
|
490
507
|
}
|
|
491
508
|
|
|
@@ -1054,17 +1071,38 @@ function _streamTooltipClosestSessionItem(target, root) {
|
|
|
1054
1071
|
return item;
|
|
1055
1072
|
}
|
|
1056
1073
|
|
|
1074
|
+
function _streamTooltipVisible() {
|
|
1075
|
+
const tooltip = _streamState.tooltipEl;
|
|
1076
|
+
return !!(tooltip && tooltip.style.display !== 'none');
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function _clearPendingStreamTooltip(invalidateRequest) {
|
|
1080
|
+
if (_streamState.tooltipTimer) {
|
|
1081
|
+
clearTimeout(_streamState.tooltipTimer);
|
|
1082
|
+
_streamState.tooltipTimer = null;
|
|
1083
|
+
}
|
|
1084
|
+
_streamState._tooltipPendingSessionId = null;
|
|
1085
|
+
if (invalidateRequest) _streamState._tooltipReqId++;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1057
1088
|
function _scheduleStreamTooltip(item) {
|
|
1058
1089
|
const id = item?.dataset?.sessionId;
|
|
1059
1090
|
if (!id) return;
|
|
1060
|
-
_streamState.
|
|
1091
|
+
if (_streamState._tooltipPendingSessionId === id) return;
|
|
1092
|
+
if (_streamState._tooltipActiveSessionId === id && _streamTooltipVisible()) return;
|
|
1093
|
+
_clearPendingStreamTooltip(true);
|
|
1094
|
+
_streamState._tooltipPendingSessionId = id;
|
|
1095
|
+
_streamState.tooltipTimer = setTimeout(() => {
|
|
1096
|
+
_streamState.tooltipTimer = null;
|
|
1097
|
+
_streamState._tooltipPendingSessionId = null;
|
|
1098
|
+
showStreamTooltip(id, item);
|
|
1099
|
+
}, 500);
|
|
1061
1100
|
}
|
|
1062
1101
|
|
|
1063
1102
|
function _handleStreamTooltipMouseOver(e, list) {
|
|
1064
1103
|
const item = _streamTooltipClosestSessionItem(e.target, list);
|
|
1065
1104
|
if (!item) return;
|
|
1066
1105
|
if (e.relatedTarget && _streamTooltipContains(item, e.relatedTarget)) return;
|
|
1067
|
-
hideStreamTooltip();
|
|
1068
1106
|
_scheduleStreamTooltip(item);
|
|
1069
1107
|
}
|
|
1070
1108
|
|
|
@@ -1072,6 +1110,14 @@ function _handleStreamTooltipMouseOut(e, list) {
|
|
|
1072
1110
|
const item = _streamTooltipClosestSessionItem(e.target, list);
|
|
1073
1111
|
if (!item) return;
|
|
1074
1112
|
if (e.relatedTarget && _streamTooltipContains(item, e.relatedTarget)) return;
|
|
1113
|
+
const id = item?.dataset?.sessionId;
|
|
1114
|
+
if (_streamState._tooltipPendingSessionId === id) _clearPendingStreamTooltip(true);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
function _handleStreamTooltipDocumentPointerDown(e) {
|
|
1118
|
+
const tooltip = _streamState.tooltipEl;
|
|
1119
|
+
if (!tooltip || !_streamTooltipVisible()) return;
|
|
1120
|
+
if (_streamTooltipContains(tooltip, e.target)) return;
|
|
1075
1121
|
hideStreamTooltip();
|
|
1076
1122
|
}
|
|
1077
1123
|
|
|
@@ -1083,6 +1129,7 @@ function bindStreamTooltips() {
|
|
|
1083
1129
|
|
|
1084
1130
|
list.addEventListener('mouseover', (e) => _handleStreamTooltipMouseOver(e, list));
|
|
1085
1131
|
list.addEventListener('mouseout', (e) => _handleStreamTooltipMouseOut(e, list));
|
|
1132
|
+
document.addEventListener('pointerdown', _handleStreamTooltipDocumentPointerDown, true);
|
|
1086
1133
|
}
|
|
1087
1134
|
|
|
1088
1135
|
// --- Init ---
|
|
@@ -1120,7 +1167,9 @@ if (typeof module !== 'undefined' && module.exports) {
|
|
|
1120
1167
|
bindStreamTooltips,
|
|
1121
1168
|
_handleStreamTooltipMouseOver,
|
|
1122
1169
|
_handleStreamTooltipMouseOut,
|
|
1170
|
+
_handleStreamTooltipDocumentPointerDown,
|
|
1123
1171
|
_streamTooltipClosestSessionItem,
|
|
1124
1172
|
_streamTooltipContains,
|
|
1173
|
+
_streamTooltipVisible,
|
|
1125
1174
|
};
|
|
1126
1175
|
}
|