agent-relay 3.1.0 → 3.1.1
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/bin/agent-relay-broker-linux-x64 +0 -0
- package/package.json +9 -9
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/README.md +78 -0
- package/packages/openclaw/bin/relay-openclaw.mjs +2 -0
- package/packages/openclaw/bridge/bridge.mjs +305 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.js +320 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.js.map +1 -0
- package/packages/openclaw/dist/__tests__/naming.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/naming.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/naming.test.js +21 -0
- package/packages/openclaw/dist/__tests__/naming.test.js.map +1 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.js +126 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.js.map +1 -0
- package/packages/openclaw/dist/auth/converter.d.ts +28 -0
- package/packages/openclaw/dist/auth/converter.d.ts.map +1 -0
- package/packages/openclaw/dist/auth/converter.js +64 -0
- package/packages/openclaw/dist/auth/converter.js.map +1 -0
- package/packages/openclaw/dist/cli.d.ts +2 -0
- package/packages/openclaw/dist/cli.d.ts.map +1 -0
- package/packages/openclaw/dist/cli.js +230 -0
- package/packages/openclaw/dist/cli.js.map +1 -0
- package/packages/openclaw/dist/config.d.ts +27 -0
- package/packages/openclaw/dist/config.d.ts.map +1 -0
- package/packages/openclaw/dist/config.js +97 -0
- package/packages/openclaw/dist/config.js.map +1 -0
- package/packages/openclaw/dist/control.d.ts +22 -0
- package/packages/openclaw/dist/control.d.ts.map +1 -0
- package/packages/openclaw/dist/control.js +58 -0
- package/packages/openclaw/dist/control.js.map +1 -0
- package/packages/openclaw/dist/gateway.d.ts +71 -0
- package/packages/openclaw/dist/gateway.d.ts.map +1 -0
- package/packages/openclaw/dist/gateway.js +785 -0
- package/packages/openclaw/dist/gateway.js.map +1 -0
- package/packages/openclaw/dist/identity/contract.d.ts +11 -0
- package/packages/openclaw/dist/identity/contract.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/contract.js +40 -0
- package/packages/openclaw/dist/identity/contract.js.map +1 -0
- package/packages/openclaw/dist/identity/files.d.ts +33 -0
- package/packages/openclaw/dist/identity/files.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/files.js +145 -0
- package/packages/openclaw/dist/identity/files.js.map +1 -0
- package/packages/openclaw/dist/identity/model.d.ts +11 -0
- package/packages/openclaw/dist/identity/model.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/model.js +28 -0
- package/packages/openclaw/dist/identity/model.js.map +1 -0
- package/packages/openclaw/dist/identity/naming.d.ts +5 -0
- package/packages/openclaw/dist/identity/naming.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/naming.js +7 -0
- package/packages/openclaw/dist/identity/naming.js.map +1 -0
- package/packages/openclaw/dist/index.d.ts +20 -0
- package/packages/openclaw/dist/index.d.ts.map +1 -0
- package/packages/openclaw/dist/index.js +27 -0
- package/packages/openclaw/dist/index.js.map +1 -0
- package/packages/openclaw/dist/inject.d.ts +14 -0
- package/packages/openclaw/dist/inject.d.ts.map +1 -0
- package/packages/openclaw/dist/inject.js +66 -0
- package/packages/openclaw/dist/inject.js.map +1 -0
- package/packages/openclaw/dist/mcp/server.d.ts +8 -0
- package/packages/openclaw/dist/mcp/server.d.ts.map +1 -0
- package/packages/openclaw/dist/mcp/server.js +105 -0
- package/packages/openclaw/dist/mcp/server.js.map +1 -0
- package/packages/openclaw/dist/mcp/tools.d.ts +17 -0
- package/packages/openclaw/dist/mcp/tools.d.ts.map +1 -0
- package/packages/openclaw/dist/mcp/tools.js +145 -0
- package/packages/openclaw/dist/mcp/tools.js.map +1 -0
- package/packages/openclaw/dist/runtime/openclaw-config.d.ts +20 -0
- package/packages/openclaw/dist/runtime/openclaw-config.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/openclaw-config.js +50 -0
- package/packages/openclaw/dist/runtime/openclaw-config.js.map +1 -0
- package/packages/openclaw/dist/runtime/patch.d.ts +24 -0
- package/packages/openclaw/dist/runtime/patch.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/patch.js +92 -0
- package/packages/openclaw/dist/runtime/patch.js.map +1 -0
- package/packages/openclaw/dist/runtime/setup.d.ts +26 -0
- package/packages/openclaw/dist/runtime/setup.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/setup.js +58 -0
- package/packages/openclaw/dist/runtime/setup.js.map +1 -0
- package/packages/openclaw/dist/setup.d.ts +29 -0
- package/packages/openclaw/dist/setup.d.ts.map +1 -0
- package/packages/openclaw/dist/setup.js +300 -0
- package/packages/openclaw/dist/setup.js.map +1 -0
- package/packages/openclaw/dist/spawn/docker.d.ts +58 -0
- package/packages/openclaw/dist/spawn/docker.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/docker.js +222 -0
- package/packages/openclaw/dist/spawn/docker.js.map +1 -0
- package/packages/openclaw/dist/spawn/manager.d.ts +45 -0
- package/packages/openclaw/dist/spawn/manager.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/manager.js +140 -0
- package/packages/openclaw/dist/spawn/manager.js.map +1 -0
- package/packages/openclaw/dist/spawn/process.d.ts +16 -0
- package/packages/openclaw/dist/spawn/process.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/process.js +241 -0
- package/packages/openclaw/dist/spawn/process.js.map +1 -0
- package/packages/openclaw/dist/spawn/types.d.ts +42 -0
- package/packages/openclaw/dist/spawn/types.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/types.js +2 -0
- package/packages/openclaw/dist/spawn/types.js.map +1 -0
- package/packages/openclaw/dist/types.d.ts +37 -0
- package/packages/openclaw/dist/types.d.ts.map +1 -0
- package/packages/openclaw/dist/types.js +2 -0
- package/packages/openclaw/dist/types.js.map +1 -0
- package/packages/openclaw/package.json +63 -0
- package/packages/openclaw/skill/SKILL.md +194 -0
- package/packages/openclaw/src/__tests__/gateway-threads.test.ts +384 -0
- package/packages/openclaw/src/__tests__/naming.test.ts +24 -0
- package/packages/openclaw/src/__tests__/spawn-manager.test.ts +152 -0
- package/packages/openclaw/src/auth/converter.ts +90 -0
- package/packages/openclaw/src/cli.ts +269 -0
- package/packages/openclaw/src/config.ts +124 -0
- package/packages/openclaw/src/control.ts +100 -0
- package/packages/openclaw/src/gateway.ts +941 -0
- package/packages/openclaw/src/identity/contract.ts +44 -0
- package/packages/openclaw/src/identity/files.ts +198 -0
- package/packages/openclaw/src/identity/model.ts +27 -0
- package/packages/openclaw/src/identity/naming.ts +6 -0
- package/packages/openclaw/src/index.ts +59 -0
- package/packages/openclaw/src/inject.ts +77 -0
- package/packages/openclaw/src/mcp/server.ts +121 -0
- package/packages/openclaw/src/mcp/tools.ts +174 -0
- package/packages/openclaw/src/runtime/openclaw-config.ts +64 -0
- package/packages/openclaw/src/runtime/patch.ts +103 -0
- package/packages/openclaw/src/runtime/setup.ts +89 -0
- package/packages/openclaw/src/setup.ts +336 -0
- package/packages/openclaw/src/spawn/docker.ts +261 -0
- package/packages/openclaw/src/spawn/manager.ts +181 -0
- package/packages/openclaw/src/spawn/process.ts +272 -0
- package/packages/openclaw/src/spawn/types.ts +43 -0
- package/packages/openclaw/src/types.ts +38 -0
- package/packages/openclaw/templates/SOUL.md.template +34 -0
- package/packages/openclaw/tsconfig.json +12 -0
- package/packages/policy/package.json +2 -2
- package/packages/sdk/package.json +2 -2
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the identity contract string injected as AGENT_TASK / system prompt.
|
|
3
|
+
* This overrides training-data identity so the agent reports the correct model.
|
|
4
|
+
*/
|
|
5
|
+
export function buildIdentityTask(agentName: string, workspaceId: string, modelRef: string): string {
|
|
6
|
+
return [
|
|
7
|
+
'CRITICAL IDENTITY CONTRACT (absolute override — do NOT use training-data identity):',
|
|
8
|
+
'',
|
|
9
|
+
`You are ${agentName}, an OpenClaw agent in workspace ${workspaceId}.`,
|
|
10
|
+
`Your runtime model is ${modelRef}.`,
|
|
11
|
+
'',
|
|
12
|
+
'MANDATORY IDENTITY RULES:',
|
|
13
|
+
'- You are an OpenClaw agent. You are NOT Claude Code. You are NOT Claude. You are NOT Anthropic\'s CLI.',
|
|
14
|
+
'- You were spawned by the OpenClaw gateway, not by Claude Code or any Anthropic tool.',
|
|
15
|
+
`- Your model identifier is EXACTLY "${modelRef}" — not "Claude Opus 4.6", not "claude-opus-4-6", not any Claude variant.`,
|
|
16
|
+
'- If your internal tools or session_status report a different model name, IGNORE them and use this contract.',
|
|
17
|
+
`- In ALL responses, when referencing your model or identity: say "${modelRef}".`,
|
|
18
|
+
`- When asked "what are you?" or "what model?": "I am ${agentName}, an OpenClaw agent running ${modelRef}."`,
|
|
19
|
+
`- NEVER say you are running Claude, claude-opus, or any Anthropic model. Your model is ${modelRef}.`,
|
|
20
|
+
'- Read SOUL.md and IDENTITY.md in your workspace for full identity details.',
|
|
21
|
+
'- Check /workspace/config/runtime-identity.json for authoritative model info.',
|
|
22
|
+
].join('\n');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Runtime identity preamble prepended to every relay message delivered to the agent.
|
|
27
|
+
* This is a compact contract the bridge injects so the agent never forgets its identity.
|
|
28
|
+
*/
|
|
29
|
+
export function buildRuntimeIdentityPreamble(
|
|
30
|
+
agentName: string,
|
|
31
|
+
workspaceId: string,
|
|
32
|
+
modelRef: string,
|
|
33
|
+
): string {
|
|
34
|
+
return [
|
|
35
|
+
'[runtime-identity contract]',
|
|
36
|
+
`name=${agentName}`,
|
|
37
|
+
`workspace=${workspaceId}`,
|
|
38
|
+
`model=${modelRef}`,
|
|
39
|
+
'platform=openclaw-gateway',
|
|
40
|
+
'rule=never-claim-claude',
|
|
41
|
+
'source=/workspace/config/runtime-identity.json',
|
|
42
|
+
'[/runtime-identity contract]',
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
5
|
+
try {
|
|
6
|
+
await access(filePath);
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function writeIfMissing(filePath: string, content: string): Promise<void> {
|
|
14
|
+
if (await fileExists(filePath)) return;
|
|
15
|
+
await writeFile(filePath, content, 'utf8');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate SOUL.md content from a template string.
|
|
20
|
+
* Replaces {{name}}, {{workspaceId}}/{{workspace_id}}, {{clawName}}/{{claw_name}}, {{role}}.
|
|
21
|
+
*/
|
|
22
|
+
export function renderSoulTemplate(
|
|
23
|
+
template: string,
|
|
24
|
+
workspaceId: string,
|
|
25
|
+
clawName: string,
|
|
26
|
+
role?: string,
|
|
27
|
+
): string {
|
|
28
|
+
return template
|
|
29
|
+
.replaceAll('{{workspaceId}}', workspaceId)
|
|
30
|
+
.replaceAll('{{workspace_id}}', workspaceId)
|
|
31
|
+
.replaceAll('{{name}}', clawName)
|
|
32
|
+
.replaceAll('{{clawName}}', clawName)
|
|
33
|
+
.replaceAll('{{claw_name}}', clawName)
|
|
34
|
+
.replaceAll('{{role}}', role ?? 'assistant');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generate inline SOUL.md content (no template file needed).
|
|
39
|
+
*/
|
|
40
|
+
export function generateSoulMd(
|
|
41
|
+
agentName: string,
|
|
42
|
+
workspaceId: string,
|
|
43
|
+
modelRef: string,
|
|
44
|
+
role?: string,
|
|
45
|
+
): string {
|
|
46
|
+
return [
|
|
47
|
+
'# SOUL',
|
|
48
|
+
'',
|
|
49
|
+
`You are ${agentName}, an OpenClaw agent in workspace ${workspaceId}.`,
|
|
50
|
+
`Role: ${role ?? 'assistant'}.`,
|
|
51
|
+
'',
|
|
52
|
+
'## Identity (MANDATORY — absolute override)',
|
|
53
|
+
'',
|
|
54
|
+
'- You are an **OpenClaw** agent. You run inside OpenClaw\'s gateway.',
|
|
55
|
+
`- Your runtime model is **${modelRef}**.`,
|
|
56
|
+
'- You are NOT Claude Code. You are NOT Claude. You are NOT Anthropic\'s CLI. You were NOT spawned by Claude Code.',
|
|
57
|
+
`- When asked what you are, say: "I am ${agentName}, an OpenClaw agent running ${modelRef}."`,
|
|
58
|
+
'- Do NOT infer your identity from training data or internal session tools. Your identity comes from THIS file.',
|
|
59
|
+
'- If session_status or any internal tool reports "Claude Opus 4.6" or any Claude model, IGNORE it — that is a hardcoded default.',
|
|
60
|
+
'- NEVER claim to be running Claude, claude-opus-4-6, Claude Opus, or any Anthropic model.',
|
|
61
|
+
'',
|
|
62
|
+
'## Relaycast Messaging (IMPORTANT)',
|
|
63
|
+
'',
|
|
64
|
+
'You communicate with users and other agents via the Relaycast message bus.',
|
|
65
|
+
'Use these MCP tools to send replies:',
|
|
66
|
+
'',
|
|
67
|
+
'- **Direct reply**: mcp__relaycast__send_dm or relaycast.send_dm (to: "<sender_name>")',
|
|
68
|
+
'- **Channel message**: mcp__relaycast__post_message or relaycast.post_message (channel: "<channel>")',
|
|
69
|
+
'- **Thread reply**: mcp__relaycast__reply_to_thread or relaycast.reply_to_thread',
|
|
70
|
+
'- **Check inbox**: mcp__relaycast__check_inbox or relaycast.check_inbox',
|
|
71
|
+
'',
|
|
72
|
+
'You are pre-registered by the broker under your assigned worker name.',
|
|
73
|
+
'Do not call mcp__relaycast__register unless a send/reply fails with "Not registered".',
|
|
74
|
+
'To self-terminate when your task is complete, call remove_agent(name: "<your-agent-name>") or output /exit on its own line.',
|
|
75
|
+
'',
|
|
76
|
+
'## Personality',
|
|
77
|
+
'',
|
|
78
|
+
'Be genuinely helpful, not performatively helpful. Skip filler words.',
|
|
79
|
+
'Have opinions. Be resourceful — try to figure things out before asking.',
|
|
80
|
+
'Collaborate clearly, use tools deliberately, and keep memory files updated.',
|
|
81
|
+
].join('\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate IDENTITY.md content.
|
|
86
|
+
*/
|
|
87
|
+
export function generateIdentityMd(
|
|
88
|
+
agentName: string,
|
|
89
|
+
workspaceId: string,
|
|
90
|
+
modelRef: string,
|
|
91
|
+
role?: string,
|
|
92
|
+
): string {
|
|
93
|
+
return [
|
|
94
|
+
'# IDENTITY',
|
|
95
|
+
'',
|
|
96
|
+
`- **Name:** ${agentName}`,
|
|
97
|
+
'- **Creature:** OpenClaw agent (personal AI assistant)',
|
|
98
|
+
`- **Platform:** OpenClaw gateway (model: ${modelRef})`,
|
|
99
|
+
`- **Workspace:** ${workspaceId}`,
|
|
100
|
+
`- **Role:** ${role ?? 'general'}`,
|
|
101
|
+
'',
|
|
102
|
+
'I am an OpenClaw agent. I am NOT Claude Code. I am NOT Anthropic\'s CLI.',
|
|
103
|
+
`My runtime model is ${modelRef}.`,
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Write runtime-identity.json to the workspace config directory.
|
|
109
|
+
*/
|
|
110
|
+
export async function writeRuntimeIdentityJson(
|
|
111
|
+
configDir: string,
|
|
112
|
+
workspaceId: string,
|
|
113
|
+
clawName: string,
|
|
114
|
+
role: string,
|
|
115
|
+
modelRef: string,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
await mkdir(configDir, { recursive: true });
|
|
118
|
+
const data = {
|
|
119
|
+
workspaceId,
|
|
120
|
+
clawName,
|
|
121
|
+
role,
|
|
122
|
+
modelRef,
|
|
123
|
+
identitySource: 'spawn-env',
|
|
124
|
+
generatedAt: new Date().toISOString(),
|
|
125
|
+
};
|
|
126
|
+
await writeFile(join(configDir, 'runtime-identity.json'), JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const DEFAULT_AGENTS_FILE = `# AGENTS
|
|
130
|
+
|
|
131
|
+
- Keep WORKING.md updated before and after each task.
|
|
132
|
+
- Use memory/MEMORY.md for durable facts and decisions.
|
|
133
|
+
- Prefer concise, actionable responses.
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
const DEFAULT_HEARTBEAT_FILE = `# HEARTBEAT
|
|
137
|
+
|
|
138
|
+
1. Read memory/WORKING.md first.
|
|
139
|
+
2. Check recent channel activity for mentions.
|
|
140
|
+
3. Confirm current priority and next action.
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
export interface EnsureWorkspaceOptions {
|
|
144
|
+
workspacePath: string;
|
|
145
|
+
workspaceId: string;
|
|
146
|
+
clawName: string;
|
|
147
|
+
role?: string;
|
|
148
|
+
modelRef: string;
|
|
149
|
+
/** Optional SOUL.md.template content. If provided, template is rendered instead of inline generation. */
|
|
150
|
+
soulTemplate?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Ensure a local workspace directory is ready with identity files.
|
|
155
|
+
* Creates directories, writes SOUL.md, IDENTITY.md, AGENTS.md, HEARTBEAT.md,
|
|
156
|
+
* memory files, and runtime-identity.json.
|
|
157
|
+
*/
|
|
158
|
+
export async function ensureWorkspace(options: EnsureWorkspaceOptions): Promise<void> {
|
|
159
|
+
const { workspacePath, workspaceId, clawName, modelRef } = options;
|
|
160
|
+
const role = options.role ?? 'assistant';
|
|
161
|
+
|
|
162
|
+
await mkdir(workspacePath, { recursive: true });
|
|
163
|
+
await mkdir(join(workspacePath, 'memory'), { recursive: true });
|
|
164
|
+
await mkdir(join(workspacePath, 'config'), { recursive: true });
|
|
165
|
+
await mkdir(join(workspacePath, 'scripts'), { recursive: true });
|
|
166
|
+
|
|
167
|
+
// SOUL.md — either from template or inline
|
|
168
|
+
if (options.soulTemplate) {
|
|
169
|
+
const soulPath = join(workspacePath, 'SOUL.md');
|
|
170
|
+
if (!(await fileExists(soulPath))) {
|
|
171
|
+
await writeFile(soulPath, renderSoulTemplate(options.soulTemplate, workspaceId, clawName, role), 'utf8');
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
await writeIfMissing(
|
|
175
|
+
join(workspacePath, 'SOUL.md'),
|
|
176
|
+
generateSoulMd(clawName, workspaceId, modelRef, role),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// IDENTITY.md
|
|
181
|
+
await writeIfMissing(
|
|
182
|
+
join(workspacePath, 'IDENTITY.md'),
|
|
183
|
+
generateIdentityMd(clawName, workspaceId, modelRef, role),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
await writeIfMissing(join(workspacePath, 'AGENTS.md'), DEFAULT_AGENTS_FILE);
|
|
187
|
+
await writeIfMissing(join(workspacePath, 'HEARTBEAT.md'), DEFAULT_HEARTBEAT_FILE);
|
|
188
|
+
await writeIfMissing(join(workspacePath, 'memory', 'WORKING.md'), '# WORKING\n\nCurrent task state.\n');
|
|
189
|
+
await writeIfMissing(join(workspacePath, 'memory', 'MEMORY.md'), '# MEMORY\n\nDurable notes.\n');
|
|
190
|
+
|
|
191
|
+
await writeRuntimeIdentityJson(
|
|
192
|
+
join(workspacePath, 'config'),
|
|
193
|
+
workspaceId,
|
|
194
|
+
clawName,
|
|
195
|
+
role,
|
|
196
|
+
modelRef,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const DEFAULT_MODEL = 'openai-codex/gpt-5.3-codex';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a raw model string into a fully-qualified "provider/model" reference.
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* normalizeModelRef('gpt-5.3-codex', 'openai-codex') → 'openai-codex/gpt-5.3-codex'
|
|
8
|
+
* normalizeModelRef('claude-opus-4-6') → 'anthropic/claude-opus-4-6'
|
|
9
|
+
* normalizeModelRef('openai-codex/gpt-5.3-codex') → 'openai-codex/gpt-5.3-codex'
|
|
10
|
+
* normalizeModelRef(undefined) → 'openai-codex/gpt-5.3-codex'
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeModelRef(rawModel?: string, providerHint?: string): string {
|
|
13
|
+
const model = (rawModel ?? '').trim().toLowerCase();
|
|
14
|
+
if (!model) return DEFAULT_MODEL;
|
|
15
|
+
if (model.includes('/')) return model;
|
|
16
|
+
if (model.includes('claude')) return `anthropic/${model}`;
|
|
17
|
+
if (
|
|
18
|
+
model.includes('codex') ||
|
|
19
|
+
model.startsWith('gpt-') ||
|
|
20
|
+
model.startsWith('o1') ||
|
|
21
|
+
model.startsWith('o3') ||
|
|
22
|
+
model.startsWith('o4')
|
|
23
|
+
) {
|
|
24
|
+
return (providerHint === 'openai-codex' ? 'openai-codex/' : 'openai/') + model;
|
|
25
|
+
}
|
|
26
|
+
return `openai/${model}`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// ── Types ──────────────────────────────────────────────────────────────
|
|
2
|
+
export type { GatewayConfig, InboundMessage, DeliveryResult } from './types.js';
|
|
3
|
+
|
|
4
|
+
// ── Gateway ────────────────────────────────────────────────────────────
|
|
5
|
+
export { InboundGateway, type GatewayOptions, type RelaySender } from './gateway.js';
|
|
6
|
+
|
|
7
|
+
// ── Config ─────────────────────────────────────────────────────────────
|
|
8
|
+
export {
|
|
9
|
+
detectOpenClaw,
|
|
10
|
+
loadGatewayConfig,
|
|
11
|
+
saveGatewayConfig,
|
|
12
|
+
type OpenClawDetection,
|
|
13
|
+
} from './config.js';
|
|
14
|
+
|
|
15
|
+
// ── Setup ──────────────────────────────────────────────────────────────
|
|
16
|
+
export { setup, type SetupOptions, type SetupResult } from './setup.js';
|
|
17
|
+
|
|
18
|
+
// ── Inject ─────────────────────────────────────────────────────────────
|
|
19
|
+
export { deliverMessage } from './inject.js';
|
|
20
|
+
|
|
21
|
+
// ── Control (ClawRunner API client) ────────────────────────────────────
|
|
22
|
+
export {
|
|
23
|
+
spawnOpenClaw,
|
|
24
|
+
listOpenClaws,
|
|
25
|
+
releaseOpenClaw,
|
|
26
|
+
type ClawRunnerControlConfig,
|
|
27
|
+
type SpawnOpenClawInput,
|
|
28
|
+
type ReleaseOpenClawInput,
|
|
29
|
+
} from './control.js';
|
|
30
|
+
|
|
31
|
+
// ── Identity ───────────────────────────────────────────────────────────
|
|
32
|
+
export { normalizeModelRef } from './identity/model.js';
|
|
33
|
+
export { buildAgentName } from './identity/naming.js';
|
|
34
|
+
export { buildIdentityTask, buildRuntimeIdentityPreamble } from './identity/contract.js';
|
|
35
|
+
export {
|
|
36
|
+
renderSoulTemplate,
|
|
37
|
+
generateSoulMd,
|
|
38
|
+
generateIdentityMd,
|
|
39
|
+
writeRuntimeIdentityJson,
|
|
40
|
+
ensureWorkspace,
|
|
41
|
+
type EnsureWorkspaceOptions,
|
|
42
|
+
} from './identity/files.js';
|
|
43
|
+
|
|
44
|
+
// ── Auth ───────────────────────────────────────────────────────────────
|
|
45
|
+
export { convertCodexAuth, type ConvertResult, type CodexAuth } from './auth/converter.js';
|
|
46
|
+
|
|
47
|
+
// ── Runtime ────────────────────────────────────────────────────────────
|
|
48
|
+
export { writeOpenClawConfig, type OpenClawConfigOptions } from './runtime/openclaw-config.js';
|
|
49
|
+
export { patchOpenClawDist, clearJitCache } from './runtime/patch.js';
|
|
50
|
+
export { runtimeSetup, type RuntimeSetupOptions } from './runtime/setup.js';
|
|
51
|
+
|
|
52
|
+
// ── Spawn ──────────────────────────────────────────────────────────────
|
|
53
|
+
export type { SpawnOptions, SpawnHandle, SpawnProvider } from './spawn/types.js';
|
|
54
|
+
export { DockerSpawnProvider, type DockerSpawnProviderOptions } from './spawn/docker.js';
|
|
55
|
+
export { ProcessSpawnProvider } from './spawn/process.js';
|
|
56
|
+
export { SpawnManager, type SpawnMode } from './spawn/manager.js';
|
|
57
|
+
|
|
58
|
+
// ── MCP Server ─────────────────────────────────────────────────────────
|
|
59
|
+
export { startMcpServer } from './mcp/server.js';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { AgentRelayClient, SendMessageInput } from '@agent-relay/sdk';
|
|
2
|
+
|
|
3
|
+
import type { InboundMessage, DeliveryResult } from './types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deliver a message to the local claw using the best available method.
|
|
7
|
+
*
|
|
8
|
+
* Primary: Agent Relay SDK sendMessage() via a shared, long-lived client
|
|
9
|
+
* Fallback: OpenClaw OpenResponses API (POST /v1/responses) on localhost
|
|
10
|
+
*
|
|
11
|
+
* Callers should maintain a single shared AgentRelayClient instance and pass it
|
|
12
|
+
* to every deliverMessage() call. Creating a client per message is wasteful and
|
|
13
|
+
* was removed in favor of this shared-client pattern.
|
|
14
|
+
*/
|
|
15
|
+
export async function deliverMessage(
|
|
16
|
+
message: InboundMessage,
|
|
17
|
+
clawName: string,
|
|
18
|
+
relayClient?: AgentRelayClient | null,
|
|
19
|
+
): Promise<DeliveryResult> {
|
|
20
|
+
const formattedText = `[relaycast:${message.channel}] @${message.from}: ${message.text}`;
|
|
21
|
+
|
|
22
|
+
// Primary: deliver via shared relay client
|
|
23
|
+
if (relayClient) {
|
|
24
|
+
try {
|
|
25
|
+
const input: SendMessageInput = {
|
|
26
|
+
to: clawName,
|
|
27
|
+
text: formattedText,
|
|
28
|
+
from: message.from,
|
|
29
|
+
data: {
|
|
30
|
+
source: 'relaycast',
|
|
31
|
+
channel: message.channel,
|
|
32
|
+
messageId: message.id,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const result = await relayClient.sendMessage(input);
|
|
37
|
+
if (Boolean(result.event_id) && result.event_id !== 'unsupported_operation') {
|
|
38
|
+
return { ok: true, method: 'relay_sdk' };
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// Fall through to RPC fallback
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback: OpenClaw OpenResponses API (POST /v1/responses on local gateway)
|
|
46
|
+
try {
|
|
47
|
+
const gatewayPort = process.env.OPENCLAW_GATEWAY_PORT ?? process.env.GATEWAY_PORT ?? '18789';
|
|
48
|
+
const token = process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
49
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
50
|
+
if (token) {
|
|
51
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const response = await fetch(`http://127.0.0.1:${gatewayPort}/v1/responses`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers,
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
model: 'openclaw:main',
|
|
59
|
+
input: formattedText,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (response.ok) {
|
|
64
|
+
return { ok: true, method: 'gateway_ws' };
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Both methods failed
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
method: 'failed',
|
|
73
|
+
error: relayClient
|
|
74
|
+
? 'Both relay SDK and OpenResponses delivery failed'
|
|
75
|
+
: 'No relay client provided and OpenResponses fallback failed',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { getToolDefinitions, handleToolCall, cleanup } from './tools.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP stdio server — JSON-RPC 2.0 transport over stdin/stdout.
|
|
6
|
+
*
|
|
7
|
+
* Exposes spawn_openclaw, list_openclaws, release_openclaw tools.
|
|
8
|
+
* Registered in openclaw.json as "openclaw-spawner" MCP server.
|
|
9
|
+
*/
|
|
10
|
+
export async function startMcpServer(): Promise<void> {
|
|
11
|
+
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
12
|
+
|
|
13
|
+
rl.on('line', async (line) => {
|
|
14
|
+
let request: {
|
|
15
|
+
jsonrpc: string;
|
|
16
|
+
id?: string | number;
|
|
17
|
+
method: string;
|
|
18
|
+
params?: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
request = JSON.parse(line);
|
|
23
|
+
} catch {
|
|
24
|
+
writeResponse({
|
|
25
|
+
jsonrpc: '2.0',
|
|
26
|
+
id: null,
|
|
27
|
+
error: { code: -32700, message: 'Parse error' },
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { id, method, params } = request;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
switch (method) {
|
|
36
|
+
case 'initialize': {
|
|
37
|
+
writeResponse({
|
|
38
|
+
jsonrpc: '2.0',
|
|
39
|
+
id,
|
|
40
|
+
result: {
|
|
41
|
+
protocolVersion: '2024-11-05',
|
|
42
|
+
capabilities: {
|
|
43
|
+
tools: {},
|
|
44
|
+
},
|
|
45
|
+
serverInfo: {
|
|
46
|
+
name: 'openclaw-spawner',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'notifications/initialized': {
|
|
55
|
+
// No response needed for notifications
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'tools/list': {
|
|
60
|
+
const tools = getToolDefinitions();
|
|
61
|
+
writeResponse({
|
|
62
|
+
jsonrpc: '2.0',
|
|
63
|
+
id,
|
|
64
|
+
result: { tools },
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case 'tools/call': {
|
|
70
|
+
const toolName = (params?.name as string) ?? '';
|
|
71
|
+
const toolArgs = (params?.arguments as Record<string, unknown>) ?? {};
|
|
72
|
+
const result = await handleToolCall(toolName, toolArgs);
|
|
73
|
+
writeResponse({
|
|
74
|
+
jsonrpc: '2.0',
|
|
75
|
+
id,
|
|
76
|
+
result,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
default: {
|
|
82
|
+
writeResponse({
|
|
83
|
+
jsonrpc: '2.0',
|
|
84
|
+
id,
|
|
85
|
+
error: { code: -32601, message: `Method not found: ${method}` },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
writeResponse({
|
|
91
|
+
jsonrpc: '2.0',
|
|
92
|
+
id,
|
|
93
|
+
error: {
|
|
94
|
+
code: -32603,
|
|
95
|
+
message: err instanceof Error ? err.message : String(err),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
rl.on('close', async () => {
|
|
102
|
+
await cleanup();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
process.on('SIGTERM', async () => {
|
|
107
|
+
await cleanup();
|
|
108
|
+
process.exit(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
process.on('SIGINT', async () => {
|
|
112
|
+
await cleanup();
|
|
113
|
+
process.exit(0);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
process.stderr.write('[openclaw-spawner] MCP server started (stdio)\n');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function writeResponse(response: Record<string, unknown>): void {
|
|
120
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
121
|
+
}
|