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
package/dist/cli.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve, join } from 'path';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
4
|
+
import { createServer } from './server.js';
|
|
5
|
+
// ── init command ──────────────────────────────────────────
|
|
6
|
+
const ECHO_MANIFEST = `{
|
|
7
|
+
"id": "echo",
|
|
8
|
+
"name": "Echo Agent",
|
|
9
|
+
"description": "A minimal example agent that echoes user input.",
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"fixedContext": "context.md",
|
|
12
|
+
"a2aCard": {
|
|
13
|
+
"skills": [
|
|
14
|
+
{
|
|
15
|
+
"id": "echo",
|
|
16
|
+
"name": "Echo",
|
|
17
|
+
"description": "Repeats back whatever you send",
|
|
18
|
+
"tags": ["example"]
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"monitor": {
|
|
23
|
+
"position": { "x": 200, "y": 200 },
|
|
24
|
+
"sprite": "default"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
const ECHO_CONTEXT = `# Role
|
|
29
|
+
|
|
30
|
+
You are **Echo Agent**, a minimal example agent.
|
|
31
|
+
|
|
32
|
+
Repeat back the user's input, prefixed with "ECHO:".
|
|
33
|
+
Do not add any other commentary.
|
|
34
|
+
`;
|
|
35
|
+
function runInit(targetDir) {
|
|
36
|
+
const agentsDir = join(targetDir, 'agents');
|
|
37
|
+
const echoDir = join(agentsDir, 'echo');
|
|
38
|
+
if (existsSync(echoDir)) {
|
|
39
|
+
console.log(`agents/echo/ already exists — skipping.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
mkdirSync(echoDir, { recursive: true });
|
|
43
|
+
writeFileSync(join(echoDir, 'manifest.jsonc'), ECHO_MANIFEST);
|
|
44
|
+
writeFileSync(join(echoDir, 'context.md'), ECHO_CONTEXT);
|
|
45
|
+
console.log(`Created agents/echo/ with manifest.jsonc and context.md.
|
|
46
|
+
|
|
47
|
+
Next steps:
|
|
48
|
+
agents-dojo # start the server
|
|
49
|
+
agents-dojo --help # see all options
|
|
50
|
+
|
|
51
|
+
To add your own agent:
|
|
52
|
+
mkdir agents/my-agent
|
|
53
|
+
# create manifest.jsonc and context.md inside it
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
export const DEFAULT_ARGS = {
|
|
57
|
+
agentsDir: './agents',
|
|
58
|
+
port: 41241,
|
|
59
|
+
monitorPort: 41242,
|
|
60
|
+
singleAgent: null,
|
|
61
|
+
help: false,
|
|
62
|
+
};
|
|
63
|
+
function parseArgs(argv) {
|
|
64
|
+
const args = { ...DEFAULT_ARGS };
|
|
65
|
+
for (let i = 0; i < argv.length; i++) {
|
|
66
|
+
const a = argv[i];
|
|
67
|
+
if (a === '--help' || a === '-h')
|
|
68
|
+
args.help = true;
|
|
69
|
+
else if (a === '--agents-dir' && argv[i + 1])
|
|
70
|
+
args.agentsDir = resolve(argv[++i]);
|
|
71
|
+
else if (a.startsWith('--agents-dir='))
|
|
72
|
+
args.agentsDir = resolve(a.slice('--agents-dir='.length));
|
|
73
|
+
else if (a === '--port' && argv[i + 1])
|
|
74
|
+
args.port = parseInt(argv[++i], 10);
|
|
75
|
+
else if (a === '--monitor-port' && argv[i + 1])
|
|
76
|
+
args.monitorPort = parseInt(argv[++i], 10);
|
|
77
|
+
else if (a === '--no-monitor')
|
|
78
|
+
args.monitorPort = null;
|
|
79
|
+
else if (a.startsWith('--agent='))
|
|
80
|
+
args.singleAgent = a.slice('--agent='.length);
|
|
81
|
+
else if (a === '--agent' && argv[i + 1])
|
|
82
|
+
args.singleAgent = argv[++i];
|
|
83
|
+
}
|
|
84
|
+
return args;
|
|
85
|
+
}
|
|
86
|
+
function printHelp() {
|
|
87
|
+
console.log(`Usage: agents-dojo [command] [options]
|
|
88
|
+
|
|
89
|
+
Commands:
|
|
90
|
+
init Create agents/echo/ example in the current directory
|
|
91
|
+
(default) Start the A2A server
|
|
92
|
+
|
|
93
|
+
Options:
|
|
94
|
+
--agents-dir <path> Directory containing agent subdirectories (default: ${DEFAULT_ARGS.agentsDir})
|
|
95
|
+
--port <port> A2A HTTP port (default: ${DEFAULT_ARGS.port})
|
|
96
|
+
--monitor-port <port> Monitor WebSocket port (default: ${DEFAULT_ARGS.monitorPort})
|
|
97
|
+
--no-monitor Disable Monitor WebSocket
|
|
98
|
+
--agent=<id> Single Agent mode: only load and serve the named agent
|
|
99
|
+
-h, --help Show this help
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
async function main() {
|
|
103
|
+
const rawArgs = process.argv.slice(2);
|
|
104
|
+
// Handle `agents-dojo init [dir]`
|
|
105
|
+
if (rawArgs[0] === 'init') {
|
|
106
|
+
runInit(resolve(rawArgs[1] ?? '.'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const args = parseArgs(rawArgs);
|
|
110
|
+
if (args.help) {
|
|
111
|
+
printHelp();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(`[agents-dojo] agents-dir: ${args.agentsDir}`);
|
|
115
|
+
console.log(`[agents-dojo] A2A port: ${args.port}`);
|
|
116
|
+
if (args.monitorPort !== null) {
|
|
117
|
+
console.log(`[agents-dojo] Monitor port: ${args.monitorPort}`);
|
|
118
|
+
}
|
|
119
|
+
if (args.singleAgent) {
|
|
120
|
+
console.log(`[agents-dojo] Single agent mode: ${args.singleAgent}`);
|
|
121
|
+
}
|
|
122
|
+
const server = await createServer({
|
|
123
|
+
agentsDir: args.agentsDir,
|
|
124
|
+
port: args.port,
|
|
125
|
+
monitorPort: args.monitorPort ?? undefined,
|
|
126
|
+
singleAgent: args.singleAgent ?? undefined,
|
|
127
|
+
});
|
|
128
|
+
console.log(`[agents-dojo] ${server.registry.list().length} agents loaded: ${server.registry.list().join(', ')}`);
|
|
129
|
+
console.log(`[agents-dojo] Ready.`);
|
|
130
|
+
}
|
|
131
|
+
main().catch((err) => {
|
|
132
|
+
console.error('[agents-dojo] Fatal:', err);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/context-manager.ts
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join, resolve, isAbsolute } from 'path';
|
|
4
|
+
export class ContextLoadError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'ContextLoadError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function loadContext(agentDir, relativePath) {
|
|
11
|
+
const resolved = isAbsolute(relativePath) ? relativePath : join(agentDir, relativePath);
|
|
12
|
+
const normalized = resolve(resolved);
|
|
13
|
+
// Reject path traversal
|
|
14
|
+
const agentAbs = resolve(agentDir);
|
|
15
|
+
if (!normalized.startsWith(agentAbs + '/') && normalized !== agentAbs) {
|
|
16
|
+
throw new ContextLoadError(`Path "${relativePath}" escapes agent directory`);
|
|
17
|
+
}
|
|
18
|
+
if (!existsSync(normalized)) {
|
|
19
|
+
throw new ContextLoadError(`Context file not found: ${normalized}`);
|
|
20
|
+
}
|
|
21
|
+
return readFileSync(normalized, 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
export function resolveConfigDir(agentDir, override) {
|
|
24
|
+
if (override) {
|
|
25
|
+
if (isAbsolute(override))
|
|
26
|
+
return override;
|
|
27
|
+
return join(agentDir, override);
|
|
28
|
+
}
|
|
29
|
+
return join(agentDir, '.claude');
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import type { MonitorBus } from './monitor-bus.js';
|
|
3
|
+
export interface TranslatorContext {
|
|
4
|
+
agentId: string;
|
|
5
|
+
taskId: string;
|
|
6
|
+
contextId: string;
|
|
7
|
+
bus: {
|
|
8
|
+
publish: (event: any) => void;
|
|
9
|
+
finished: () => void;
|
|
10
|
+
};
|
|
11
|
+
monitorBus?: MonitorBus;
|
|
12
|
+
}
|
|
13
|
+
export interface Translator {
|
|
14
|
+
onSdkEvent: (msg: SDKMessage) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function createTranslator(ctx: TranslatorContext): Translator;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/event-translator.ts
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
export function createTranslator(ctx) {
|
|
4
|
+
let finalText = '';
|
|
5
|
+
// Map tool_use_id -> tool name, so tool_result events (which only carry id)
|
|
6
|
+
// can be reported back to the monitor with the right name.
|
|
7
|
+
const toolNames = new Map();
|
|
8
|
+
function publishWorking(text) {
|
|
9
|
+
ctx.bus.publish({
|
|
10
|
+
kind: 'status-update',
|
|
11
|
+
taskId: ctx.taskId,
|
|
12
|
+
contextId: ctx.contextId,
|
|
13
|
+
status: {
|
|
14
|
+
kind: 'status',
|
|
15
|
+
state: 'working',
|
|
16
|
+
message: {
|
|
17
|
+
kind: 'message',
|
|
18
|
+
role: 'agent',
|
|
19
|
+
messageId: uuidv4(),
|
|
20
|
+
parts: [{ kind: 'text', text }],
|
|
21
|
+
taskId: ctx.taskId,
|
|
22
|
+
contextId: ctx.contextId,
|
|
23
|
+
},
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
},
|
|
26
|
+
final: false,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function publishCompleted() {
|
|
30
|
+
ctx.bus.publish({
|
|
31
|
+
kind: 'status-update',
|
|
32
|
+
taskId: ctx.taskId,
|
|
33
|
+
contextId: ctx.contextId,
|
|
34
|
+
status: {
|
|
35
|
+
kind: 'status',
|
|
36
|
+
state: 'completed',
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
},
|
|
39
|
+
final: true,
|
|
40
|
+
});
|
|
41
|
+
ctx.bus.finished();
|
|
42
|
+
}
|
|
43
|
+
function publishFailed(reason) {
|
|
44
|
+
ctx.bus.publish({
|
|
45
|
+
kind: 'status-update',
|
|
46
|
+
taskId: ctx.taskId,
|
|
47
|
+
contextId: ctx.contextId,
|
|
48
|
+
status: {
|
|
49
|
+
kind: 'status',
|
|
50
|
+
state: 'failed',
|
|
51
|
+
message: {
|
|
52
|
+
kind: 'message',
|
|
53
|
+
role: 'agent',
|
|
54
|
+
messageId: uuidv4(),
|
|
55
|
+
parts: [{ kind: 'text', text: reason }],
|
|
56
|
+
taskId: ctx.taskId,
|
|
57
|
+
contextId: ctx.contextId,
|
|
58
|
+
},
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
},
|
|
61
|
+
final: true,
|
|
62
|
+
});
|
|
63
|
+
ctx.bus.finished();
|
|
64
|
+
}
|
|
65
|
+
function summarizeInput(input) {
|
|
66
|
+
if (input == null)
|
|
67
|
+
return '';
|
|
68
|
+
if (typeof input === 'string')
|
|
69
|
+
return input.slice(0, 80);
|
|
70
|
+
try {
|
|
71
|
+
const s = JSON.stringify(input);
|
|
72
|
+
return s.length > 80 ? s.slice(0, 80) + '…' : s;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
onSdkEvent(msg) {
|
|
80
|
+
const m = msg;
|
|
81
|
+
if (m.type === 'assistant' && m.message?.content) {
|
|
82
|
+
for (const block of m.message.content) {
|
|
83
|
+
if (block.type === 'text') {
|
|
84
|
+
finalText = block.text;
|
|
85
|
+
// Treat all assistant text as "thinking" / intermediate
|
|
86
|
+
publishWorking(block.text);
|
|
87
|
+
}
|
|
88
|
+
else if (block.type === 'tool_use' && ctx.monitorBus) {
|
|
89
|
+
if (block.id)
|
|
90
|
+
toolNames.set(block.id, block.name);
|
|
91
|
+
ctx.monitorBus.emit({
|
|
92
|
+
type: 'tool_call_start',
|
|
93
|
+
taskId: ctx.taskId,
|
|
94
|
+
toolName: block.name,
|
|
95
|
+
inputSummary: summarizeInput(block.input),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (m.type === 'user' && m.message?.content && ctx.monitorBus) {
|
|
101
|
+
const content = Array.isArray(m.message.content) ? m.message.content : [];
|
|
102
|
+
for (const block of content) {
|
|
103
|
+
if (block.type === 'tool_result') {
|
|
104
|
+
const id = block.tool_use_id;
|
|
105
|
+
const name = block.tool_name || (id && toolNames.get(id)) || 'tool';
|
|
106
|
+
ctx.monitorBus.emit({
|
|
107
|
+
type: 'tool_call_end',
|
|
108
|
+
taskId: ctx.taskId,
|
|
109
|
+
toolName: name,
|
|
110
|
+
success: !block.is_error,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (m.type === 'result') {
|
|
116
|
+
if (m.subtype === 'success' || m.subtype === undefined) {
|
|
117
|
+
// Emit the final artifact with the last text we saw
|
|
118
|
+
if (finalText) {
|
|
119
|
+
ctx.bus.publish({
|
|
120
|
+
kind: 'artifact-update',
|
|
121
|
+
taskId: ctx.taskId,
|
|
122
|
+
contextId: ctx.contextId,
|
|
123
|
+
artifact: {
|
|
124
|
+
artifactId: uuidv4(),
|
|
125
|
+
name: 'Response',
|
|
126
|
+
parts: [{ kind: 'text', text: finalText }],
|
|
127
|
+
},
|
|
128
|
+
append: false,
|
|
129
|
+
lastChunk: true,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
publishCompleted();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// error_max_turns, error_during_execution, etc.
|
|
136
|
+
const reason = m.errors?.join('; ') || m.subtype;
|
|
137
|
+
publishFailed(reason);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createServer } from './server.js';
|
|
2
|
+
export type { CreateServerOptions, DojoServer } from './server.js';
|
|
3
|
+
export { AgentRegistry } from './agent-registry.js';
|
|
4
|
+
export { loadAgent, loadAgents } from './agent-loader.js';
|
|
5
|
+
export type { AgentManifest, LoadedAgent, A2ACardConfig, MonitorConfig } from './types.js';
|
|
6
|
+
export { AgentManifestSchema, A2ACardSchema } from './manifest-schema.js';
|
|
7
|
+
export { a2aToContentBlocks } from './part-mapper.js';
|
|
8
|
+
export { createMonitorBus, type MonitorEvent } from './monitor-bus.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export { createServer } from './server.js';
|
|
3
|
+
export { AgentRegistry } from './agent-registry.js';
|
|
4
|
+
export { loadAgent, loadAgents } from './agent-loader.js';
|
|
5
|
+
export { AgentManifestSchema, A2ACardSchema } from './manifest-schema.js';
|
|
6
|
+
export { a2aToContentBlocks } from './part-mapper.js';
|
|
7
|
+
export { createMonitorBus } from './monitor-bus.js';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const A2ASkillSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
description: z.ZodString;
|
|
6
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
examples: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
8
|
+
inputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
|
+
outputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export declare const A2ACardSchema: z.ZodObject<{
|
|
12
|
+
provider: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
organization: z.ZodString;
|
|
14
|
+
url: z.ZodString;
|
|
15
|
+
}, z.core.$strip>>;
|
|
16
|
+
capabilities: z.ZodOptional<z.ZodObject<{
|
|
17
|
+
streaming: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
pushNotifications: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
stateTransitionHistory: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$strip>>;
|
|
21
|
+
defaultInputModes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
22
|
+
text: "text";
|
|
23
|
+
file: "file";
|
|
24
|
+
data: "data";
|
|
25
|
+
}>>>;
|
|
26
|
+
defaultOutputModes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
27
|
+
text: "text";
|
|
28
|
+
file: "file";
|
|
29
|
+
"task-status": "task-status";
|
|
30
|
+
}>>>;
|
|
31
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
description: z.ZodString;
|
|
35
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
36
|
+
examples: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
37
|
+
inputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
38
|
+
outputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
}, z.core.$strip>>>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
export declare const AgentManifestSchema: z.ZodObject<{
|
|
42
|
+
id: z.ZodString;
|
|
43
|
+
name: z.ZodString;
|
|
44
|
+
description: z.ZodString;
|
|
45
|
+
version: z.ZodString;
|
|
46
|
+
fixedContext: z.ZodString;
|
|
47
|
+
systemPromptAppend: z.ZodOptional<z.ZodString>;
|
|
48
|
+
model: z.ZodOptional<z.ZodString>;
|
|
49
|
+
fallbackModel: z.ZodOptional<z.ZodString>;
|
|
50
|
+
executable: z.ZodOptional<z.ZodEnum<{
|
|
51
|
+
bun: "bun";
|
|
52
|
+
deno: "deno";
|
|
53
|
+
node: "node";
|
|
54
|
+
}>>;
|
|
55
|
+
executableArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
|
+
pathToClaudeCodeExecutable: z.ZodOptional<z.ZodString>;
|
|
57
|
+
extraArgs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
|
|
58
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
|
|
59
|
+
configDir: z.ZodOptional<z.ZodString>;
|
|
60
|
+
tools: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
61
|
+
type: z.ZodLiteral<"preset">;
|
|
62
|
+
preset: z.ZodLiteral<"claude_code">;
|
|
63
|
+
}, z.core.$strip>]>>;
|
|
64
|
+
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
65
|
+
disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
66
|
+
toolAliases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
67
|
+
permissionMode: z.ZodOptional<z.ZodEnum<{
|
|
68
|
+
default: "default";
|
|
69
|
+
acceptEdits: "acceptEdits";
|
|
70
|
+
bypassPermissions: "bypassPermissions";
|
|
71
|
+
plan: "plan";
|
|
72
|
+
dontAsk: "dontAsk";
|
|
73
|
+
auto: "auto";
|
|
74
|
+
}>>;
|
|
75
|
+
planModeInstructions: z.ZodOptional<z.ZodString>;
|
|
76
|
+
allowDangerouslySkipPermissions: z.ZodOptional<z.ZodBoolean>;
|
|
77
|
+
permissionPromptToolName: z.ZodOptional<z.ZodString>;
|
|
78
|
+
strictMcpConfig: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
additionalDirectories: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
80
|
+
hooks: z.ZodOptional<z.ZodString>;
|
|
81
|
+
includeHookEvents: z.ZodOptional<z.ZodBoolean>;
|
|
82
|
+
mcpServers: z.ZodOptional<z.ZodString>;
|
|
83
|
+
plugins: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
84
|
+
type: z.ZodLiteral<"local">;
|
|
85
|
+
path: z.ZodString;
|
|
86
|
+
}, z.core.$strip>>>;
|
|
87
|
+
skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>>;
|
|
88
|
+
settingSources: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
89
|
+
local: "local";
|
|
90
|
+
user: "user";
|
|
91
|
+
project: "project";
|
|
92
|
+
}>>>;
|
|
93
|
+
settings: z.ZodOptional<z.ZodString>;
|
|
94
|
+
agent: z.ZodOptional<z.ZodString>;
|
|
95
|
+
agents: z.ZodOptional<z.ZodString>;
|
|
96
|
+
agentProgressSummaries: z.ZodOptional<z.ZodBoolean>;
|
|
97
|
+
forwardSubagentText: z.ZodOptional<z.ZodBoolean>;
|
|
98
|
+
thinking: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
99
|
+
type: z.ZodLiteral<"adaptive">;
|
|
100
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
101
|
+
type: z.ZodLiteral<"enabled">;
|
|
102
|
+
budgetTokens: z.ZodNumber;
|
|
103
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
104
|
+
type: z.ZodLiteral<"disabled">;
|
|
105
|
+
}, z.core.$strip>]>>;
|
|
106
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
107
|
+
low: "low";
|
|
108
|
+
medium: "medium";
|
|
109
|
+
high: "high";
|
|
110
|
+
xhigh: "xhigh";
|
|
111
|
+
max: "max";
|
|
112
|
+
}>>;
|
|
113
|
+
maxThinkingTokens: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
115
|
+
maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
taskBudget: z.ZodOptional<z.ZodObject<{
|
|
117
|
+
tokens: z.ZodNumber;
|
|
118
|
+
}, z.core.$strip>>;
|
|
119
|
+
betas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
120
|
+
outputFormat: z.ZodOptional<z.ZodObject<{
|
|
121
|
+
type: z.ZodLiteral<"json_schema">;
|
|
122
|
+
schema: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
123
|
+
}, z.core.$strip>>;
|
|
124
|
+
sandbox: z.ZodOptional<z.ZodString>;
|
|
125
|
+
forkSession: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
+
a2aCard: z.ZodOptional<z.ZodObject<{
|
|
127
|
+
provider: z.ZodOptional<z.ZodObject<{
|
|
128
|
+
organization: z.ZodString;
|
|
129
|
+
url: z.ZodString;
|
|
130
|
+
}, z.core.$strip>>;
|
|
131
|
+
capabilities: z.ZodOptional<z.ZodObject<{
|
|
132
|
+
streaming: z.ZodOptional<z.ZodBoolean>;
|
|
133
|
+
pushNotifications: z.ZodOptional<z.ZodBoolean>;
|
|
134
|
+
stateTransitionHistory: z.ZodOptional<z.ZodBoolean>;
|
|
135
|
+
}, z.core.$strip>>;
|
|
136
|
+
defaultInputModes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
137
|
+
text: "text";
|
|
138
|
+
file: "file";
|
|
139
|
+
data: "data";
|
|
140
|
+
}>>>;
|
|
141
|
+
defaultOutputModes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
142
|
+
text: "text";
|
|
143
|
+
file: "file";
|
|
144
|
+
"task-status": "task-status";
|
|
145
|
+
}>>>;
|
|
146
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
147
|
+
id: z.ZodString;
|
|
148
|
+
name: z.ZodString;
|
|
149
|
+
description: z.ZodString;
|
|
150
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
151
|
+
examples: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
152
|
+
inputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
153
|
+
outputModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
154
|
+
}, z.core.$strip>>>;
|
|
155
|
+
}, z.core.$strip>>;
|
|
156
|
+
monitor: z.ZodOptional<z.ZodObject<{
|
|
157
|
+
position: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
x: z.ZodNumber;
|
|
159
|
+
y: z.ZodNumber;
|
|
160
|
+
}, z.core.$strip>>;
|
|
161
|
+
sprite: z.ZodOptional<z.ZodString>;
|
|
162
|
+
}, z.core.$strip>>;
|
|
163
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const A2ASkillSchema = z.object({
|
|
3
|
+
id: z.string().min(1),
|
|
4
|
+
name: z.string().min(1),
|
|
5
|
+
description: z.string().min(1),
|
|
6
|
+
tags: z.array(z.string()).optional(),
|
|
7
|
+
examples: z.array(z.string()).optional(),
|
|
8
|
+
inputModes: z.array(z.string()).optional(),
|
|
9
|
+
outputModes: z.array(z.string()).optional(),
|
|
10
|
+
});
|
|
11
|
+
export const A2ACardSchema = z.object({
|
|
12
|
+
provider: z.object({ organization: z.string(), url: z.string() }).optional(),
|
|
13
|
+
capabilities: z
|
|
14
|
+
.object({
|
|
15
|
+
streaming: z.boolean().optional(),
|
|
16
|
+
pushNotifications: z.boolean().optional(),
|
|
17
|
+
stateTransitionHistory: z.boolean().optional(),
|
|
18
|
+
})
|
|
19
|
+
.optional(),
|
|
20
|
+
defaultInputModes: z.array(z.enum(['text', 'file', 'data'])).optional(),
|
|
21
|
+
defaultOutputModes: z.array(z.enum(['text', 'file', 'task-status'])).optional(),
|
|
22
|
+
skills: z.array(A2ASkillSchema).optional(),
|
|
23
|
+
});
|
|
24
|
+
export const AgentManifestSchema = z.object({
|
|
25
|
+
id: z.string().min(1),
|
|
26
|
+
name: z.string().min(1),
|
|
27
|
+
description: z.string().min(1),
|
|
28
|
+
version: z
|
|
29
|
+
.string()
|
|
30
|
+
.regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/, 'Must be semver'),
|
|
31
|
+
fixedContext: z.string().min(1),
|
|
32
|
+
systemPromptAppend: z.string().optional(),
|
|
33
|
+
model: z.string().optional(),
|
|
34
|
+
fallbackModel: z.string().optional(),
|
|
35
|
+
executable: z.enum(['bun', 'deno', 'node']).optional(),
|
|
36
|
+
executableArgs: z.array(z.string()).optional(),
|
|
37
|
+
pathToClaudeCodeExecutable: z.string().optional(),
|
|
38
|
+
extraArgs: z.record(z.string(), z.string().nullable()).optional(),
|
|
39
|
+
env: z.record(z.string(), z.string().optional()).optional(),
|
|
40
|
+
configDir: z.string().optional(),
|
|
41
|
+
tools: z
|
|
42
|
+
.union([z.array(z.string()), z.object({ type: z.literal('preset'), preset: z.literal('claude_code') })])
|
|
43
|
+
.optional(),
|
|
44
|
+
allowedTools: z.array(z.string()).optional(),
|
|
45
|
+
disallowedTools: z.array(z.string()).optional(),
|
|
46
|
+
toolAliases: z.record(z.string(), z.string()).optional(),
|
|
47
|
+
permissionMode: z
|
|
48
|
+
.enum(['default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto'])
|
|
49
|
+
.optional(),
|
|
50
|
+
planModeInstructions: z.string().optional(),
|
|
51
|
+
allowDangerouslySkipPermissions: z.boolean().optional(),
|
|
52
|
+
permissionPromptToolName: z.string().optional(),
|
|
53
|
+
strictMcpConfig: z.boolean().optional(),
|
|
54
|
+
additionalDirectories: z.array(z.string()).optional(),
|
|
55
|
+
hooks: z.string().optional(),
|
|
56
|
+
includeHookEvents: z.boolean().optional(),
|
|
57
|
+
mcpServers: z.string().optional(),
|
|
58
|
+
plugins: z
|
|
59
|
+
.array(z.object({
|
|
60
|
+
type: z.literal('local'),
|
|
61
|
+
path: z.string(),
|
|
62
|
+
}))
|
|
63
|
+
.optional(),
|
|
64
|
+
skills: z.union([z.literal('all'), z.array(z.string())]).optional(),
|
|
65
|
+
settingSources: z.array(z.enum(['user', 'project', 'local'])).optional(),
|
|
66
|
+
settings: z.string().optional(),
|
|
67
|
+
agent: z.string().optional(),
|
|
68
|
+
agents: z.string().optional(),
|
|
69
|
+
agentProgressSummaries: z.boolean().optional(),
|
|
70
|
+
forwardSubagentText: z.boolean().optional(),
|
|
71
|
+
thinking: z
|
|
72
|
+
.union([
|
|
73
|
+
z.object({ type: z.literal('adaptive') }),
|
|
74
|
+
z.object({ type: z.literal('enabled'), budgetTokens: z.number() }),
|
|
75
|
+
z.object({ type: z.literal('disabled') }),
|
|
76
|
+
])
|
|
77
|
+
.optional(),
|
|
78
|
+
effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
79
|
+
maxThinkingTokens: z.number().optional(),
|
|
80
|
+
maxTurns: z.number().optional(),
|
|
81
|
+
maxBudgetUsd: z.number().optional(),
|
|
82
|
+
taskBudget: z.object({ tokens: z.number() }).optional(),
|
|
83
|
+
betas: z.array(z.string()).optional(),
|
|
84
|
+
outputFormat: z.object({ type: z.literal('json_schema'), schema: z.record(z.string(), z.any()) }).optional(),
|
|
85
|
+
sandbox: z.string().optional(),
|
|
86
|
+
forkSession: z.boolean().optional(),
|
|
87
|
+
a2aCard: A2ACardSchema.optional(),
|
|
88
|
+
monitor: z
|
|
89
|
+
.object({
|
|
90
|
+
position: z.object({ x: z.number(), y: z.number() }).optional(),
|
|
91
|
+
sprite: z.string().optional(),
|
|
92
|
+
})
|
|
93
|
+
.optional(),
|
|
94
|
+
});
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/metrics.ts
|
|
2
|
+
import { Router } from 'express';
|
|
3
|
+
let tasksTotal = 0;
|
|
4
|
+
let tasksFailed = 0;
|
|
5
|
+
let agentsLoaded = 0;
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
export function recordTaskStart() {
|
|
8
|
+
// Reserved for future use (e.g. active gauge); counters are incremented in recordTaskEnd
|
|
9
|
+
}
|
|
10
|
+
export function recordTaskEnd(success) {
|
|
11
|
+
tasksTotal++;
|
|
12
|
+
if (!success)
|
|
13
|
+
tasksFailed++;
|
|
14
|
+
}
|
|
15
|
+
export function setAgentsLoaded(n) {
|
|
16
|
+
agentsLoaded = n;
|
|
17
|
+
}
|
|
18
|
+
export function createMetricsRouter() {
|
|
19
|
+
const router = Router();
|
|
20
|
+
router.get('/', (_req, res) => {
|
|
21
|
+
res.json({
|
|
22
|
+
agents_loaded: agentsLoaded,
|
|
23
|
+
tasks_total: tasksTotal,
|
|
24
|
+
tasks_failed: tasksFailed,
|
|
25
|
+
uptime_seconds: Math.floor((Date.now() - startTime) / 1000),
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
return router;
|
|
29
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type MonitorEvent = {
|
|
2
|
+
type: 'agent_loaded';
|
|
3
|
+
agentId: string;
|
|
4
|
+
position?: {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
};
|
|
8
|
+
} | {
|
|
9
|
+
type: 'agent_reloaded';
|
|
10
|
+
agentId: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'task_created';
|
|
13
|
+
taskId: string;
|
|
14
|
+
contextId: string;
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
preview: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'task_status';
|
|
20
|
+
taskId: string;
|
|
21
|
+
state: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'tool_call_start';
|
|
25
|
+
taskId: string;
|
|
26
|
+
toolName: string;
|
|
27
|
+
inputSummary: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'tool_call_end';
|
|
30
|
+
taskId: string;
|
|
31
|
+
toolName: string;
|
|
32
|
+
success: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type MonitorSubscriber = (event: MonitorEvent) => void;
|
|
35
|
+
export type Unsubscribe = () => void;
|
|
36
|
+
export interface MonitorBus {
|
|
37
|
+
subscribe(fn: MonitorSubscriber): Unsubscribe;
|
|
38
|
+
emit(event: MonitorEvent): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function createMonitorBus(): MonitorBus;
|