agents-dojo 0.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 +47 -0
- package/dist/a2a-server.d.ts +14 -0
- package/dist/a2a-server.js +44 -0
- package/dist/agent-executor.d.ts +15 -0
- package/dist/agent-executor.js +147 -0
- package/dist/agent-loader.d.ts +8 -0
- package/dist/agent-loader.js +89 -0
- package/dist/agent-registry.d.ts +20 -0
- package/dist/agent-registry.js +39 -0
- package/dist/claude-bridge.d.ts +11 -0
- package/dist/claude-bridge.js +124 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +134 -0
- package/dist/context-manager.d.ts +5 -0
- package/dist/context-manager.js +30 -0
- package/dist/event-translator.d.ts +16 -0
- package/dist/event-translator.js +142 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/manifest-schema.d.ts +163 -0
- package/dist/manifest-schema.js +94 -0
- package/dist/metrics.d.ts +5 -0
- package/dist/metrics.js +29 -0
- package/dist/monitor-bus.d.ts +40 -0
- package/dist/monitor-bus.js +19 -0
- package/dist/monitor-ws.d.ts +25 -0
- package/dist/monitor-ws.js +61 -0
- package/dist/part-mapper.d.ts +31 -0
- package/dist/part-mapper.js +21 -0
- package/dist/reload-api.d.ts +3 -0
- package/dist/reload-api.js +51 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +39 -0
- package/dist/types.d.ts +106 -0
- package/dist/types.js +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function createMonitorBus() {
|
|
2
|
+
const subs = new Set();
|
|
3
|
+
return {
|
|
4
|
+
subscribe(fn) {
|
|
5
|
+
subs.add(fn);
|
|
6
|
+
return () => subs.delete(fn);
|
|
7
|
+
},
|
|
8
|
+
emit(event) {
|
|
9
|
+
for (const fn of subs) {
|
|
10
|
+
try {
|
|
11
|
+
fn(event);
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
console.error('[monitor-bus] subscriber threw:', err);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
2
|
+
import type { Server } from 'http';
|
|
3
|
+
import type { AgentRegistry } from './agent-registry.js';
|
|
4
|
+
import type { MonitorBus } from './monitor-bus.js';
|
|
5
|
+
export interface MonitorWsOptions {
|
|
6
|
+
server: Server;
|
|
7
|
+
bus: MonitorBus;
|
|
8
|
+
path: string;
|
|
9
|
+
registry: AgentRegistry;
|
|
10
|
+
}
|
|
11
|
+
export type MonitorCommand = {
|
|
12
|
+
type: 'reload';
|
|
13
|
+
agentId: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'set_position';
|
|
16
|
+
agentId: string;
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
};
|
|
20
|
+
export type MonitorCommandHandler = (cmd: MonitorCommand) => void | Promise<void>;
|
|
21
|
+
export interface MonitorWsHandle {
|
|
22
|
+
wss: WebSocketServer;
|
|
23
|
+
clients: Set<WebSocket>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createMonitorWs(opts: MonitorWsOptions): MonitorWsHandle;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/monitor-ws.ts
|
|
2
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
|
+
export function createMonitorWs(opts) {
|
|
4
|
+
const clients = new Set();
|
|
5
|
+
const wss = new WebSocketServer({ server: opts.server, path: opts.path });
|
|
6
|
+
function handleCommand(cmd) {
|
|
7
|
+
if (cmd.type === 'reload') {
|
|
8
|
+
const result = opts.registry.reload(cmd.agentId);
|
|
9
|
+
// WS protocol is fire-and-forget for commands; on failure, log so the
|
|
10
|
+
// operator sees it in the server stderr rather than silently dropping.
|
|
11
|
+
if (!result.ok) {
|
|
12
|
+
console.error(`[monitor-ws] reload failed for "${cmd.agentId}": ${result.code} — ${result.message}`);
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (cmd.type === 'set_position') {
|
|
17
|
+
const agent = opts.registry.get(cmd.agentId);
|
|
18
|
+
if (!agent)
|
|
19
|
+
return;
|
|
20
|
+
agent.manifest.monitor = { ...agent.manifest.monitor, position: { x: cmd.x, y: cmd.y } };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
wss.on('connection', (ws) => {
|
|
24
|
+
clients.add(ws);
|
|
25
|
+
// Replay current agent list so late-connecting monitors see all loaded agents
|
|
26
|
+
for (const id of opts.registry.list()) {
|
|
27
|
+
const agent = opts.registry.get(id);
|
|
28
|
+
if (agent && ws.readyState === WebSocket.OPEN) {
|
|
29
|
+
ws.send(JSON.stringify({
|
|
30
|
+
type: 'agent_loaded',
|
|
31
|
+
agentId: id,
|
|
32
|
+
position: agent.manifest.monitor?.position,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
ws.on('close', () => clients.delete(ws));
|
|
37
|
+
ws.on('error', () => clients.delete(ws));
|
|
38
|
+
ws.on('message', (data) => {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(data.toString());
|
|
41
|
+
if (parsed &&
|
|
42
|
+
typeof parsed === 'object' &&
|
|
43
|
+
(parsed.type === 'reload' || parsed.type === 'set_position')) {
|
|
44
|
+
handleCommand(parsed);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore malformed JSON
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
opts.bus.subscribe((event) => {
|
|
53
|
+
const msg = JSON.stringify(event);
|
|
54
|
+
for (const ws of clients) {
|
|
55
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
56
|
+
ws.send(msg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return { wss, clients };
|
|
61
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type A2APart = {
|
|
2
|
+
kind: 'text';
|
|
3
|
+
text: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'file';
|
|
6
|
+
file: {
|
|
7
|
+
uri: string;
|
|
8
|
+
mimeType: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'data';
|
|
13
|
+
data: unknown;
|
|
14
|
+
};
|
|
15
|
+
export type AnthropicContentBlock = {
|
|
16
|
+
type: 'text';
|
|
17
|
+
text: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'image';
|
|
20
|
+
source: {
|
|
21
|
+
type: 'url';
|
|
22
|
+
url: string;
|
|
23
|
+
};
|
|
24
|
+
} | {
|
|
25
|
+
type: 'document';
|
|
26
|
+
source: {
|
|
27
|
+
type: 'url';
|
|
28
|
+
url: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare function a2aToContentBlocks(parts: A2APart[]): AnthropicContentBlock[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const IMAGE_MIME_PREFIX = 'image/';
|
|
2
|
+
const PDF_MIME = 'application/pdf';
|
|
3
|
+
export function a2aToContentBlocks(parts) {
|
|
4
|
+
return parts.map(part => {
|
|
5
|
+
switch (part.kind) {
|
|
6
|
+
case 'text':
|
|
7
|
+
return { type: 'text', text: part.text };
|
|
8
|
+
case 'file': {
|
|
9
|
+
if (part.file.mimeType.startsWith(IMAGE_MIME_PREFIX)) {
|
|
10
|
+
return { type: 'image', source: { type: 'url', url: part.file.uri } };
|
|
11
|
+
}
|
|
12
|
+
if (part.file.mimeType === PDF_MIME) {
|
|
13
|
+
return { type: 'document', source: { type: 'url', url: part.file.uri } };
|
|
14
|
+
}
|
|
15
|
+
throw new Error(`Unsupported file mime type: ${part.file.mimeType}`);
|
|
16
|
+
}
|
|
17
|
+
case 'data':
|
|
18
|
+
return { type: 'text', text: JSON.stringify(part.data, null, 2) };
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/reload-api.ts
|
|
2
|
+
import { Router } from 'express';
|
|
3
|
+
export function createReloadApi(registry) {
|
|
4
|
+
const router = Router();
|
|
5
|
+
router.get('/', (_req, res) => {
|
|
6
|
+
const items = registry.list().map(id => {
|
|
7
|
+
const a = registry.get(id);
|
|
8
|
+
return {
|
|
9
|
+
id,
|
|
10
|
+
name: a?.manifest.name,
|
|
11
|
+
version: a?.manifest.version,
|
|
12
|
+
position: a?.manifest.monitor?.position,
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
res.json({ agents: items });
|
|
16
|
+
});
|
|
17
|
+
router.post('/:id/reload', (req, res) => {
|
|
18
|
+
const { id } = req.params;
|
|
19
|
+
const result = registry.reload(id);
|
|
20
|
+
if (!result.ok) {
|
|
21
|
+
// Discriminate on the error code from the registry rather than parsing
|
|
22
|
+
// the message string.
|
|
23
|
+
if (result.code === 'not_found') {
|
|
24
|
+
res.status(404).json({ error: result.message });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// load_error: the on-disk manifest is invalid or a referenced file
|
|
28
|
+
// is missing. Surface the upstream error to the caller.
|
|
29
|
+
res.status(500).json({ error: result.message });
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
res.json({ ok: true, agent: { id, name: result.agent.manifest.name } });
|
|
34
|
+
});
|
|
35
|
+
router.post('/:id/position', (req, res) => {
|
|
36
|
+
const { id } = req.params;
|
|
37
|
+
const { x, y } = req.body ?? {};
|
|
38
|
+
if (typeof x !== 'number' || typeof y !== 'number') {
|
|
39
|
+
res.status(400).json({ error: 'x and y must be numbers' });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const agent = registry.get(id);
|
|
43
|
+
if (!agent) {
|
|
44
|
+
res.status(404).json({ error: `Agent "${id}" not found` });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
agent.manifest.monitor = { ...agent.manifest.monitor, position: { x, y } };
|
|
48
|
+
res.json({ ok: true });
|
|
49
|
+
});
|
|
50
|
+
return router;
|
|
51
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createServer as createHttpServer } from 'http';
|
|
2
|
+
import { AgentRegistry } from './agent-registry.js';
|
|
3
|
+
import { createMonitorBus } from './monitor-bus.js';
|
|
4
|
+
export interface CreateServerOptions {
|
|
5
|
+
agentsDir: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
monitorPort?: number;
|
|
8
|
+
singleAgent?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DojoServer {
|
|
11
|
+
registry: AgentRegistry;
|
|
12
|
+
bus: ReturnType<typeof createMonitorBus>;
|
|
13
|
+
httpServer: ReturnType<typeof createHttpServer>;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createServer(opts: CreateServerOptions): Promise<DojoServer>;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createServer as createHttpServer } from 'http';
|
|
2
|
+
import { AgentRegistry } from './agent-registry.js';
|
|
3
|
+
import { createA2AServer } from './a2a-server.js';
|
|
4
|
+
import { createReloadApi } from './reload-api.js';
|
|
5
|
+
import { createMetricsRouter, setAgentsLoaded } from './metrics.js';
|
|
6
|
+
import { createMonitorBus } from './monitor-bus.js';
|
|
7
|
+
import { createMonitorWs } from './monitor-ws.js';
|
|
8
|
+
export async function createServer(opts) {
|
|
9
|
+
// Create bus FIRST so handlers can reference it
|
|
10
|
+
const bus = createMonitorBus();
|
|
11
|
+
const registry = new AgentRegistry(opts.agentsDir);
|
|
12
|
+
registry.load();
|
|
13
|
+
setAgentsLoaded(registry.list().length);
|
|
14
|
+
registry.on('agent_loaded', (e) => {
|
|
15
|
+
setAgentsLoaded(registry.list().length);
|
|
16
|
+
bus.emit({ type: 'agent_loaded', ...e });
|
|
17
|
+
});
|
|
18
|
+
registry.on('agent_reloaded', (e) => bus.emit({ type: 'agent_reloaded', ...e }));
|
|
19
|
+
const a2a = createA2AServer({ registry, singleAgent: opts.singleAgent, port: opts.port, monitorBus: bus });
|
|
20
|
+
const app = a2a.app;
|
|
21
|
+
app.use('/agents', createReloadApi(registry));
|
|
22
|
+
app.use('/metrics', createMetricsRouter());
|
|
23
|
+
const httpServer = createHttpServer(app);
|
|
24
|
+
if (opts.monitorPort) {
|
|
25
|
+
// Bind monitor on a separate server
|
|
26
|
+
const monitorHttp = createHttpServer();
|
|
27
|
+
createMonitorWs({ server: monitorHttp, bus, path: '/monitor', registry });
|
|
28
|
+
await new Promise((r) => monitorHttp.listen(opts.monitorPort, r));
|
|
29
|
+
}
|
|
30
|
+
await new Promise((r) => httpServer.listen(opts.port ?? 41241, r));
|
|
31
|
+
return {
|
|
32
|
+
registry,
|
|
33
|
+
bus,
|
|
34
|
+
httpServer,
|
|
35
|
+
close: () => new Promise((resolve) => {
|
|
36
|
+
httpServer.close(() => resolve());
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface A2ACardConfig {
|
|
2
|
+
provider?: {
|
|
3
|
+
organization: string;
|
|
4
|
+
url: string;
|
|
5
|
+
};
|
|
6
|
+
capabilities?: {
|
|
7
|
+
streaming?: boolean;
|
|
8
|
+
pushNotifications?: boolean;
|
|
9
|
+
stateTransitionHistory?: boolean;
|
|
10
|
+
};
|
|
11
|
+
defaultInputModes?: Array<'text' | 'file' | 'data'>;
|
|
12
|
+
defaultOutputModes?: Array<'text' | 'file' | 'task-status'>;
|
|
13
|
+
skills?: Array<{
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
tags?: string[];
|
|
18
|
+
examples?: string[];
|
|
19
|
+
inputModes?: string[];
|
|
20
|
+
outputModes?: string[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface MonitorConfig {
|
|
24
|
+
position?: {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
};
|
|
28
|
+
sprite?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface LoadedAgent {
|
|
31
|
+
manifest: AgentManifest;
|
|
32
|
+
agentDir: string;
|
|
33
|
+
fixedContextContent: string;
|
|
34
|
+
mcpServersPath?: string;
|
|
35
|
+
hooksPath?: string;
|
|
36
|
+
settingsPath?: string;
|
|
37
|
+
agentsPath?: string;
|
|
38
|
+
sandboxPath?: string;
|
|
39
|
+
}
|
|
40
|
+
export type AgentManifest = {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
description: string;
|
|
44
|
+
version: string;
|
|
45
|
+
fixedContext: string;
|
|
46
|
+
systemPromptAppend?: string;
|
|
47
|
+
model?: string;
|
|
48
|
+
fallbackModel?: string;
|
|
49
|
+
executable?: 'bun' | 'deno' | 'node';
|
|
50
|
+
executableArgs?: string[];
|
|
51
|
+
pathToClaudeCodeExecutable?: string;
|
|
52
|
+
extraArgs?: Record<string, string | null>;
|
|
53
|
+
env?: Record<string, string | undefined>;
|
|
54
|
+
configDir?: string;
|
|
55
|
+
tools?: string[] | {
|
|
56
|
+
type: 'preset';
|
|
57
|
+
preset: 'claude_code';
|
|
58
|
+
};
|
|
59
|
+
allowedTools?: string[];
|
|
60
|
+
disallowedTools?: string[];
|
|
61
|
+
toolAliases?: Record<string, string>;
|
|
62
|
+
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk' | 'auto';
|
|
63
|
+
planModeInstructions?: string;
|
|
64
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
65
|
+
permissionPromptToolName?: string;
|
|
66
|
+
strictMcpConfig?: boolean;
|
|
67
|
+
additionalDirectories?: string[];
|
|
68
|
+
hooks?: string;
|
|
69
|
+
includeHookEvents?: boolean;
|
|
70
|
+
mcpServers?: string;
|
|
71
|
+
plugins?: Array<{
|
|
72
|
+
type: 'local';
|
|
73
|
+
path: string;
|
|
74
|
+
}>;
|
|
75
|
+
skills?: string[] | 'all';
|
|
76
|
+
settingSources?: Array<'user' | 'project' | 'local'>;
|
|
77
|
+
settings?: string;
|
|
78
|
+
agent?: string;
|
|
79
|
+
agents?: string;
|
|
80
|
+
agentProgressSummaries?: boolean;
|
|
81
|
+
forwardSubagentText?: boolean;
|
|
82
|
+
thinking?: {
|
|
83
|
+
type: 'adaptive';
|
|
84
|
+
} | {
|
|
85
|
+
type: 'enabled';
|
|
86
|
+
budgetTokens: number;
|
|
87
|
+
} | {
|
|
88
|
+
type: 'disabled';
|
|
89
|
+
};
|
|
90
|
+
effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
91
|
+
maxThinkingTokens?: number;
|
|
92
|
+
maxTurns?: number;
|
|
93
|
+
maxBudgetUsd?: number;
|
|
94
|
+
taskBudget?: {
|
|
95
|
+
tokens: number;
|
|
96
|
+
};
|
|
97
|
+
betas?: string[];
|
|
98
|
+
outputFormat?: {
|
|
99
|
+
type: 'json_schema';
|
|
100
|
+
schema: object;
|
|
101
|
+
};
|
|
102
|
+
sandbox?: string;
|
|
103
|
+
forkSession?: boolean;
|
|
104
|
+
a2aCard?: A2ACardConfig;
|
|
105
|
+
monitor?: MonitorConfig;
|
|
106
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agents-dojo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A2A-compatible Agent framework built on Claude Code SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"agents-dojo": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"test:e2e": "vitest run tests/e2e",
|
|
21
|
+
"start": "node dist/cli.js",
|
|
22
|
+
"dev": "tsc --watch"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@a2a-js/sdk": "^0.3.13",
|
|
26
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.145",
|
|
27
|
+
"express": "^5.2.1",
|
|
28
|
+
"strip-json-comments": "^5.0.3",
|
|
29
|
+
"uuid": "^14.0.0",
|
|
30
|
+
"ws": "^8.18.0",
|
|
31
|
+
"zod": "^4.4.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/express": "^5.0.6",
|
|
35
|
+
"@types/node": "^25.9.1",
|
|
36
|
+
"@types/uuid": "^10.0.0",
|
|
37
|
+
"@types/ws": "^8.5.10",
|
|
38
|
+
"typescript": "^5.6.0",
|
|
39
|
+
"vitest": "^2.1.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18"
|
|
43
|
+
}
|
|
44
|
+
}
|