@virtue-ai/gateway-connect 0.3.0 → 0.3.2
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 +2 -8
- package/dist/index.js +11 -3
- package/dist/trajectory-plugin.js +33 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,12 +26,6 @@ openclaw models auth paste-token --provider openai
|
|
|
26
26
|
npx @virtue-ai/gateway-connect --gateway-url https://virtueai-agent-gtw-xxxx.ngrok.io
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
To use a specific model:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npx @virtue-ai/gateway-connect --gateway-url https://virtueai-agent-gtw-xxxx.ngrok.io --model openai/gpt-4o
|
|
33
|
-
```
|
|
34
|
-
|
|
35
29
|
This will:
|
|
36
30
|
|
|
37
31
|
1. Open your browser for OAuth login
|
|
@@ -52,7 +46,7 @@ Interactive TUI mode:
|
|
|
52
46
|
|
|
53
47
|
```bash
|
|
54
48
|
# Terminal 1: start the OpenClaw gateway
|
|
55
|
-
openclaw gateway
|
|
49
|
+
openclaw gateway --allow-unconfigured
|
|
56
50
|
|
|
57
51
|
# Terminal 2: open the TUI
|
|
58
52
|
openclaw tui
|
|
@@ -69,7 +63,7 @@ openclaw gateway stop
|
|
|
69
63
|
In TUI mode, use slash commands to switch models on the fly:
|
|
70
64
|
|
|
71
65
|
```
|
|
72
|
-
/model openai/gpt-
|
|
66
|
+
/model openai/gpt-5.2
|
|
73
67
|
/model anthropic/claude-opus-4-6
|
|
74
68
|
/models # opens model picker
|
|
75
69
|
```
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,8 @@ const OPENCLAW_CONFIG_PATH = path.join(OPENCLAW_DIR, 'openclaw.json');
|
|
|
34
34
|
const TOOLS_PLUGIN_ID = 'virtueai-mcp-tools';
|
|
35
35
|
const TOOLS_PLUGIN_DIR = path.join(OPENCLAW_DIR, 'extensions', TOOLS_PLUGIN_ID);
|
|
36
36
|
const DEFAULT_GATEWAY_URL = 'https://virtueai-agent-gtw-l3phon63.ngrok.io';
|
|
37
|
+
const DEFAULT_API_URL = 'https://agentgateway1.virtueai.io';
|
|
38
|
+
const DEFAULT_GATEWAY_ID = 'gtw_L3pHOn63';
|
|
37
39
|
// ---------------------------------------------------------------------------
|
|
38
40
|
// Helpers
|
|
39
41
|
// ---------------------------------------------------------------------------
|
|
@@ -340,11 +342,13 @@ ${toolRegistrations.join('\n')}
|
|
|
340
342
|
// ---------------------------------------------------------------------------
|
|
341
343
|
// Step 3: Write config & patch openclaw.json
|
|
342
344
|
// ---------------------------------------------------------------------------
|
|
343
|
-
function writeGatewayConfig(gatewayUrl, accessToken, guardUuid) {
|
|
345
|
+
function writeGatewayConfig(gatewayUrl, accessToken, guardUuid, apiUrl, gatewayId) {
|
|
344
346
|
fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
|
|
345
347
|
const config = {
|
|
346
348
|
trajectory: {
|
|
347
349
|
gatewayUrl,
|
|
350
|
+
apiUrl: apiUrl || DEFAULT_API_URL,
|
|
351
|
+
gatewayId: gatewayId || DEFAULT_GATEWAY_ID,
|
|
348
352
|
guardUuid: guardUuid || process.env.VIRTUEAI_GUARD_UUID || '',
|
|
349
353
|
},
|
|
350
354
|
_auth: {
|
|
@@ -435,7 +439,9 @@ Usage:
|
|
|
435
439
|
npx @virtue-ai/gateway-connect [options]
|
|
436
440
|
|
|
437
441
|
Options:
|
|
438
|
-
--gateway-url <url>
|
|
442
|
+
--gateway-url <url> MCP gateway URL (default: ${DEFAULT_GATEWAY_URL})
|
|
443
|
+
--api-url <url> Prompt-guard API URL (default: ${DEFAULT_API_URL})
|
|
444
|
+
--gateway-id <id> Gateway ID for trajectory (default: ${DEFAULT_GATEWAY_ID})
|
|
439
445
|
--model <model> Model to use (e.g. openai/gpt-4o, anthropic/claude-sonnet-4-5)
|
|
440
446
|
--guard-uuid <uuid> Guard UUID for trajectory recording (or set VIRTUEAI_GUARD_UUID)
|
|
441
447
|
--help Show this help message
|
|
@@ -456,6 +462,8 @@ Supported models:
|
|
|
456
462
|
let gatewayUrl = getArg('gateway-url') || DEFAULT_GATEWAY_URL;
|
|
457
463
|
const model = getArg('model');
|
|
458
464
|
const guardUuid = getArg('guard-uuid') || process.env.VIRTUEAI_GUARD_UUID;
|
|
465
|
+
const apiUrl = getArg('api-url') || DEFAULT_API_URL;
|
|
466
|
+
const gatewayId = getArg('gateway-id') || DEFAULT_GATEWAY_ID;
|
|
459
467
|
gatewayUrl = gatewayUrl.replace(/\/mcp\/?$/, '').toLowerCase();
|
|
460
468
|
console.log('\n VirtueAI Gateway Connect\n');
|
|
461
469
|
console.log(` Gateway: ${gatewayUrl}`);
|
|
@@ -472,7 +480,7 @@ Supported models:
|
|
|
472
480
|
}
|
|
473
481
|
generateMcpToolsPlugin(gatewayUrl, accessToken, tools);
|
|
474
482
|
// Step 3: Write gateway config (for trajectory plugin)
|
|
475
|
-
writeGatewayConfig(gatewayUrl, accessToken, guardUuid);
|
|
483
|
+
writeGatewayConfig(gatewayUrl, accessToken, guardUuid, apiUrl, gatewayId);
|
|
476
484
|
// Step 4: Patch openclaw.json (enable tools plugin + set model)
|
|
477
485
|
patchOpenClawConfig(model);
|
|
478
486
|
// Step 5: Install trajectory plugin
|
|
@@ -49,6 +49,8 @@ function loadConfig() {
|
|
|
49
49
|
const cfg = JSON.parse(raw);
|
|
50
50
|
|
|
51
51
|
const gatewayUrl = cfg._auth?.gatewayUrl ?? cfg.trajectory?.gatewayUrl ?? "";
|
|
52
|
+
const apiUrl = cfg.trajectory?.apiUrl ?? gatewayUrl;
|
|
53
|
+
const gatewayId = cfg.trajectory?.gatewayId ?? "";
|
|
52
54
|
const token = cfg._auth?.accessToken ?? "";
|
|
53
55
|
|
|
54
56
|
const guardUuid =
|
|
@@ -56,8 +58,8 @@ function loadConfig() {
|
|
|
56
58
|
process.env.VIRTUEAI_GUARD_UUID ||
|
|
57
59
|
DEFAULT_GUARD_UUID;
|
|
58
60
|
|
|
59
|
-
if (!
|
|
60
|
-
return { gatewayUrl, token, guardUuid };
|
|
61
|
+
if (!apiUrl || !token) return null;
|
|
62
|
+
return { gatewayUrl, apiUrl, gatewayId, token, guardUuid };
|
|
61
63
|
} catch {
|
|
62
64
|
return null;
|
|
63
65
|
}
|
|
@@ -70,6 +72,19 @@ function truncate(s, max = 2000) {
|
|
|
70
72
|
return s.length > max ? s.slice(0, max) + "..." : s;
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Strip OpenClaw's sender metadata prefix from the raw prompt.
|
|
77
|
+
* The prompt arrives as:
|
|
78
|
+
* Sender (untrusted metadata):\\n{json}\\n\\n[timestamp] actual message
|
|
79
|
+
* We want just "actual message".
|
|
80
|
+
*/
|
|
81
|
+
function stripSenderMetadata(prompt) {
|
|
82
|
+
if (!prompt || typeof prompt !== "string") return prompt;
|
|
83
|
+
// Match the "Sender (untrusted metadata):" block + JSON + timestamp prefix
|
|
84
|
+
const match = prompt.match(/^Sender \\(untrusted metadata\\):[\\s\\S]*?\\n\\n(?:\\[.*?\\]\\s*)?(.*)$/s);
|
|
85
|
+
return match ? match[1].trim() : prompt.trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
73
88
|
const plugin = {
|
|
74
89
|
id: "${PLUGIN_ID}",
|
|
75
90
|
name: "VirtueAI Trajectory",
|
|
@@ -84,7 +99,7 @@ const plugin = {
|
|
|
84
99
|
|
|
85
100
|
let gatewaySessionId = null;
|
|
86
101
|
let endpointDisabled = false;
|
|
87
|
-
const endpoint = config.
|
|
102
|
+
const endpoint = config.apiUrl + "/api/prompt-guard/topic_guard";
|
|
88
103
|
|
|
89
104
|
api.logger.info("[virtueai-trajectory] Plugin registered, sending to " + config.gatewayUrl);
|
|
90
105
|
|
|
@@ -94,6 +109,7 @@ const plugin = {
|
|
|
94
109
|
const body = {
|
|
95
110
|
user_prompt: truncate(content),
|
|
96
111
|
guard_uuid: config.guardUuid,
|
|
112
|
+
gateway_id: config.gatewayId,
|
|
97
113
|
role,
|
|
98
114
|
};
|
|
99
115
|
if (gatewaySessionId) {
|
|
@@ -130,34 +146,40 @@ const plugin = {
|
|
|
130
146
|
}
|
|
131
147
|
}
|
|
132
148
|
|
|
133
|
-
// Hook: user prompt sent to LLM
|
|
134
149
|
api.on("llm_input", (event) => {
|
|
135
|
-
|
|
136
|
-
|
|
150
|
+
const cleaned = stripSenderMetadata(event.prompt);
|
|
151
|
+
api.logger.info("[virtueai-trajectory] llm_input fired, prompt=" + (cleaned ?? "").slice(0, 80));
|
|
152
|
+
if (cleaned) {
|
|
153
|
+
sendStep("user", cleaned);
|
|
137
154
|
}
|
|
138
155
|
});
|
|
139
156
|
|
|
140
|
-
// Hook: LLM response received
|
|
141
157
|
api.on("llm_output", (event) => {
|
|
158
|
+
api.logger.info("[virtueai-trajectory] llm_output fired, assistantTexts.length=" + (event.assistantTexts?.length ?? "undefined") + ", keys=" + Object.keys(event).join(","));
|
|
142
159
|
const text = (event.assistantTexts ?? []).join("\\n").trim();
|
|
143
160
|
if (text) {
|
|
161
|
+
api.logger.info("[virtueai-trajectory] llm_output sending agent text, len=" + text.length);
|
|
144
162
|
sendStep("agent", text);
|
|
163
|
+
} else {
|
|
164
|
+
api.logger.warn("[virtueai-trajectory] llm_output fired but assistantTexts empty");
|
|
145
165
|
}
|
|
146
166
|
});
|
|
147
167
|
|
|
148
|
-
// Hook: tool call completed
|
|
149
168
|
api.on("after_tool_call", (event) => {
|
|
150
|
-
|
|
169
|
+
api.logger.info("[virtueai-trajectory] after_tool_call fired, tool=" + event.toolName);
|
|
170
|
+
const toolParams = event.params
|
|
151
171
|
? Object.entries(event.params)
|
|
152
172
|
.map(([k, v]) => k + "=" + JSON.stringify(v))
|
|
153
173
|
.join(", ")
|
|
154
174
|
: "";
|
|
155
|
-
const callStr = event.toolName + "(" +
|
|
175
|
+
const callStr = event.toolName + "(" + toolParams + ")";
|
|
156
176
|
const resultStr = event.result != null ? truncate(event.result, 500) : (event.error ?? "no result");
|
|
157
177
|
sendStep("agent", callStr + " → " + resultStr);
|
|
158
178
|
});
|
|
159
179
|
|
|
160
|
-
api.
|
|
180
|
+
api.on("agent_end", (event) => {
|
|
181
|
+
api.logger.info("[virtueai-trajectory] agent_end fired, success=" + event.success + ", durationMs=" + event.durationMs);
|
|
182
|
+
});
|
|
161
183
|
},
|
|
162
184
|
};
|
|
163
185
|
|