openclaw-node-harness 2.0.3 → 2.1.0
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 +646 -3
- package/bin/hyperagent.mjs +419 -0
- package/bin/mesh-agent.js +603 -81
- package/bin/mesh-bridge.js +340 -11
- package/bin/mesh-deploy-listener.js +119 -97
- package/bin/mesh-deploy.js +8 -0
- package/bin/mesh-task-daemon.js +1005 -40
- package/bin/mesh.js +423 -6
- package/config/claude-settings.json +95 -0
- package/config/daemon.json.template +2 -1
- package/config/git-hooks/pre-commit +13 -0
- package/config/git-hooks/pre-push +12 -0
- package/config/harness-rules.json +174 -0
- package/config/plan-templates/team-bugfix.yaml +52 -0
- package/config/plan-templates/team-deploy.yaml +50 -0
- package/config/plan-templates/team-feature.yaml +71 -0
- package/config/roles/qa-engineer.yaml +36 -0
- package/config/roles/solidity-dev.yaml +51 -0
- package/config/roles/tech-architect.yaml +36 -0
- package/config/rules/framework/solidity.md +22 -0
- package/config/rules/framework/typescript.md +21 -0
- package/config/rules/framework/unity.md +21 -0
- package/config/rules/universal/design-docs.md +18 -0
- package/config/rules/universal/git-hygiene.md +18 -0
- package/config/rules/universal/security.md +19 -0
- package/config/rules/universal/test-standards.md +19 -0
- package/identity/DELEGATION.md +6 -6
- package/install.sh +300 -8
- package/lib/circling-parser.js +119 -0
- package/lib/hyperagent-store.mjs +652 -0
- package/lib/kanban-io.js +59 -10
- package/lib/mcp-knowledge/bench.mjs +118 -0
- package/lib/mcp-knowledge/core.mjs +528 -0
- package/lib/mcp-knowledge/package.json +25 -0
- package/lib/mcp-knowledge/server.mjs +245 -0
- package/lib/mcp-knowledge/test.mjs +802 -0
- package/lib/memory-budget.mjs +261 -0
- package/lib/mesh-collab.js +354 -4
- package/lib/mesh-harness.js +427 -0
- package/lib/mesh-plans.js +13 -5
- package/lib/mesh-registry.js +11 -2
- package/lib/mesh-tasks.js +67 -0
- package/lib/plan-templates.js +226 -0
- package/lib/pre-compression-flush.mjs +320 -0
- package/lib/role-loader.js +292 -0
- package/lib/rule-loader.js +358 -0
- package/lib/session-store.mjs +458 -0
- package/lib/transcript-parser.mjs +292 -0
- package/mission-control/drizzle/soul_schema_update.sql +29 -0
- package/mission-control/drizzle.config.ts +1 -4
- package/mission-control/package-lock.json +1571 -83
- package/mission-control/package.json +6 -2
- package/mission-control/scripts/gen-chronology.js +3 -3
- package/mission-control/scripts/import-pipeline-v2.js +0 -16
- package/mission-control/scripts/import-pipeline.js +0 -15
- package/mission-control/src/app/api/cowork/clusters/[id]/members/route.ts +117 -0
- package/mission-control/src/app/api/cowork/clusters/[id]/route.ts +84 -0
- package/mission-control/src/app/api/cowork/clusters/route.ts +141 -0
- package/mission-control/src/app/api/cowork/dispatch/route.ts +128 -0
- package/mission-control/src/app/api/cowork/events/route.ts +65 -0
- package/mission-control/src/app/api/cowork/intervene/route.ts +259 -0
- package/mission-control/src/app/api/cowork/sessions/[id]/route.ts +37 -0
- package/mission-control/src/app/api/cowork/sessions/route.ts +64 -0
- package/mission-control/src/app/api/diagnostics/route.ts +97 -0
- package/mission-control/src/app/api/diagnostics/test-runner/route.ts +990 -0
- package/mission-control/src/app/api/mesh/events/route.ts +95 -19
- package/mission-control/src/app/api/mesh/identity/route.ts +11 -0
- package/mission-control/src/app/api/mesh/tasks/[id]/route.ts +92 -0
- package/mission-control/src/app/api/mesh/tasks/route.ts +91 -0
- package/mission-control/src/app/api/tasks/[id]/handoff/route.ts +1 -1
- package/mission-control/src/app/api/tasks/[id]/route.ts +90 -4
- package/mission-control/src/app/api/tasks/route.ts +21 -30
- package/mission-control/src/app/cowork/page.tsx +261 -0
- package/mission-control/src/app/diagnostics/page.tsx +385 -0
- package/mission-control/src/app/graph/page.tsx +26 -0
- package/mission-control/src/app/memory/page.tsx +1 -1
- package/mission-control/src/app/obsidian/page.tsx +36 -6
- package/mission-control/src/app/roadmap/page.tsx +24 -0
- package/mission-control/src/app/souls/page.tsx +2 -2
- package/mission-control/src/components/board/execution-config.tsx +431 -0
- package/mission-control/src/components/board/kanban-board.tsx +75 -9
- package/mission-control/src/components/board/kanban-column.tsx +135 -19
- package/mission-control/src/components/board/task-card.tsx +55 -2
- package/mission-control/src/components/board/unified-task-dialog.tsx +82 -4
- package/mission-control/src/components/cowork/cluster-card.tsx +176 -0
- package/mission-control/src/components/cowork/create-cluster-dialog.tsx +251 -0
- package/mission-control/src/components/cowork/dispatch-form.tsx +423 -0
- package/mission-control/src/components/cowork/role-picker.tsx +102 -0
- package/mission-control/src/components/cowork/session-card.tsx +284 -0
- package/mission-control/src/components/layout/sidebar.tsx +39 -2
- package/mission-control/src/lib/__tests__/daily-log.test.ts +82 -0
- package/mission-control/src/lib/__tests__/memory-md.test.ts +87 -0
- package/mission-control/src/lib/__tests__/mesh-kv-sync.test.ts +465 -0
- package/mission-control/src/lib/__tests__/mocks/mock-kv.ts +131 -0
- package/mission-control/src/lib/__tests__/status-kanban.test.ts +46 -0
- package/mission-control/src/lib/__tests__/task-markdown.test.ts +188 -0
- package/mission-control/src/lib/__tests__/wikilinks.test.ts +175 -0
- package/mission-control/src/lib/config.ts +58 -0
- package/mission-control/src/lib/db/index.ts +69 -0
- package/mission-control/src/lib/db/schema.ts +61 -3
- package/mission-control/src/lib/hooks.ts +309 -0
- package/mission-control/src/lib/memory/entities.ts +3 -2
- package/mission-control/src/lib/nats.ts +66 -1
- package/mission-control/src/lib/parsers/task-markdown.ts +52 -2
- package/mission-control/src/lib/parsers/transcript.ts +4 -4
- package/mission-control/src/lib/scheduler.ts +12 -11
- package/mission-control/src/lib/sync/mesh-kv.ts +279 -0
- package/mission-control/src/lib/sync/tasks.ts +23 -1
- package/mission-control/src/lib/task-id.ts +32 -0
- package/mission-control/src/lib/tts/index.ts +33 -9
- package/mission-control/tsconfig.json +2 -1
- package/mission-control/vitest.config.ts +14 -0
- package/package.json +15 -2
- package/services/service-manifest.json +1 -1
- package/skills/cc-godmode/references/agents.md +8 -8
- package/workspace-bin/memory-daemon.mjs +199 -5
- package/workspace-bin/session-search.mjs +204 -0
- package/workspace-bin/web-fetch.mjs +65 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @openclaw/mcp-knowledge — MCP server entrypoint
|
|
4
|
+
*
|
|
5
|
+
* Dual transport:
|
|
6
|
+
* - KNOWLEDGE_PORT not set → stdio MCP (Claude Code, OpenClaw child process)
|
|
7
|
+
* - KNOWLEDGE_PORT set → HTTP MCP + /health (mesh-internal, worker nodes)
|
|
8
|
+
*
|
|
9
|
+
* All core logic lives in core.mjs. This file is pure transport wiring.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
13
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
|
+
import {
|
|
15
|
+
CallToolRequestSchema,
|
|
16
|
+
ListToolsRequestSchema,
|
|
17
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
import { hostname } from 'node:os';
|
|
20
|
+
import { createKnowledgeEngine, WORKSPACE } from './core.mjs';
|
|
21
|
+
|
|
22
|
+
// ─── MCP Tool Definitions ────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
const TOOLS = [
|
|
25
|
+
{
|
|
26
|
+
name: 'semantic_search',
|
|
27
|
+
description:
|
|
28
|
+
'Search the knowledge base by meaning. Returns documents semantically similar to the query, ranked by relevance. Use this to find notes, lore, architecture docs, and memories related to a topic.',
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
query: { type: 'string', description: 'Natural language search query' },
|
|
33
|
+
limit: { type: 'number', description: 'Max results (default 10)', default: 10 },
|
|
34
|
+
},
|
|
35
|
+
required: ['query'],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'find_related',
|
|
40
|
+
description:
|
|
41
|
+
'Find documents related to a specific file. Returns other knowledge base documents that are semantically similar to the given document.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
doc_path: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Relative path of the document (e.g. "projects/arcane/lore/FACTIONS_LORE_INTEGRATED_V1.md")',
|
|
48
|
+
},
|
|
49
|
+
limit: { type: 'number', description: 'Max results (default 10)', default: 10 },
|
|
50
|
+
},
|
|
51
|
+
required: ['doc_path'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'reindex',
|
|
56
|
+
description:
|
|
57
|
+
'Manually trigger reindexing of the knowledge base. Use after adding or editing knowledge files. Pass force=true to re-embed all files regardless of content hash.',
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
force: { type: 'boolean', description: 'Force re-embed all files', default: false },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'knowledge_stats',
|
|
67
|
+
description: 'Get statistics about the indexed knowledge base.',
|
|
68
|
+
inputSchema: { type: 'object', properties: {} },
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
// ─── MCP Server Factory ──────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function createMcpServer(engine) {
|
|
75
|
+
const server = new Server(
|
|
76
|
+
{ name: '@openclaw/mcp-knowledge', version: '0.1.0' },
|
|
77
|
+
{ capabilities: { tools: {} } }
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
81
|
+
|
|
82
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
83
|
+
const { name, arguments: args } = request.params;
|
|
84
|
+
try {
|
|
85
|
+
switch (name) {
|
|
86
|
+
case 'semantic_search': {
|
|
87
|
+
const results = await engine.search(args.query, args.limit || 10);
|
|
88
|
+
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
89
|
+
}
|
|
90
|
+
case 'find_related': {
|
|
91
|
+
const results = await engine.related(args.doc_path, args.limit || 10);
|
|
92
|
+
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
93
|
+
}
|
|
94
|
+
case 'reindex': {
|
|
95
|
+
const result = await engine.reindex(args?.force || false);
|
|
96
|
+
return {
|
|
97
|
+
content: [{
|
|
98
|
+
type: 'text',
|
|
99
|
+
text: `Reindex complete: ${result.indexed} indexed, ${result.skipped} unchanged, ${result.deleted} removed. ${result.total} files scanned.`,
|
|
100
|
+
}],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
case 'knowledge_stats': {
|
|
104
|
+
const stats = engine.stats();
|
|
105
|
+
return { content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }] };
|
|
106
|
+
}
|
|
107
|
+
default:
|
|
108
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {
|
|
111
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return server;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Stdio Mode ──────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
async function startStdio(engine) {
|
|
121
|
+
const server = createMcpServer(engine);
|
|
122
|
+
const transport = new StdioServerTransport();
|
|
123
|
+
await server.connect(transport);
|
|
124
|
+
process.stderr.write('[mcp-knowledge] MCP server running on stdio\n');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── HTTP Mode ───────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
async function startHttp(engine, port, host) {
|
|
130
|
+
const { StreamableHTTPServerTransport } = await import(
|
|
131
|
+
'@modelcontextprotocol/sdk/server/streamableHttp.js'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const { createServer } = await import('node:http');
|
|
135
|
+
|
|
136
|
+
const startTime = Date.now();
|
|
137
|
+
|
|
138
|
+
const httpServer = createServer(async (req, res) => {
|
|
139
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
140
|
+
|
|
141
|
+
// Health endpoint — liveness probe for mesh health publisher
|
|
142
|
+
if (url.pathname === '/health' && req.method === 'GET') {
|
|
143
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
144
|
+
res.end(JSON.stringify({
|
|
145
|
+
status: 'ok',
|
|
146
|
+
uptime: Math.floor((Date.now() - startTime) / 1000),
|
|
147
|
+
}));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// MCP endpoint
|
|
152
|
+
if (url.pathname === '/mcp') {
|
|
153
|
+
if (req.method === 'POST') {
|
|
154
|
+
// Stateless: fresh server+transport per request
|
|
155
|
+
const server = createMcpServer(engine);
|
|
156
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
157
|
+
res.on('close', () => {
|
|
158
|
+
transport.close().catch(() => {});
|
|
159
|
+
server.close().catch(() => {});
|
|
160
|
+
});
|
|
161
|
+
await server.connect(transport);
|
|
162
|
+
|
|
163
|
+
// Parse body
|
|
164
|
+
const chunks = [];
|
|
165
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
166
|
+
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
167
|
+
|
|
168
|
+
await transport.handleRequest(req, res, body);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// GET and DELETE not supported in stateless mode
|
|
173
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
174
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use POST for MCP requests.' }));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 404 for everything else
|
|
179
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
180
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
httpServer.listen(port, host, () => {
|
|
184
|
+
process.stderr.write(`[mcp-knowledge] HTTP MCP server listening on ${host}:${port}\n`);
|
|
185
|
+
process.stderr.write(`[mcp-knowledge] MCP endpoint: POST http://${host}:${port}/mcp\n`);
|
|
186
|
+
process.stderr.write(`[mcp-knowledge] Health check: GET http://${host}:${port}/health\n`);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── NATS Mesh Tool Registration ─────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
async function registerNatsTools(engine) {
|
|
193
|
+
const require = createRequire(import.meta.url);
|
|
194
|
+
const { createRegistry } = require('../mesh-registry.js');
|
|
195
|
+
const nodeId = process.env.OPENCLAW_NODE_ID || hostname().toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
196
|
+
|
|
197
|
+
const { registry } = await createRegistry(nodeId);
|
|
198
|
+
|
|
199
|
+
await registry.register({
|
|
200
|
+
name: 'knowledge',
|
|
201
|
+
version: '0.1.0',
|
|
202
|
+
description: 'Semantic search over markdown knowledge base',
|
|
203
|
+
methods: [
|
|
204
|
+
{ name: 'search', description: 'Semantic search by meaning', params: { query: 'string', limit: 'number?' } },
|
|
205
|
+
{ name: 'related', description: 'Find related documents', params: { doc_path: 'string', limit: 'number?' } },
|
|
206
|
+
{ name: 'reindex', description: 'Re-index knowledge base', params: { force: 'boolean?' } },
|
|
207
|
+
{ name: 'stats', description: 'Index statistics', params: {} },
|
|
208
|
+
],
|
|
209
|
+
}, {
|
|
210
|
+
search: async ({ query, limit }) => engine.search(query, limit || 10),
|
|
211
|
+
related: async ({ doc_path, limit }) => engine.related(doc_path, limit || 10),
|
|
212
|
+
reindex: async ({ force }) => engine.reindex(force || false),
|
|
213
|
+
stats: async () => engine.stats(),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
registry.startHeartbeat();
|
|
217
|
+
process.stderr.write(`[mcp-knowledge] NATS mesh tools registered as ${nodeId}\n`);
|
|
218
|
+
return registry;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
async function main() {
|
|
224
|
+
const engine = await createKnowledgeEngine();
|
|
225
|
+
|
|
226
|
+
// Optional NATS mesh tool registration (non-blocking if NATS unavailable)
|
|
227
|
+
try {
|
|
228
|
+
await registerNatsTools(engine);
|
|
229
|
+
} catch {
|
|
230
|
+
process.stderr.write('[mcp-knowledge] NATS not available — mesh tools disabled\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const port = process.env.KNOWLEDGE_PORT;
|
|
234
|
+
if (port) {
|
|
235
|
+
const host = process.env.KNOWLEDGE_HOST || '127.0.0.1';
|
|
236
|
+
await startHttp(engine, parseInt(port, 10), host);
|
|
237
|
+
} else {
|
|
238
|
+
await startStdio(engine);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main().catch((err) => {
|
|
243
|
+
process.stderr.write(`[mcp-knowledge] fatal: ${err.message}\n${err.stack}\n`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
});
|