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
|
@@ -4,23 +4,249 @@ const path = require('node:path');
|
|
|
4
4
|
const { v4: uuidv4 } = require('uuid');
|
|
5
5
|
const brain = require('./brain');
|
|
6
6
|
const { createSessionIngestService } = require('./memory/session-ingest-service');
|
|
7
|
+
const ctmSessionContext = require('./memory/ctm-session-context');
|
|
8
|
+
const ctmContextClient = require('./memory/ctm-context-client');
|
|
7
9
|
const { collectIngestRecords } = require('./sources/base');
|
|
8
10
|
const { ensureBuiltinSourceAdapters } = require('./sources/builtin');
|
|
9
11
|
const sourceRegistry = require('./sources/registry');
|
|
10
12
|
const { RECORD_TYPES } = require('./sources/record-types');
|
|
13
|
+
const { wallEAgentMemoryInstructions } = require('./lib/mcp-integration');
|
|
11
14
|
let _embeddings;
|
|
12
15
|
try { _embeddings = require('./embeddings'); } catch { _embeddings = null; }
|
|
13
16
|
|
|
14
17
|
const PROTOCOL_VERSION = '2025-03-26';
|
|
15
18
|
|
|
19
|
+
const MCP_RESOURCES = [
|
|
20
|
+
{
|
|
21
|
+
uri: 'walle://status/session-memory',
|
|
22
|
+
name: 'Wall-E session memory status',
|
|
23
|
+
description: 'Operational status for Wall-E brain memory and CTM cached session-context access.',
|
|
24
|
+
mimeType: 'application/json',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const MCP_RESOURCE_TEMPLATES = [
|
|
29
|
+
{
|
|
30
|
+
uriTemplate: 'walle-session://ctm/{session_id}/full',
|
|
31
|
+
name: 'CTM session full context',
|
|
32
|
+
description: 'Full cached CTM session context from session_conversations/session_messages. Use for explicit session transfer.',
|
|
33
|
+
mimeType: 'application/json',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
uriTemplate: 'walle-session://ctm/{session_id}/compact',
|
|
37
|
+
name: 'CTM session compact context',
|
|
38
|
+
description: 'Prompt-friendly markdown view of cached CTM session context.',
|
|
39
|
+
mimeType: 'text/markdown',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
uriTemplate: 'walle-context://task/{query}',
|
|
43
|
+
name: 'Task-scoped CTM context pack',
|
|
44
|
+
description: 'Search cached CTM sessions for a task prompt and return a deduplicated context pack.',
|
|
45
|
+
mimeType: 'text/markdown',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const MCP_PROMPTS = [
|
|
50
|
+
{
|
|
51
|
+
name: 'walle_memory_routing',
|
|
52
|
+
title: 'Wall-E Memory Routing Policy',
|
|
53
|
+
description: 'Instructions for when to use Wall-E MCP memory, CTM session context, live tools, or public web search.',
|
|
54
|
+
arguments: [
|
|
55
|
+
{ name: 'walle_port', description: 'Optional Wall-E port used in the instruction text', required: false },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'walle_coding_resume',
|
|
60
|
+
title: 'Resume With Wall-E Session Context',
|
|
61
|
+
description: 'Prompt template for resuming coding work using Wall-E CTM session memory and diary context.',
|
|
62
|
+
arguments: [
|
|
63
|
+
{ name: 'task', description: 'Current coding task or user request', required: false },
|
|
64
|
+
{ name: 'session_id', description: 'Optional CTM or agent session id to inspect first', required: false },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const ANY_OBJECT_SCHEMA = { type: 'object', additionalProperties: true };
|
|
70
|
+
|
|
71
|
+
const MEMORY_RESULT_SCHEMA = {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
id: { type: 'string' },
|
|
75
|
+
source: { type: 'string' },
|
|
76
|
+
source_id: { type: 'string' },
|
|
77
|
+
memory_type: { type: 'string' },
|
|
78
|
+
content: { type: 'string' },
|
|
79
|
+
timestamp: { type: 'string' },
|
|
80
|
+
},
|
|
81
|
+
additionalProperties: true,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ROUTE_OUTPUT_SCHEMA = {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
route: { type: 'string' },
|
|
88
|
+
should_use_walle: { type: 'boolean' },
|
|
89
|
+
primary_tools: { type: 'array', items: { type: 'string' } },
|
|
90
|
+
secondary_tools: { type: 'array', items: { type: 'string' } },
|
|
91
|
+
avoid_tools: { type: 'array', items: { type: 'string' } },
|
|
92
|
+
avoid_until_memory_miss: { type: 'array', items: { type: 'string' } },
|
|
93
|
+
reason: { type: 'string' },
|
|
94
|
+
matched: { type: 'array', items: ANY_OBJECT_SCHEMA },
|
|
95
|
+
},
|
|
96
|
+
required: ['route', 'should_use_walle', 'reason', 'matched'],
|
|
97
|
+
additionalProperties: true,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const SEARCH_MEMORIES_OUTPUT_SCHEMA = {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
results: { type: 'array', items: MEMORY_RESULT_SCHEMA },
|
|
104
|
+
filters_applied: ANY_OBJECT_SCHEMA,
|
|
105
|
+
vector_search_used: { type: 'boolean' },
|
|
106
|
+
},
|
|
107
|
+
required: ['results', 'filters_applied', 'vector_search_used'],
|
|
108
|
+
additionalProperties: true,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const LOOKUP_CONTEXT_OUTPUT_SCHEMA = {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
route: ROUTE_OUTPUT_SCHEMA,
|
|
115
|
+
query: { type: 'string' },
|
|
116
|
+
memory_results: { type: 'array', items: MEMORY_RESULT_SCHEMA },
|
|
117
|
+
session_results: { type: 'array', items: ANY_OBJECT_SCHEMA },
|
|
118
|
+
entity_results: { type: 'array', items: ANY_OBJECT_SCHEMA },
|
|
119
|
+
context_pack: { anyOf: [ANY_OBJECT_SCHEMA, { type: 'null' }] },
|
|
120
|
+
sources: ANY_OBJECT_SCHEMA,
|
|
121
|
+
},
|
|
122
|
+
required: ['route', 'query', 'sources'],
|
|
123
|
+
additionalProperties: true,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const TOOL_OUTPUT_SCHEMAS = {
|
|
127
|
+
walle_route_query: ROUTE_OUTPUT_SCHEMA,
|
|
128
|
+
walle_lookup_context: LOOKUP_CONTEXT_OUTPUT_SCHEMA,
|
|
129
|
+
search_memories: SEARCH_MEMORIES_OUTPUT_SCHEMA,
|
|
130
|
+
remember: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
stored: { type: 'boolean' },
|
|
134
|
+
id: { type: 'string' },
|
|
135
|
+
},
|
|
136
|
+
required: ['stored', 'id'],
|
|
137
|
+
additionalProperties: true,
|
|
138
|
+
},
|
|
139
|
+
walle_memory_status: ANY_OBJECT_SCHEMA,
|
|
140
|
+
walle_search_sessions: ANY_OBJECT_SCHEMA,
|
|
141
|
+
walle_get_session: ANY_OBJECT_SCHEMA,
|
|
142
|
+
walle_context_pack: ANY_OBJECT_SCHEMA,
|
|
143
|
+
walle_diary_write: ANY_OBJECT_SCHEMA,
|
|
144
|
+
walle_diary_read: ANY_OBJECT_SCHEMA,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const READ_ONLY_TOOL_ANNOTATION = Object.freeze({
|
|
148
|
+
readOnlyHint: true,
|
|
149
|
+
destructiveHint: false,
|
|
150
|
+
idempotentHint: true,
|
|
151
|
+
openWorldHint: false,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const ADDITIVE_WRITE_TOOL_ANNOTATION = Object.freeze({
|
|
155
|
+
readOnlyHint: false,
|
|
156
|
+
destructiveHint: false,
|
|
157
|
+
idempotentHint: false,
|
|
158
|
+
openWorldHint: false,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const IDEMPOTENT_WRITE_TOOL_ANNOTATION = Object.freeze({
|
|
162
|
+
readOnlyHint: false,
|
|
163
|
+
destructiveHint: false,
|
|
164
|
+
idempotentHint: true,
|
|
165
|
+
openWorldHint: false,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const TOOL_ANNOTATIONS = {
|
|
169
|
+
walle_route_query: READ_ONLY_TOOL_ANNOTATION,
|
|
170
|
+
walle_lookup_context: READ_ONLY_TOOL_ANNOTATION,
|
|
171
|
+
search_memories: READ_ONLY_TOOL_ANNOTATION,
|
|
172
|
+
remember: ADDITIVE_WRITE_TOOL_ANNOTATION,
|
|
173
|
+
list_knowledge: READ_ONLY_TOOL_ANNOTATION,
|
|
174
|
+
list_people: READ_ONLY_TOOL_ANNOTATION,
|
|
175
|
+
get_brief: READ_ONLY_TOOL_ANNOTATION,
|
|
176
|
+
ask_walle: {
|
|
177
|
+
readOnlyHint: false,
|
|
178
|
+
destructiveHint: false,
|
|
179
|
+
idempotentHint: false,
|
|
180
|
+
openWorldHint: true,
|
|
181
|
+
},
|
|
182
|
+
entity_search: READ_ONLY_TOOL_ANNOTATION,
|
|
183
|
+
entity_graph: READ_ONLY_TOOL_ANNOTATION,
|
|
184
|
+
knowledge_timeline: READ_ONLY_TOOL_ANNOTATION,
|
|
185
|
+
knowledge_query: READ_ONLY_TOOL_ANNOTATION,
|
|
186
|
+
dedup_scan: READ_ONLY_TOOL_ANNOTATION,
|
|
187
|
+
export_brain: READ_ONLY_TOOL_ANNOTATION,
|
|
188
|
+
brain_stats: READ_ONLY_TOOL_ANNOTATION,
|
|
189
|
+
walle_memory_status: READ_ONLY_TOOL_ANNOTATION,
|
|
190
|
+
walle_list_sources: READ_ONLY_TOOL_ANNOTATION,
|
|
191
|
+
walle_source_ingest: ADDITIVE_WRITE_TOOL_ANNOTATION,
|
|
192
|
+
walle_search_sessions: READ_ONLY_TOOL_ANNOTATION,
|
|
193
|
+
walle_get_session: READ_ONLY_TOOL_ANNOTATION,
|
|
194
|
+
walle_context_pack: READ_ONLY_TOOL_ANNOTATION,
|
|
195
|
+
walle_diary_write: IDEMPOTENT_WRITE_TOOL_ANNOTATION,
|
|
196
|
+
walle_diary_read: READ_ONLY_TOOL_ANNOTATION,
|
|
197
|
+
walle_reconnect: IDEMPOTENT_WRITE_TOOL_ANNOTATION,
|
|
198
|
+
walle_repair_status: READ_ONLY_TOOL_ANNOTATION,
|
|
199
|
+
walle_rebuild_ctm_message_index: IDEMPOTENT_WRITE_TOOL_ANNOTATION,
|
|
200
|
+
walle_rebuild_source_index: IDEMPOTENT_WRITE_TOOL_ANNOTATION,
|
|
201
|
+
};
|
|
202
|
+
|
|
16
203
|
const MCP_TOOLS = [
|
|
204
|
+
{
|
|
205
|
+
name: 'walle_route_query',
|
|
206
|
+
description: 'Classify whether a user request should use Wall-E memory/session tools, live source tools, or public web search. Use when unsure before choosing web/search for private, remembered, work, or prior-session context.',
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: {
|
|
210
|
+
query: { type: 'string', description: 'The user request or task to route' },
|
|
211
|
+
context: { type: 'string', description: 'Optional nearby conversation context' },
|
|
212
|
+
},
|
|
213
|
+
required: ['query'],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'walle_lookup_context',
|
|
218
|
+
description: 'One-stop Wall-E context lookup. Routes the request, then returns the most relevant private memory, entity, and CTM session context when Wall-E should be used. Use before public web search for ambiguous private/work-context requests.',
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {
|
|
222
|
+
query: { type: 'string', description: 'The user request or task to look up' },
|
|
223
|
+
context: { type: 'string', description: 'Optional nearby conversation context' },
|
|
224
|
+
limit: { type: 'number', description: 'Max memories/session snippets/entities per source (default 5, max 20)' },
|
|
225
|
+
include_memories: { type: 'boolean', description: 'Include Wall-E memory search results (default true when route needs Wall-E)' },
|
|
226
|
+
include_sessions: { type: 'boolean', description: 'Include CTM coding-session context (default true for session routes)' },
|
|
227
|
+
include_entities: { type: 'boolean', description: 'Include knowledge-graph entity matches (default true for people/work routes)' },
|
|
228
|
+
entity_name: { type: 'string', description: 'Optional explicit entity/person/project/tool name to look up' },
|
|
229
|
+
force_memory: { type: 'boolean', description: 'Search Wall-E memory even when routing says live/public/direct' },
|
|
230
|
+
prefer_db: { type: 'boolean', description: 'Prefer cached CTM DB for session context (default true)' },
|
|
231
|
+
source: { type: 'string', description: 'Optional memory source filter' },
|
|
232
|
+
memory_type: { type: 'string', description: 'Optional memory_type filter' },
|
|
233
|
+
since: { type: 'string', description: 'Optional inclusive ISO timestamp lower bound for memory results' },
|
|
234
|
+
until: { type: 'string', description: 'Optional inclusive ISO timestamp upper bound for memory results' },
|
|
235
|
+
},
|
|
236
|
+
required: ['query'],
|
|
237
|
+
},
|
|
238
|
+
},
|
|
17
239
|
{
|
|
18
240
|
name: 'search_memories',
|
|
19
|
-
description: "Search
|
|
241
|
+
description: "Search Wall-E's private/user memory across ingested Slack, email, calendar, files, and work context. Use before public web search for remembered context, prior discussions, decisions, preferences, people, projects, tools, or 'do you know' / 'what did we discuss' questions.",
|
|
20
242
|
inputSchema: {
|
|
21
243
|
type: 'object',
|
|
22
244
|
properties: {
|
|
23
245
|
query: { type: 'string', description: 'Search query (space-separated terms, all must match)' },
|
|
246
|
+
source: { type: 'string', description: 'Optional source filter, e.g. slack, email, calendar, ctm, walle-diary, codex-jsonl' },
|
|
247
|
+
memory_type: { type: 'string', description: 'Optional memory_type filter, e.g. message, email, agent_diary, coding_session_user_message' },
|
|
248
|
+
since: { type: 'string', description: 'Optional inclusive ISO timestamp lower bound' },
|
|
249
|
+
until: { type: 'string', description: 'Optional inclusive ISO timestamp upper bound' },
|
|
24
250
|
limit: { type: 'number', description: 'Max results to return (default 20, max 200)' },
|
|
25
251
|
},
|
|
26
252
|
required: ['query'],
|
|
@@ -72,7 +298,7 @@ const MCP_TOOLS = [
|
|
|
72
298
|
},
|
|
73
299
|
{
|
|
74
300
|
name: 'ask_walle',
|
|
75
|
-
description: 'Ask Wall-E a question
|
|
301
|
+
description: 'Ask Wall-E a question through the full memory-aware chat pipeline. Use for complex private/work-context questions that need reasoning across stored memories, knowledge, people, projects, and conversation history.',
|
|
76
302
|
inputSchema: {
|
|
77
303
|
type: 'object',
|
|
78
304
|
properties: {
|
|
@@ -83,7 +309,7 @@ const MCP_TOOLS = [
|
|
|
83
309
|
},
|
|
84
310
|
{
|
|
85
311
|
name: 'entity_search',
|
|
86
|
-
description: 'Search for entities (people, projects, tools, orgs) in Wall-E\'s knowledge graph by name or type.',
|
|
312
|
+
description: 'Search for entities (people, projects, tools, orgs, concepts) in Wall-E\'s private knowledge graph by name or type. Use for user-context lookups before public web search.',
|
|
87
313
|
inputSchema: {
|
|
88
314
|
type: 'object',
|
|
89
315
|
properties: {
|
|
@@ -181,7 +407,7 @@ const MCP_TOOLS = [
|
|
|
181
407
|
},
|
|
182
408
|
{
|
|
183
409
|
name: 'walle_source_ingest',
|
|
184
|
-
description: 'Ingest a source file through a Wall-E source adapter. Use dry_run first when inspecting an unfamiliar source.',
|
|
410
|
+
description: 'Ingest a source file through a Wall-E source adapter. Use dry_run first when inspecting an unfamiliar source; set confirm_write=true for non-dry-run ingestion.',
|
|
185
411
|
inputSchema: {
|
|
186
412
|
type: 'object',
|
|
187
413
|
properties: {
|
|
@@ -192,6 +418,7 @@ const MCP_TOOLS = [
|
|
|
192
418
|
privacy_class: { type: 'string', description: 'Optional source privacy class' },
|
|
193
419
|
privacy_floor: { type: 'string', description: 'Optional maximum privacy class allowed to write' },
|
|
194
420
|
dry_run: { type: 'boolean', description: 'Parse and validate without writing memories' },
|
|
421
|
+
confirm_write: { type: 'boolean', description: 'Required when dry_run is false because ingestion writes Wall-E memories' },
|
|
195
422
|
metadata: { type: 'object', description: 'Optional adapter metadata' },
|
|
196
423
|
},
|
|
197
424
|
required: ['adapter_id', 'uri'],
|
|
@@ -199,30 +426,54 @@ const MCP_TOOLS = [
|
|
|
199
426
|
},
|
|
200
427
|
{
|
|
201
428
|
name: 'walle_search_sessions',
|
|
202
|
-
description: 'Search
|
|
429
|
+
description: 'Search coding-session history. Tries cached CTM DB session messages first, then ingested Wall-E memories/diary entries. Returns deduplicated attributed snippets plus session pointers.',
|
|
203
430
|
inputSchema: {
|
|
204
431
|
type: 'object',
|
|
205
432
|
properties: {
|
|
206
433
|
query: { type: 'string', description: 'Search query' },
|
|
207
434
|
limit: { type: 'number', description: 'Max session snippets (default 10, max 50)' },
|
|
435
|
+
prefer_db: { type: 'boolean', description: 'Try cached CTM DB tables before Wall-E memory (default true)' },
|
|
436
|
+
include_memory: { type: 'boolean', description: 'Also search Wall-E ingested memories and diary entries (default true)' },
|
|
208
437
|
},
|
|
209
438
|
required: ['query'],
|
|
210
439
|
},
|
|
211
440
|
},
|
|
212
441
|
{
|
|
213
442
|
name: 'walle_get_session',
|
|
214
|
-
description: 'Retrieve session
|
|
443
|
+
description: 'Retrieve cached CTM session context by session id/source id, or parse a JSONL session directly when adapter_id and uri are provided. DB tables are preferred for full context transfer.',
|
|
215
444
|
inputSchema: {
|
|
216
445
|
type: 'object',
|
|
217
446
|
properties: {
|
|
447
|
+
session_id: { type: 'string', description: 'CTM session id or agent session id to retrieve from cached DB tables' },
|
|
448
|
+
session_ids: { type: 'array', items: { type: 'string' }, description: 'Several CTM/agent session ids to retrieve together' },
|
|
218
449
|
source_id: { type: 'string', description: 'Stable source/session id to retrieve from memory' },
|
|
219
450
|
adapter_id: { type: 'string', description: 'Optional adapter id for direct JSONL parse' },
|
|
220
451
|
uri: { type: 'string', description: 'Optional JSONL path for direct parse' },
|
|
221
|
-
limit: { type: 'number', description: 'Max records (default 50,
|
|
452
|
+
limit: { type: 'number', description: 'Max records/messages (default 50 for memory, 200 for CTM DB; use 0/full/all for all cached DB messages)' },
|
|
453
|
+
cursor: { type: 'number', description: 'Message offset for cached DB pagination' },
|
|
454
|
+
format: { type: 'string', description: 'Cached DB output format: messages, markdown, or compact' },
|
|
455
|
+
dedupe: { type: 'boolean', description: 'Deduplicate repeated message content (default true)' },
|
|
456
|
+
prefer_db: { type: 'boolean', description: 'Use cached CTM DB tables before Wall-E memory fallback (default true)' },
|
|
222
457
|
include_raw: { type: 'boolean', description: 'Include raw content when available' },
|
|
223
458
|
},
|
|
224
459
|
},
|
|
225
460
|
},
|
|
461
|
+
{
|
|
462
|
+
name: 'walle_context_pack',
|
|
463
|
+
description: 'Build a deduplicated task-scoped context pack from cached CTM session history. Use before answering or starting a coding task when the user references prior attempts, old bugs, branches, decisions, restarts, or past agent work.',
|
|
464
|
+
inputSchema: {
|
|
465
|
+
type: 'object',
|
|
466
|
+
properties: {
|
|
467
|
+
task: { type: 'string', description: 'Current user prompt or task description' },
|
|
468
|
+
query: { type: 'string', description: 'Optional explicit search query; defaults to task' },
|
|
469
|
+
session_ids: { type: 'array', items: { type: 'string' }, description: 'Optional CTM/agent session ids to force include' },
|
|
470
|
+
limit: { type: 'number', description: 'Max sessions to include (default 5, max 50)' },
|
|
471
|
+
token_budget: { type: 'number', description: 'Approximate context-pack token budget (default 12000)' },
|
|
472
|
+
mode: { type: 'string', description: 'auto, compact, or full' },
|
|
473
|
+
include_raw: { type: 'boolean', description: 'Include raw cached messages when mode/full transfer needs exact source payloads' },
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
},
|
|
226
477
|
{
|
|
227
478
|
name: 'walle_diary_write',
|
|
228
479
|
description: 'Write an idempotent agent diary entry for a coding session stop, handoff, or compaction boundary.',
|
|
@@ -272,26 +523,72 @@ const MCP_TOOLS = [
|
|
|
272
523
|
properties: {},
|
|
273
524
|
},
|
|
274
525
|
},
|
|
526
|
+
{
|
|
527
|
+
name: 'walle_rebuild_ctm_message_index',
|
|
528
|
+
description: 'Ask the CTM API to backfill session_messages and FTS rows from cached session_conversations. Use dry_run first to inspect planned work; set confirm_write=true for writes.',
|
|
529
|
+
inputSchema: {
|
|
530
|
+
type: 'object',
|
|
531
|
+
properties: {
|
|
532
|
+
limit: { type: 'number', description: 'Max session_conversations rows to repair (default 100, max 1000)' },
|
|
533
|
+
dry_run: { type: 'boolean', description: 'Plan the repair without writing (default false)' },
|
|
534
|
+
confirm_write: { type: 'boolean', description: 'Required when dry_run is false because rebuild writes CTM index rows' },
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
},
|
|
275
538
|
{
|
|
276
539
|
name: 'walle_rebuild_source_index',
|
|
277
|
-
description: 'Rebuild the memory/source index from stored memories. Use dry_run to inspect expected work without writing.',
|
|
540
|
+
description: 'Rebuild the memory/source index from stored memories. Use dry_run to inspect expected work without writing; set confirm_write=true for writes.',
|
|
278
541
|
inputSchema: {
|
|
279
542
|
type: 'object',
|
|
280
543
|
properties: {
|
|
281
544
|
source_id: { type: 'string', description: 'Optional stable source/session id to rebuild' },
|
|
282
545
|
dry_run: { type: 'boolean', description: 'Return current index status without rebuilding' },
|
|
546
|
+
confirm_write: { type: 'boolean', description: 'Required when dry_run is false because rebuild writes Wall-E source-index rows' },
|
|
283
547
|
},
|
|
284
548
|
},
|
|
285
549
|
},
|
|
286
550
|
];
|
|
287
551
|
|
|
552
|
+
function toolTitle(name) {
|
|
553
|
+
return String(name || '')
|
|
554
|
+
.split('_')
|
|
555
|
+
.filter(Boolean)
|
|
556
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
557
|
+
.join(' ');
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function applyMcpToolMetadata(tools) {
|
|
561
|
+
for (const tool of tools) {
|
|
562
|
+
const annotations = TOOL_ANNOTATIONS[tool.name] || READ_ONLY_TOOL_ANNOTATION;
|
|
563
|
+
tool.annotations = { title: toolTitle(tool.name), ...annotations };
|
|
564
|
+
if (TOOL_OUTPUT_SCHEMAS[tool.name]) {
|
|
565
|
+
tool.outputSchema = TOOL_OUTPUT_SCHEMAS[tool.name];
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
applyMcpToolMetadata(MCP_TOOLS);
|
|
571
|
+
|
|
288
572
|
async function executeTool(name, args) {
|
|
289
573
|
switch (name) {
|
|
574
|
+
case 'walle_route_query': {
|
|
575
|
+
return routeWallEQuery(args.query, { context: args.context });
|
|
576
|
+
}
|
|
577
|
+
case 'walle_lookup_context': {
|
|
578
|
+
return lookupWallEContext(args);
|
|
579
|
+
}
|
|
290
580
|
case 'search_memories': {
|
|
291
581
|
const limit = args.limit || 20;
|
|
292
|
-
|
|
582
|
+
const filters = {
|
|
583
|
+
source: args.source,
|
|
584
|
+
memory_type: args.memory_type,
|
|
585
|
+
since: args.since,
|
|
586
|
+
until: args.until,
|
|
587
|
+
};
|
|
588
|
+
const hasFilters = Object.values(filters).some(Boolean);
|
|
589
|
+
let results = brain.searchMemories({ query: args.query, limit, ...filters });
|
|
293
590
|
// Hybrid: merge with vector results if embeddings available
|
|
294
|
-
if (_embeddings && _embeddings.isAvailable()) {
|
|
591
|
+
if (!hasFilters && _embeddings && _embeddings.isAvailable()) {
|
|
295
592
|
try {
|
|
296
593
|
const queryEmb = await _embeddings.computeEmbedding(args.query);
|
|
297
594
|
if (queryEmb) {
|
|
@@ -302,7 +599,11 @@ async function executeTool(name, args) {
|
|
|
302
599
|
}
|
|
303
600
|
} catch {}
|
|
304
601
|
}
|
|
305
|
-
return {
|
|
602
|
+
return {
|
|
603
|
+
results: results.map(formatMemoryResult),
|
|
604
|
+
filters_applied: Object.fromEntries(Object.entries(filters).filter(([, value]) => Boolean(value))),
|
|
605
|
+
vector_search_used: Boolean(!hasFilters && _embeddings && _embeddings.isAvailable()),
|
|
606
|
+
};
|
|
306
607
|
}
|
|
307
608
|
case 'remember': {
|
|
308
609
|
const result = brain.insertKnowledge({
|
|
@@ -412,6 +713,11 @@ async function executeTool(name, args) {
|
|
|
412
713
|
return {
|
|
413
714
|
ok: true,
|
|
414
715
|
stats,
|
|
716
|
+
ctm_db: await getCtmDbStatus(),
|
|
717
|
+
resources: {
|
|
718
|
+
static: MCP_RESOURCES.map((resource) => resource.uri),
|
|
719
|
+
templates: MCP_RESOURCE_TEMPLATES.map((template) => template.uriTemplate),
|
|
720
|
+
},
|
|
415
721
|
source_adapters: sourceRegistry.list().map(formatSourceAdapter),
|
|
416
722
|
diary_count: countDiaryEntries(),
|
|
417
723
|
tools: MCP_TOOLS.filter((tool) => tool.name.startsWith('walle_')).map((tool) => tool.name),
|
|
@@ -428,6 +734,7 @@ async function executeTool(name, args) {
|
|
|
428
734
|
}
|
|
429
735
|
case 'walle_source_ingest': {
|
|
430
736
|
ensureBuiltinSourceAdapters();
|
|
737
|
+
requireWriteConfirmation('walle_source_ingest', args);
|
|
431
738
|
assertJsonlPath(args.uri);
|
|
432
739
|
const sourceRef = {
|
|
433
740
|
adapterId: args.adapter_id,
|
|
@@ -446,11 +753,52 @@ async function executeTool(name, args) {
|
|
|
446
753
|
}
|
|
447
754
|
case 'walle_search_sessions': {
|
|
448
755
|
const limit = clampLimit(args.limit, 10, 50);
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
.
|
|
452
|
-
|
|
453
|
-
|
|
756
|
+
const ctmResults = args.prefer_db === false
|
|
757
|
+
? { ok: false, results: [], skipped: true, reason: 'prefer_db_false' }
|
|
758
|
+
: await safeCtmSearchSessions({ query: args.query, limit });
|
|
759
|
+
const memoryResults = args.include_memory === false
|
|
760
|
+
? []
|
|
761
|
+
: brain.searchMemories({ query: args.query, limit: limit * 4 })
|
|
762
|
+
.filter(isSessionMemory)
|
|
763
|
+
.slice(0, limit * 2)
|
|
764
|
+
.map((memory, index) => ({
|
|
765
|
+
...formatSessionMemory(memory),
|
|
766
|
+
source_layer: 'wall-e-memory',
|
|
767
|
+
rank: 10000 + index,
|
|
768
|
+
}));
|
|
769
|
+
const ctmSourceLayer = ctmResults.source === 'ctm-api' ? 'ctm-api' : 'ctm-db';
|
|
770
|
+
const dbResults = (ctmResults.results || []).map((result, index) => ({
|
|
771
|
+
...result,
|
|
772
|
+
source_layer: ctmSourceLayer,
|
|
773
|
+
rank: Number.isFinite(result.rank) ? result.rank : index,
|
|
774
|
+
}));
|
|
775
|
+
const results = ctmSessionContext.dedupeItems([...dbResults, ...memoryResults], {
|
|
776
|
+
limit,
|
|
777
|
+
contentFields: ['snippet', 'content'],
|
|
778
|
+
keyPrefix: (item) => `${item.session_id || item.source_id || ''}:${item.role || item.memory_type || ''}`,
|
|
779
|
+
}).map(stripRank);
|
|
780
|
+
return {
|
|
781
|
+
results,
|
|
782
|
+
sources: {
|
|
783
|
+
ctm_db: {
|
|
784
|
+
ok: Boolean(ctmResults.ok),
|
|
785
|
+
unavailable: Boolean(ctmResults.unavailable),
|
|
786
|
+
source: ctmResults.source || '',
|
|
787
|
+
backend_source: ctmResults.backend_source || '',
|
|
788
|
+
api_transport: ctmResults.api_transport || null,
|
|
789
|
+
db_path: ctmResults.db_path || '',
|
|
790
|
+
active_source: ctmResults.active_source || '',
|
|
791
|
+
fallback_used: Boolean(ctmResults.fallback_used),
|
|
792
|
+
reason: ctmResults.reason || '',
|
|
793
|
+
count: (ctmResults.results || []).length,
|
|
794
|
+
},
|
|
795
|
+
wall_e_memory: {
|
|
796
|
+
searched: args.include_memory !== false,
|
|
797
|
+
count: memoryResults.length,
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
deduped: true,
|
|
801
|
+
};
|
|
454
802
|
}
|
|
455
803
|
case 'walle_get_session': {
|
|
456
804
|
const limit = clampLimit(args.limit, 50, 200);
|
|
@@ -475,12 +823,47 @@ async function executeTool(name, args) {
|
|
|
475
823
|
.map((record) => formatSourceRecord(record, { includeRaw: Boolean(args.include_raw) })),
|
|
476
824
|
};
|
|
477
825
|
}
|
|
826
|
+
const wantsDb = args.prefer_db !== false;
|
|
827
|
+
const dbSessionIds = args.session_ids || args.session_id || args.source_id;
|
|
828
|
+
if (wantsDb && dbSessionIds) {
|
|
829
|
+
const dbContext = await safeCtmSessionContext({
|
|
830
|
+
session_ids: args.session_ids,
|
|
831
|
+
session_id: args.session_id,
|
|
832
|
+
source_id: args.source_id,
|
|
833
|
+
limit: args.limit ?? 200,
|
|
834
|
+
cursor: args.cursor || 0,
|
|
835
|
+
include_raw: Boolean(args.include_raw),
|
|
836
|
+
dedupe: args.dedupe !== false,
|
|
837
|
+
format: args.format || 'messages',
|
|
838
|
+
});
|
|
839
|
+
if (dbContext.ok && (dbContext.sessions.length > 0 || dbContext.messages.length > 0)) {
|
|
840
|
+
return dbContext;
|
|
841
|
+
}
|
|
842
|
+
if (args.session_id || args.session_ids) return dbContext;
|
|
843
|
+
}
|
|
478
844
|
if (!args.source_id) throw new Error('source_id is required when adapter_id and uri are not provided');
|
|
479
845
|
return {
|
|
480
846
|
source_id: args.source_id,
|
|
847
|
+
transfer: {
|
|
848
|
+
full_context_supported: false,
|
|
849
|
+
served_from: 'wall-e-memory',
|
|
850
|
+
jsonl_fallback_used: false,
|
|
851
|
+
ctm_db_preferred: wantsDb,
|
|
852
|
+
},
|
|
481
853
|
records: getSessionMemories(args.source_id, { limit }).map(formatSessionMemory),
|
|
482
854
|
};
|
|
483
855
|
}
|
|
856
|
+
case 'walle_context_pack': {
|
|
857
|
+
return await safeCtmContextPack({
|
|
858
|
+
task: args.task || '',
|
|
859
|
+
query: args.query || '',
|
|
860
|
+
session_ids: args.session_ids,
|
|
861
|
+
limit: args.limit || 5,
|
|
862
|
+
token_budget: args.token_budget || 12000,
|
|
863
|
+
mode: args.mode || 'auto',
|
|
864
|
+
include_raw: Boolean(args.include_raw),
|
|
865
|
+
});
|
|
866
|
+
}
|
|
484
867
|
case 'walle_diary_write': {
|
|
485
868
|
return writeDiaryEntry(args);
|
|
486
869
|
}
|
|
@@ -495,7 +878,15 @@ async function executeTool(name, args) {
|
|
|
495
878
|
const repair = require('./utils/repair');
|
|
496
879
|
return repair.scanIntegrity();
|
|
497
880
|
}
|
|
881
|
+
case 'walle_rebuild_ctm_message_index': {
|
|
882
|
+
requireWriteConfirmation('walle_rebuild_ctm_message_index', args);
|
|
883
|
+
return ctmContextClient.backfillMessageIndex({
|
|
884
|
+
limit: args.limit || 100,
|
|
885
|
+
dry_run: Boolean(args.dry_run),
|
|
886
|
+
});
|
|
887
|
+
}
|
|
498
888
|
case 'walle_rebuild_source_index': {
|
|
889
|
+
requireWriteConfirmation('walle_rebuild_source_index', args);
|
|
499
890
|
const { rebuildSourceIndex } = require('./memory/source-indexer');
|
|
500
891
|
return rebuildSourceIndex({
|
|
501
892
|
brain,
|
|
@@ -508,12 +899,281 @@ async function executeTool(name, args) {
|
|
|
508
899
|
}
|
|
509
900
|
}
|
|
510
901
|
|
|
902
|
+
function routeWallEQuery(query, opts = {}) {
|
|
903
|
+
const text = `${query || ''}\n${opts.context || ''}`.toLowerCase();
|
|
904
|
+
const has = (patterns) => patterns.some(pattern => pattern.test(text));
|
|
905
|
+
const matched = [];
|
|
906
|
+
const add = (name, patterns) => {
|
|
907
|
+
const hits = patterns.filter(pattern => pattern.test(text)).map(pattern => pattern.source);
|
|
908
|
+
if (hits.length) matched.push({ name, hits });
|
|
909
|
+
return hits.length > 0;
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
const explicitWalle = add('explicit_wall_e', [/\bwall-?e\b/, /\bwalle\b/, /\bmcp\b/, /\bmemory\b/, /\bctm\b/]);
|
|
913
|
+
const priorContext = add('prior_context', [/\blast time\b/, /\bprevious(ly)?\b/, /\bremember\b/, /\bwhat did we (discuss|decide)\b/, /\bwhat happened\b/, /\bfollow.?up\b/, /\bcontext\b/]);
|
|
914
|
+
const sessionContext = add('session_context', [/\bsession\b/, /\bagent\b/, /\bbranch\b/, /\brestart\b/, /\bresume\b/, /\bhandoff\b/, /\bdiary\b/, /\bbug\b/, /\bregression\b/, /\btitle\b/, /\bcommit\b/]);
|
|
915
|
+
const workMemory = add('work_memory', [/\bslack\b/, /\bemail\b/, /\bcalendar\b/, /\bcolleague\b/, /\bmanager\b/, /\bteam\b/, /\bproject\b/, /\bdoc(ument)?\b/, /\btool\b/, /\bdecision\b/, /\bpreference\b/]);
|
|
916
|
+
const personLookup = add('person_lookup', [/\bwho is\b/, /\bdo you know\b/, /\bwhat is .* role\b/, /\breports? to\b/]);
|
|
917
|
+
const liveAction = add('live_action', [/\bsend\b/, /\bpost\b/, /\bcreate\b/, /\bschedule\b/, /\bopen\b/, /\bdelete\b/, /\bupdate\b/, /\bunread\b/, /\binbox\b/, /\btoday'?s calendar\b/, /\btomorrow'?s calendar\b/]);
|
|
918
|
+
const publicCurrent = add('public_current', [/\bweb\b/, /\bpublic\b/, /\bnews\b/, /\blatest\b/, /\bcurrent\b/, /\bprice\b/, /\bstock\b/, /\bweather\b/, /\bdocs?\b/, /\bofficial\b/]);
|
|
919
|
+
|
|
920
|
+
if (liveAction && !priorContext && !explicitWalle) {
|
|
921
|
+
return {
|
|
922
|
+
route: 'live_source_or_action',
|
|
923
|
+
should_use_walle: false,
|
|
924
|
+
primary_tools: ['calendar/email/slack/action tools exposed by the host'],
|
|
925
|
+
secondary_tools: ['search_memories only if the user asks for remembered context'],
|
|
926
|
+
avoid_tools: ['walle_context_pack', 'walle_search_sessions'],
|
|
927
|
+
reason: 'The request appears to need live source data or a side-effecting action rather than stored memory.',
|
|
928
|
+
matched,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (sessionContext || (explicitWalle && priorContext)) {
|
|
933
|
+
return {
|
|
934
|
+
route: 'walle_session_context',
|
|
935
|
+
should_use_walle: true,
|
|
936
|
+
primary_tools: ['walle_context_pack', 'walle_search_sessions', 'walle_get_session'],
|
|
937
|
+
secondary_tools: ['walle_diary_read', 'search_memories'],
|
|
938
|
+
avoid_until_memory_miss: ['web_search', 'web_fetch'],
|
|
939
|
+
reason: 'The request appears to depend on prior coding-agent, CTM, branch, bug, restart, or handoff context.',
|
|
940
|
+
matched,
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (priorContext || workMemory || personLookup || explicitWalle) {
|
|
945
|
+
return {
|
|
946
|
+
route: 'walle_private_memory',
|
|
947
|
+
should_use_walle: true,
|
|
948
|
+
primary_tools: personLookup ? ['entity_search', 'search_memories', 'ask_walle'] : ['search_memories', 'knowledge_query', 'ask_walle'],
|
|
949
|
+
secondary_tools: ['entity_graph', 'knowledge_timeline'],
|
|
950
|
+
avoid_until_memory_miss: ['web_search', 'web_fetch'],
|
|
951
|
+
reason: 'The request appears to depend on private, remembered, or work-context data Wall-E owns.',
|
|
952
|
+
matched,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (publicCurrent) {
|
|
957
|
+
return {
|
|
958
|
+
route: 'public_or_current_fact',
|
|
959
|
+
should_use_walle: false,
|
|
960
|
+
primary_tools: ['web_search', 'web_fetch', 'official docs or public data tools'],
|
|
961
|
+
secondary_tools: ['search_memories only if the user asks for personal/work context'],
|
|
962
|
+
avoid_tools: ['walle_context_pack'],
|
|
963
|
+
reason: 'The request appears to ask for public/current information rather than private memory.',
|
|
964
|
+
matched,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
return {
|
|
969
|
+
route: 'answer_directly_or_clarify',
|
|
970
|
+
should_use_walle: false,
|
|
971
|
+
primary_tools: [],
|
|
972
|
+
secondary_tools: ['walle_route_query again with more context if uncertain'],
|
|
973
|
+
reason: 'No strong signal that Wall-E memory, live tools, or public web are required.',
|
|
974
|
+
matched,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
async function lookupWallEContext(args = {}) {
|
|
979
|
+
const query = String(args.query || '').trim();
|
|
980
|
+
if (!query) throw new Error('query is required');
|
|
981
|
+
const route = routeWallEQuery(query, { context: args.context });
|
|
982
|
+
const limit = clampLimit(args.limit, 5, 20);
|
|
983
|
+
const shouldUseWallE = Boolean(route.should_use_walle || args.force_memory);
|
|
984
|
+
const includeMemories = args.include_memories === true || (args.include_memories !== false && shouldUseWallE);
|
|
985
|
+
const includeSessions = args.include_sessions === true
|
|
986
|
+
|| (args.include_sessions !== false && route.route === 'walle_session_context');
|
|
987
|
+
const includeEntities = args.include_entities === true
|
|
988
|
+
|| (args.include_entities !== false && (route.route === 'walle_private_memory' || args.force_memory));
|
|
989
|
+
const inferredEntityName = String(args.entity_name || inferEntityName(query) || '').trim();
|
|
990
|
+
const memoryQuery = inferredEntityName || query;
|
|
991
|
+
const result = {
|
|
992
|
+
route,
|
|
993
|
+
query,
|
|
994
|
+
memory_results: [],
|
|
995
|
+
session_results: [],
|
|
996
|
+
entity_results: [],
|
|
997
|
+
context_pack: null,
|
|
998
|
+
sources: {
|
|
999
|
+
memories: { searched: false, count: 0 },
|
|
1000
|
+
ctm_sessions: { searched: false, count: 0 },
|
|
1001
|
+
entities: { searched: false, count: 0 },
|
|
1002
|
+
},
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
if (includeSessions) {
|
|
1006
|
+
const sessionSearch = await safeCtmSearchSessions({
|
|
1007
|
+
query,
|
|
1008
|
+
limit,
|
|
1009
|
+
prefer_db: args.prefer_db !== false,
|
|
1010
|
+
});
|
|
1011
|
+
result.session_results = (sessionSearch.results || []).slice(0, limit).map(stripRank);
|
|
1012
|
+
result.sources.ctm_sessions = {
|
|
1013
|
+
searched: true,
|
|
1014
|
+
ok: Boolean(sessionSearch.ok),
|
|
1015
|
+
source: sessionSearch.source || '',
|
|
1016
|
+
reason: sessionSearch.reason || '',
|
|
1017
|
+
count: result.session_results.length,
|
|
1018
|
+
};
|
|
1019
|
+
result.context_pack = await safeCtmContextPack({
|
|
1020
|
+
task: query,
|
|
1021
|
+
query,
|
|
1022
|
+
limit: Math.min(limit, 5),
|
|
1023
|
+
token_budget: 6000,
|
|
1024
|
+
mode: 'compact',
|
|
1025
|
+
include_raw: false,
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (includeMemories) {
|
|
1030
|
+
const filters = {
|
|
1031
|
+
source: args.source,
|
|
1032
|
+
memory_type: args.memory_type,
|
|
1033
|
+
since: args.since,
|
|
1034
|
+
until: args.until,
|
|
1035
|
+
};
|
|
1036
|
+
result.memory_results = brain.searchMemories({ query: memoryQuery, limit, ...filters }).map(formatMemoryResult);
|
|
1037
|
+
result.sources.memories = {
|
|
1038
|
+
searched: true,
|
|
1039
|
+
query: memoryQuery,
|
|
1040
|
+
filters_applied: Object.fromEntries(Object.entries(filters).filter(([, value]) => Boolean(value))),
|
|
1041
|
+
count: result.memory_results.length,
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (includeEntities) {
|
|
1046
|
+
if (inferredEntityName) {
|
|
1047
|
+
result.entity_results = searchEntitiesForContext(inferredEntityName, limit);
|
|
1048
|
+
result.sources.entities = {
|
|
1049
|
+
searched: true,
|
|
1050
|
+
query: inferredEntityName,
|
|
1051
|
+
count: result.entity_results.length,
|
|
1052
|
+
};
|
|
1053
|
+
} else {
|
|
1054
|
+
result.sources.entities = {
|
|
1055
|
+
searched: false,
|
|
1056
|
+
reason: 'no_entity_name_inferred',
|
|
1057
|
+
count: 0,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
return result;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
function inferEntityName(query) {
|
|
1066
|
+
const text = String(query || '').trim();
|
|
1067
|
+
for (const pattern of [
|
|
1068
|
+
/\bdo you know\s+(.+?)[?.!]?$/i,
|
|
1069
|
+
/\bwho is\s+(.+?)[?.!]?$/i,
|
|
1070
|
+
/\bwhat is\s+(.+?)'s role[?.!]?$/i,
|
|
1071
|
+
/\bwhat is the role of\s+(.+?)[?.!]?$/i,
|
|
1072
|
+
]) {
|
|
1073
|
+
const match = text.match(pattern);
|
|
1074
|
+
if (match?.[1]) {
|
|
1075
|
+
return match[1].replace(/^["']|["']$/g, '').trim();
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return '';
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function searchEntitiesForContext(name, limit) {
|
|
1082
|
+
const results = [];
|
|
1083
|
+
const exact = brain.findEntity(name);
|
|
1084
|
+
if (exact) results.push(exact);
|
|
1085
|
+
if (results.length === 0) {
|
|
1086
|
+
const fuzzy = brain.findEntityFuzzy(name);
|
|
1087
|
+
if (fuzzy) results.push(fuzzy);
|
|
1088
|
+
}
|
|
1089
|
+
if (results.length === 0) {
|
|
1090
|
+
const lower = String(name || '').toLowerCase();
|
|
1091
|
+
results.push(...brain.listEntities({ limit }).filter(entity =>
|
|
1092
|
+
String(entity.canonical_name || '').toLowerCase().includes(lower)
|
|
1093
|
+
));
|
|
1094
|
+
}
|
|
1095
|
+
return results.slice(0, limit).map(entity => ({
|
|
1096
|
+
id: entity.id,
|
|
1097
|
+
name: entity.canonical_name,
|
|
1098
|
+
type: entity.entity_type,
|
|
1099
|
+
aliases: entity.aliases,
|
|
1100
|
+
}));
|
|
1101
|
+
}
|
|
1102
|
+
|
|
511
1103
|
function clampLimit(value, fallback, max) {
|
|
512
1104
|
const n = Number(value || fallback);
|
|
513
1105
|
if (!Number.isFinite(n)) return fallback;
|
|
514
1106
|
return Math.min(Math.max(Math.trunc(n), 1), max);
|
|
515
1107
|
}
|
|
516
1108
|
|
|
1109
|
+
function formatMemoryResult(memory) {
|
|
1110
|
+
return {
|
|
1111
|
+
id: memory.id,
|
|
1112
|
+
source: memory.source,
|
|
1113
|
+
source_id: memory.source_id,
|
|
1114
|
+
memory_type: memory.memory_type,
|
|
1115
|
+
content: memory.content,
|
|
1116
|
+
timestamp: memory.timestamp,
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function requireWriteConfirmation(toolName, args = {}) {
|
|
1121
|
+
if (args.dry_run === true || args.confirm_write === true) return;
|
|
1122
|
+
throw new Error(`${toolName} writes Wall-E or CTM state. Run with dry_run=true first, or set confirm_write=true to apply changes.`);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function stripRank(item) {
|
|
1126
|
+
if (!item || typeof item !== 'object') return item;
|
|
1127
|
+
const { rank, ...rest } = item;
|
|
1128
|
+
return rest;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
async function getCtmDbStatus() {
|
|
1132
|
+
const health = await ctmContextClient.getHealth();
|
|
1133
|
+
return {
|
|
1134
|
+
available: !health.unavailable,
|
|
1135
|
+
db_path: health.db_path,
|
|
1136
|
+
active_source: health.active_source || '',
|
|
1137
|
+
fallback_used: Boolean(health.fallback_used),
|
|
1138
|
+
serving_priority: health.source === 'ctm-api' ? 'ctm-api-first' : 'ctm-db-first',
|
|
1139
|
+
source: health.source || 'ctm-db',
|
|
1140
|
+
backend_source: health.backend_source || health.source || 'ctm-db',
|
|
1141
|
+
api_transport: health.api_transport || null,
|
|
1142
|
+
schema_ok: Boolean(health.ok),
|
|
1143
|
+
reason: health.reason || '',
|
|
1144
|
+
error: health.error || '',
|
|
1145
|
+
cached_tables: health.tables || [],
|
|
1146
|
+
expected_tables: ctmSessionContext.CTM_SESSION_TABLES || [],
|
|
1147
|
+
missing_tables: health.missing_tables || [],
|
|
1148
|
+
table_counts: health.table_counts || {},
|
|
1149
|
+
attempts: health.attempts || [],
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
async function safeCtmSearchSessions(args) {
|
|
1154
|
+
try {
|
|
1155
|
+
return await ctmContextClient.searchSessions(args);
|
|
1156
|
+
} catch (err) {
|
|
1157
|
+
return { ok: false, source: 'ctm-db', results: [], reason: err.message };
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
async function safeCtmSessionContext(args) {
|
|
1162
|
+
try {
|
|
1163
|
+
return await ctmContextClient.getSessionContext(args);
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
return { ok: false, source: 'ctm-db', sessions: [], messages: [], reason: err.message };
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
async function safeCtmContextPack(args) {
|
|
1170
|
+
try {
|
|
1171
|
+
return await ctmContextClient.buildContextPack(args);
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
return { ok: false, source: 'ctm-db', sessions: [], messages: [], reason: err.message };
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
517
1177
|
function assertJsonlPath(filePath) {
|
|
518
1178
|
if (!filePath || typeof filePath !== 'string') throw new Error('uri must be a JSONL file path');
|
|
519
1179
|
if (filePath.split(/[\\/]+/).includes('..')) throw new Error('uri cannot contain traversal segments');
|
|
@@ -748,6 +1408,137 @@ function jsonResponse(res, data, status = 200) {
|
|
|
748
1408
|
res.end(body);
|
|
749
1409
|
}
|
|
750
1410
|
|
|
1411
|
+
async function readMcpResource(uri) {
|
|
1412
|
+
if (!uri || typeof uri !== 'string') throw new Error('resource uri is required');
|
|
1413
|
+
|
|
1414
|
+
if (uri === 'walle://status/session-memory') {
|
|
1415
|
+
return {
|
|
1416
|
+
uri,
|
|
1417
|
+
mimeType: 'application/json',
|
|
1418
|
+
text: JSON.stringify(await executeTool('walle_memory_status', {})),
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
let parsed;
|
|
1423
|
+
try {
|
|
1424
|
+
parsed = new URL(uri);
|
|
1425
|
+
} catch {
|
|
1426
|
+
throw new Error(`Invalid resource uri: ${uri}`);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
if (parsed.protocol === 'walle-session:' && parsed.hostname === 'ctm') {
|
|
1430
|
+
const [rawSessionId, rawMode] = parsed.pathname.split('/').filter(Boolean);
|
|
1431
|
+
if (!rawSessionId) throw new Error('walle-session resource requires a session id');
|
|
1432
|
+
const sessionId = decodeURIComponent(rawSessionId);
|
|
1433
|
+
const mode = rawMode || 'full';
|
|
1434
|
+
const context = await ctmContextClient.getSessionContext({
|
|
1435
|
+
session_id: sessionId,
|
|
1436
|
+
limit: mode === 'full' || mode === 'messages' ? 0 : 200,
|
|
1437
|
+
format: mode === 'compact' || mode === 'summary' ? 'compact' : 'messages',
|
|
1438
|
+
dedupe: true,
|
|
1439
|
+
});
|
|
1440
|
+
if (mode === 'compact' || mode === 'summary') {
|
|
1441
|
+
return {
|
|
1442
|
+
uri,
|
|
1443
|
+
mimeType: 'text/markdown',
|
|
1444
|
+
text: context.text || ctmSessionContext.renderContextMarkdown(context, { compact: true }),
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
return {
|
|
1448
|
+
uri,
|
|
1449
|
+
mimeType: 'application/json',
|
|
1450
|
+
text: JSON.stringify(context),
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
if (parsed.protocol === 'walle-context:' && parsed.hostname === 'task') {
|
|
1455
|
+
const task = decodeURIComponent(parsed.pathname.replace(/^\/+/, ''));
|
|
1456
|
+
if (!task) throw new Error('walle-context task resource requires an encoded task query');
|
|
1457
|
+
const pack = await ctmContextClient.buildContextPack({ task, mode: 'compact' });
|
|
1458
|
+
return {
|
|
1459
|
+
uri,
|
|
1460
|
+
mimeType: 'text/markdown',
|
|
1461
|
+
text: pack.text || ctmSessionContext.renderContextMarkdown(pack, { compact: true }),
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
throw new Error(`Unknown resource uri: ${uri}`);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
function memoryRoutingPromptText(wallePort) {
|
|
1469
|
+
return wallEAgentMemoryInstructions(wallePort)
|
|
1470
|
+
.replace(/<!-- wall-e-memory-routing:start -->\n?/g, '')
|
|
1471
|
+
.replace(/<!-- wall-e-memory-routing:end -->\n?/g, '')
|
|
1472
|
+
.trim();
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function readMcpPrompt(name, args = {}) {
|
|
1476
|
+
if (name === 'walle_memory_routing') {
|
|
1477
|
+
const port = Number(args.walle_port || process.env.WALL_E_PORT || 3457);
|
|
1478
|
+
return {
|
|
1479
|
+
description: 'Wall-E MCP memory routing policy',
|
|
1480
|
+
messages: [{
|
|
1481
|
+
role: 'user',
|
|
1482
|
+
content: { type: 'text', text: memoryRoutingPromptText(port) },
|
|
1483
|
+
}],
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
if (name === 'walle_coding_resume') {
|
|
1487
|
+
const task = String(args.task || '').trim();
|
|
1488
|
+
const sessionId = String(args.session_id || '').trim();
|
|
1489
|
+
const lines = [
|
|
1490
|
+
'Resume this coding work using Wall-E MCP session memory before relying on web search or raw files.',
|
|
1491
|
+
'',
|
|
1492
|
+
'Steps:',
|
|
1493
|
+
'1. If Wall-E health is uncertain, call `walle_memory_status`.',
|
|
1494
|
+
'2. Call `walle_context_pack` with the current task to recover prior attempts, decisions, changed files, and next steps.',
|
|
1495
|
+
'3. If a session id is provided, call `walle_get_session` for exact source context.',
|
|
1496
|
+
'4. Use `walle_search_sessions` for targeted follow-up searches and `walle_diary_read` for recent handoff notes.',
|
|
1497
|
+
'5. Cite returned session ids/timestamps when using retrieved context. Use public web only for public/current facts or after Wall-E misses.',
|
|
1498
|
+
];
|
|
1499
|
+
if (task) lines.push('', `Task: ${task}`);
|
|
1500
|
+
if (sessionId) lines.push(`Session id: ${sessionId}`);
|
|
1501
|
+
return {
|
|
1502
|
+
description: 'Resume coding work with Wall-E CTM session context',
|
|
1503
|
+
messages: [{
|
|
1504
|
+
role: 'user',
|
|
1505
|
+
content: { type: 'text', text: lines.join('\n') },
|
|
1506
|
+
}],
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function buildToolResourceLinks(toolName, result) {
|
|
1513
|
+
if (!result || typeof result !== 'object') return [];
|
|
1514
|
+
const links = [];
|
|
1515
|
+
const addSession = (sessionId, title) => {
|
|
1516
|
+
if (!sessionId || links.some(link => link.uri.includes(encodeURIComponent(sessionId)))) return;
|
|
1517
|
+
links.push({
|
|
1518
|
+
type: 'resource_link',
|
|
1519
|
+
uri: `walle-session://ctm/${encodeURIComponent(sessionId)}/compact`,
|
|
1520
|
+
name: title || `CTM session ${sessionId}`,
|
|
1521
|
+
description: 'Compact CTM session context linked from Wall-E tool output',
|
|
1522
|
+
mimeType: 'text/markdown',
|
|
1523
|
+
annotations: { audience: ['assistant'], priority: 0.85 },
|
|
1524
|
+
});
|
|
1525
|
+
};
|
|
1526
|
+
|
|
1527
|
+
if (toolName === 'walle_search_sessions') {
|
|
1528
|
+
for (const item of result.results || []) {
|
|
1529
|
+
if (item.source_layer === 'ctm-db' || item.source_layer === 'ctm-api') {
|
|
1530
|
+
addSession(item.session_id, item.title);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (toolName === 'walle_context_pack') {
|
|
1535
|
+
for (const item of result.sessions || []) {
|
|
1536
|
+
addSession(item.session_id || item.id, item.title);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return links.slice(0, 5);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
751
1542
|
async function handleMcp(req, res) {
|
|
752
1543
|
let msg;
|
|
753
1544
|
try {
|
|
@@ -775,7 +1566,7 @@ async function handleMcp(req, res) {
|
|
|
775
1566
|
jsonrpc: '2.0', id,
|
|
776
1567
|
result: {
|
|
777
1568
|
protocolVersion: PROTOCOL_VERSION,
|
|
778
|
-
capabilities: { tools: {} },
|
|
1569
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
779
1570
|
serverInfo: { name: 'wall-e', version: (() => { try { return require('./package.json').version; } catch { return '0.0.0'; } })() },
|
|
780
1571
|
},
|
|
781
1572
|
});
|
|
@@ -788,6 +1579,56 @@ async function handleMcp(req, res) {
|
|
|
788
1579
|
});
|
|
789
1580
|
}
|
|
790
1581
|
|
|
1582
|
+
case 'resources/list': {
|
|
1583
|
+
return jsonResponse(res, {
|
|
1584
|
+
jsonrpc: '2.0', id,
|
|
1585
|
+
result: { resources: MCP_RESOURCES },
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
case 'resources/templates/list': {
|
|
1590
|
+
return jsonResponse(res, {
|
|
1591
|
+
jsonrpc: '2.0', id,
|
|
1592
|
+
result: { resourceTemplates: MCP_RESOURCE_TEMPLATES },
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
case 'prompts/list': {
|
|
1597
|
+
return jsonResponse(res, {
|
|
1598
|
+
jsonrpc: '2.0', id,
|
|
1599
|
+
result: { prompts: MCP_PROMPTS },
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
case 'prompts/get': {
|
|
1604
|
+
const prompt = readMcpPrompt(params?.name, params?.arguments || {});
|
|
1605
|
+
if (!prompt) {
|
|
1606
|
+
return jsonResponse(res, {
|
|
1607
|
+
jsonrpc: '2.0', id,
|
|
1608
|
+
result: { content: [{ type: 'text', text: `Unknown prompt: ${params?.name}` }], isError: true },
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
return jsonResponse(res, {
|
|
1612
|
+
jsonrpc: '2.0', id,
|
|
1613
|
+
result: prompt,
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
case 'resources/read': {
|
|
1618
|
+
try {
|
|
1619
|
+
const content = await readMcpResource(params?.uri);
|
|
1620
|
+
return jsonResponse(res, {
|
|
1621
|
+
jsonrpc: '2.0', id,
|
|
1622
|
+
result: { contents: [{ uri: content.uri, mimeType: content.mimeType, text: content.text }] },
|
|
1623
|
+
});
|
|
1624
|
+
} catch (err) {
|
|
1625
|
+
return jsonResponse(res, {
|
|
1626
|
+
jsonrpc: '2.0', id,
|
|
1627
|
+
result: { contents: [{ uri: params?.uri || '', mimeType: 'text/plain', text: `Error: ${err.message}` }], isError: true },
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
791
1632
|
case 'tools/call': {
|
|
792
1633
|
const toolName = params?.name;
|
|
793
1634
|
const toolArgs = params?.arguments || {};
|
|
@@ -799,9 +1640,10 @@ async function handleMcp(req, res) {
|
|
|
799
1640
|
result: { content: [{ type: 'text', text: `Unknown tool: ${toolName}` }], isError: true },
|
|
800
1641
|
});
|
|
801
1642
|
}
|
|
1643
|
+
const content = [{ type: 'text', text: JSON.stringify(result) }].concat(buildToolResourceLinks(toolName, result));
|
|
802
1644
|
return jsonResponse(res, {
|
|
803
1645
|
jsonrpc: '2.0', id,
|
|
804
|
-
result: { content
|
|
1646
|
+
result: { content, structuredContent: result },
|
|
805
1647
|
});
|
|
806
1648
|
} catch (err) {
|
|
807
1649
|
return jsonResponse(res, {
|
|
@@ -819,4 +1661,15 @@ async function handleMcp(req, res) {
|
|
|
819
1661
|
}
|
|
820
1662
|
}
|
|
821
1663
|
|
|
822
|
-
module.exports = {
|
|
1664
|
+
module.exports = {
|
|
1665
|
+
handleMcp,
|
|
1666
|
+
MCP_TOOLS,
|
|
1667
|
+
MCP_RESOURCES,
|
|
1668
|
+
MCP_RESOURCE_TEMPLATES,
|
|
1669
|
+
MCP_PROMPTS,
|
|
1670
|
+
executeTool,
|
|
1671
|
+
lookupWallEContext,
|
|
1672
|
+
readMcpPrompt,
|
|
1673
|
+
readMcpResource,
|
|
1674
|
+
routeWallEQuery,
|
|
1675
|
+
};
|