@rethinkingstudio/clawpilot 2.1.21-beta.0 → 2.1.21-beta.10
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 +0 -8
- package/dist/ccconnect/history-payload.d.ts +4 -1
- package/dist/ccconnect/project-config.d.ts +1 -0
- package/dist/ccconnect/project-config.js +22 -1
- package/dist/ccconnect/project-config.js.map +1 -1
- package/dist/commands/provider-config.d.ts +9 -0
- package/dist/commands/provider-config.js +68 -7
- package/dist/commands/provider-config.js.map +1 -1
- package/dist/commands/provider-handlers.js +2 -2
- package/dist/commands/provider-handlers.js.map +1 -1
- package/dist/hermes/agent-bridge.d.ts +2 -0
- package/dist/hermes/agent-bridge.js +5 -0
- package/dist/hermes/agent-bridge.js.map +1 -1
- package/dist/hermes/model-catalog.d.ts +10 -0
- package/dist/hermes/model-catalog.js +49 -3
- package/dist/hermes/model-catalog.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/relay/ccconnect-relay-manager.js +5 -1
- package/dist/relay/ccconnect-relay-manager.js.map +1 -1
- package/dist/relay/hermes-relay-manager.js +11 -3
- package/dist/relay/hermes-relay-manager.js.map +1 -1
- package/dist/terminal/pty-manager.d.ts +39 -0
- package/dist/terminal/pty-manager.js +193 -0
- package/dist/terminal/pty-manager.js.map +1 -0
- package/package.json +1 -1
- package/scripts/hermes_agent_bridge.py +115 -18
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { existsSync, statSync } from "fs";
|
|
4
|
+
import { spawn } from "node-pty";
|
|
5
|
+
export class PtyManager {
|
|
6
|
+
emitEvent;
|
|
7
|
+
activeSession = null;
|
|
8
|
+
constructor(emitEvent) {
|
|
9
|
+
this.emitEvent = emitEvent;
|
|
10
|
+
}
|
|
11
|
+
open(rawParams) {
|
|
12
|
+
if (this.activeSession) {
|
|
13
|
+
throw new Error("terminal session already active");
|
|
14
|
+
}
|
|
15
|
+
const params = isRecord(rawParams) ? rawParams : {};
|
|
16
|
+
const cols = clampInt(params.cols, 120, 20, 400);
|
|
17
|
+
const rows = clampInt(params.rows, 32, 10, 200);
|
|
18
|
+
const cwd = resolveWorkingDirectory(normalizeString(params.cwd));
|
|
19
|
+
const shellCandidates = resolveShellCandidates(normalizeString(params.shell));
|
|
20
|
+
const sessionId = randomUUID();
|
|
21
|
+
const { pty, shell } = spawnWithFallback(shellCandidates, cwd, cols, rows);
|
|
22
|
+
const session = {
|
|
23
|
+
sessionId,
|
|
24
|
+
shell,
|
|
25
|
+
cwd,
|
|
26
|
+
cols,
|
|
27
|
+
rows,
|
|
28
|
+
pty,
|
|
29
|
+
};
|
|
30
|
+
this.activeSession = session;
|
|
31
|
+
pty.onData((data) => {
|
|
32
|
+
if (this.activeSession?.sessionId !== sessionId)
|
|
33
|
+
return;
|
|
34
|
+
this.emitEvent("terminal.data", { sessionId, data });
|
|
35
|
+
});
|
|
36
|
+
pty.onExit(({ exitCode, signal }) => {
|
|
37
|
+
if (this.activeSession?.sessionId !== sessionId)
|
|
38
|
+
return;
|
|
39
|
+
this.activeSession = null;
|
|
40
|
+
this.emitEvent("terminal.closed", {
|
|
41
|
+
sessionId,
|
|
42
|
+
exitCode,
|
|
43
|
+
signal,
|
|
44
|
+
reason: "process_exited",
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
const info = stripPty(session);
|
|
48
|
+
this.emitEvent("terminal.opened", info);
|
|
49
|
+
return info;
|
|
50
|
+
}
|
|
51
|
+
input(rawParams) {
|
|
52
|
+
const session = this.requireSession(rawParams);
|
|
53
|
+
const params = isRecord(rawParams) ? rawParams : {};
|
|
54
|
+
const data = normalizeString(params.data);
|
|
55
|
+
if (!data) {
|
|
56
|
+
throw new Error("terminal input data is required");
|
|
57
|
+
}
|
|
58
|
+
session.pty.write(data);
|
|
59
|
+
}
|
|
60
|
+
resize(rawParams) {
|
|
61
|
+
const session = this.requireSession(rawParams);
|
|
62
|
+
const params = isRecord(rawParams) ? rawParams : {};
|
|
63
|
+
const cols = clampInt(params.cols, session.cols, 20, 400);
|
|
64
|
+
const rows = clampInt(params.rows, session.rows, 10, 200);
|
|
65
|
+
session.pty.resize(cols, rows);
|
|
66
|
+
session.cols = cols;
|
|
67
|
+
session.rows = rows;
|
|
68
|
+
return stripPty(session);
|
|
69
|
+
}
|
|
70
|
+
close(rawParams, reason = "closed_by_client") {
|
|
71
|
+
const session = this.requireSession(rawParams);
|
|
72
|
+
this.activeSession = null;
|
|
73
|
+
session.pty.kill();
|
|
74
|
+
this.emitEvent("terminal.closed", {
|
|
75
|
+
sessionId: session.sessionId,
|
|
76
|
+
reason,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
shutdown(reason = "relay_disconnected") {
|
|
80
|
+
if (!this.activeSession)
|
|
81
|
+
return;
|
|
82
|
+
const session = this.activeSession;
|
|
83
|
+
this.activeSession = null;
|
|
84
|
+
session.pty.kill();
|
|
85
|
+
this.emitEvent("terminal.closed", {
|
|
86
|
+
sessionId: session.sessionId,
|
|
87
|
+
reason,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
currentSession() {
|
|
91
|
+
return this.activeSession ? stripPty(this.activeSession) : null;
|
|
92
|
+
}
|
|
93
|
+
requireSession(rawParams) {
|
|
94
|
+
const params = isRecord(rawParams) ? rawParams : {};
|
|
95
|
+
const sessionId = normalizeString(params.sessionId);
|
|
96
|
+
if (!this.activeSession) {
|
|
97
|
+
throw new Error("terminal session is not active");
|
|
98
|
+
}
|
|
99
|
+
if (sessionId && sessionId != this.activeSession.sessionId) {
|
|
100
|
+
throw new Error("terminal session not found");
|
|
101
|
+
}
|
|
102
|
+
return this.activeSession;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function stripPty(session) {
|
|
106
|
+
return {
|
|
107
|
+
sessionId: session.sessionId,
|
|
108
|
+
shell: session.shell,
|
|
109
|
+
cwd: session.cwd,
|
|
110
|
+
cols: session.cols,
|
|
111
|
+
rows: session.rows,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function isRecord(value) {
|
|
115
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
116
|
+
}
|
|
117
|
+
function normalizeString(value) {
|
|
118
|
+
if (typeof value !== "string") {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const trimmed = value.trim();
|
|
122
|
+
return trimmed.length === 0 ? undefined : trimmed;
|
|
123
|
+
}
|
|
124
|
+
function clampInt(value, fallback, min, max) {
|
|
125
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
126
|
+
if (!Number.isFinite(numeric))
|
|
127
|
+
return fallback;
|
|
128
|
+
return Math.max(min, Math.min(max, Math.floor(numeric)));
|
|
129
|
+
}
|
|
130
|
+
function defaultShell() {
|
|
131
|
+
if (process.platform === "win32") {
|
|
132
|
+
return "pwsh.exe";
|
|
133
|
+
}
|
|
134
|
+
return process.env.SHELL || "/bin/bash";
|
|
135
|
+
}
|
|
136
|
+
function resolveShellCandidates(requestedShell) {
|
|
137
|
+
if (requestedShell) {
|
|
138
|
+
return dedupe([requestedShell, ...platformShellFallbacks()]);
|
|
139
|
+
}
|
|
140
|
+
return dedupe([defaultShell(), ...platformShellFallbacks()]);
|
|
141
|
+
}
|
|
142
|
+
function platformShellFallbacks() {
|
|
143
|
+
if (process.platform === "win32") {
|
|
144
|
+
return ["powershell.exe", "cmd.exe"];
|
|
145
|
+
}
|
|
146
|
+
return ["/bin/zsh", "/bin/bash", "/bin/sh"];
|
|
147
|
+
}
|
|
148
|
+
function resolveWorkingDirectory(requestedCwd) {
|
|
149
|
+
const candidates = dedupe([
|
|
150
|
+
requestedCwd,
|
|
151
|
+
process.env.HOME,
|
|
152
|
+
homedir(),
|
|
153
|
+
process.cwd(),
|
|
154
|
+
"/",
|
|
155
|
+
].filter((value) => !!value));
|
|
156
|
+
for (const candidate of candidates) {
|
|
157
|
+
if (directoryExists(candidate)) {
|
|
158
|
+
return candidate;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return process.cwd();
|
|
162
|
+
}
|
|
163
|
+
function directoryExists(path) {
|
|
164
|
+
try {
|
|
165
|
+
return existsSync(path) && statSync(path).isDirectory();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function spawnWithFallback(shellCandidates, cwd, cols, rows) {
|
|
172
|
+
let lastError;
|
|
173
|
+
for (const shell of shellCandidates) {
|
|
174
|
+
try {
|
|
175
|
+
const pty = spawn(shell, [], {
|
|
176
|
+
name: "xterm-256color",
|
|
177
|
+
cols,
|
|
178
|
+
rows,
|
|
179
|
+
cwd,
|
|
180
|
+
env: { ...process.env },
|
|
181
|
+
});
|
|
182
|
+
return { pty, shell };
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
lastError = error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
throw new Error(`failed to open terminal: ${String(lastError)}; shells tried: ${shellCandidates.join(", ")}; cwd=${cwd}`);
|
|
189
|
+
}
|
|
190
|
+
function dedupe(values) {
|
|
191
|
+
return [...new Set(values)];
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=pty-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-manager.js","sourceRoot":"","sources":["../../src/terminal/pty-manager.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -87,30 +87,102 @@ def read_base_config_for_profile() -> Dict[str, Any]:
|
|
|
87
87
|
return read_yaml_config(parent.parent / "config.yaml")
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def normalize_message_content(value: Any) -> Any:
|
|
91
|
+
if value is None:
|
|
92
|
+
return ""
|
|
93
|
+
if isinstance(value, str):
|
|
94
|
+
return value
|
|
95
|
+
if isinstance(value, list):
|
|
96
|
+
parts: List[Any] = []
|
|
97
|
+
for part in value:
|
|
98
|
+
if isinstance(part, str):
|
|
99
|
+
parts.append({"type": "text", "text": part})
|
|
100
|
+
continue
|
|
101
|
+
if not isinstance(part, dict):
|
|
102
|
+
continue
|
|
103
|
+
clean_part = {k: v for k, v in part.items() if v is not None}
|
|
104
|
+
if clean_part.get("type") == "text":
|
|
105
|
+
clean_part["text"] = str(clean_part.get("text") or "")
|
|
106
|
+
if clean_part:
|
|
107
|
+
parts.append(clean_part)
|
|
108
|
+
return parts if parts else ""
|
|
109
|
+
return str(value)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def normalize_tool_calls(value: Any) -> List[Dict[str, Any]]:
|
|
113
|
+
if not isinstance(value, list):
|
|
114
|
+
return []
|
|
115
|
+
clean_calls: List[Dict[str, Any]] = []
|
|
116
|
+
for index, call in enumerate(value):
|
|
117
|
+
if not isinstance(call, dict):
|
|
118
|
+
continue
|
|
119
|
+
raw_function = call.get("function")
|
|
120
|
+
function = raw_function if isinstance(raw_function, dict) else {}
|
|
121
|
+
name = str(function.get("name") or call.get("name") or "").strip()
|
|
122
|
+
if not name:
|
|
123
|
+
continue
|
|
124
|
+
raw_arguments = function.get("arguments")
|
|
125
|
+
if raw_arguments is None:
|
|
126
|
+
arguments = "{}"
|
|
127
|
+
elif isinstance(raw_arguments, str):
|
|
128
|
+
arguments = raw_arguments
|
|
129
|
+
else:
|
|
130
|
+
try:
|
|
131
|
+
arguments = json.dumps(raw_arguments, ensure_ascii=False)
|
|
132
|
+
except Exception:
|
|
133
|
+
arguments = str(raw_arguments)
|
|
134
|
+
call_id = str(call.get("id") or call.get("call_id") or f"call_{index}").strip()
|
|
135
|
+
clean_calls.append(
|
|
136
|
+
{
|
|
137
|
+
"id": call_id,
|
|
138
|
+
"type": str(call.get("type") or "function"),
|
|
139
|
+
"function": {
|
|
140
|
+
"name": name,
|
|
141
|
+
"arguments": arguments,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
return clean_calls
|
|
146
|
+
|
|
147
|
+
|
|
90
148
|
def sanitize_messages_for_api(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
91
149
|
valid_tool_call_ids: set[str] = set()
|
|
92
150
|
for msg in messages:
|
|
93
151
|
if not isinstance(msg, dict):
|
|
94
152
|
continue
|
|
95
153
|
if msg.get("role") == "assistant":
|
|
96
|
-
for tc in msg.get("tool_calls")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
valid_tool_call_ids.add(str(tid))
|
|
154
|
+
for tc in normalize_tool_calls(msg.get("tool_calls")):
|
|
155
|
+
tid = tc.get("id") or ""
|
|
156
|
+
if tid:
|
|
157
|
+
valid_tool_call_ids.add(str(tid))
|
|
101
158
|
|
|
102
159
|
clean: List[Dict[str, Any]] = []
|
|
103
160
|
for msg in messages:
|
|
104
161
|
if not isinstance(msg, dict):
|
|
105
162
|
continue
|
|
106
|
-
role = msg.get("role")
|
|
163
|
+
role = str(msg.get("role") or "").strip()
|
|
164
|
+
if role not in {"system", "user", "assistant", "tool"}:
|
|
165
|
+
continue
|
|
166
|
+
sanitized: Dict[str, Any] = {"role": role}
|
|
107
167
|
if role == "tool":
|
|
108
168
|
tid = str(msg.get("tool_call_id") or "")
|
|
109
169
|
if not tid or tid not in valid_tool_call_ids:
|
|
110
170
|
continue
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
171
|
+
sanitized["tool_call_id"] = tid
|
|
172
|
+
if msg.get("name"):
|
|
173
|
+
sanitized["name"] = str(msg.get("name"))
|
|
174
|
+
|
|
175
|
+
if role == "assistant":
|
|
176
|
+
tool_calls = normalize_tool_calls(msg.get("tool_calls"))
|
|
177
|
+
if tool_calls:
|
|
178
|
+
sanitized["tool_calls"] = tool_calls
|
|
179
|
+
|
|
180
|
+
content = normalize_message_content(msg.get("content"))
|
|
181
|
+
if content or role != "assistant" or sanitized.get("tool_calls"):
|
|
182
|
+
sanitized["content"] = content
|
|
183
|
+
if msg.get("refusal") is not None:
|
|
184
|
+
sanitized["refusal"] = str(msg.get("refusal"))
|
|
185
|
+
clean.append(sanitized)
|
|
114
186
|
return clean
|
|
115
187
|
|
|
116
188
|
|
|
@@ -147,9 +219,10 @@ def read_session_history_from_json(session_id: Optional[str]) -> List[Dict[str,
|
|
|
147
219
|
|
|
148
220
|
|
|
149
221
|
def read_session_history(session_id: Optional[str], session_db: Optional[SessionDB]) -> List[Dict[str, Any]]:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
222
|
+
if os.environ.get("CLAWPILOT_HERMES_BRIDGE_DISABLE_SESSIONDB_HISTORY") != "1":
|
|
223
|
+
from_db = read_session_history_from_db(session_id, session_db)
|
|
224
|
+
if from_db:
|
|
225
|
+
return from_db
|
|
153
226
|
return read_session_history_from_json(session_id)
|
|
154
227
|
|
|
155
228
|
|
|
@@ -221,6 +294,16 @@ def resolve_runtime(
|
|
|
221
294
|
)
|
|
222
295
|
|
|
223
296
|
|
|
297
|
+
def resolve_toolsets(cfg: Dict[str, Any]) -> List[str]:
|
|
298
|
+
raw_toolsets = _get_platform_tools(cfg, "cli")
|
|
299
|
+
if raw_toolsets is None:
|
|
300
|
+
return []
|
|
301
|
+
try:
|
|
302
|
+
return [str(toolset) for toolset in raw_toolsets]
|
|
303
|
+
except TypeError as exc:
|
|
304
|
+
raise TypeError("Hermes CLI toolset config must be iterable") from exc
|
|
305
|
+
|
|
306
|
+
|
|
224
307
|
def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB] = None) -> Dict[str, Any]:
|
|
225
308
|
user_message = str(payload.get("userMessage") or "").strip()
|
|
226
309
|
if not user_message:
|
|
@@ -230,6 +313,8 @@ def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB]
|
|
|
230
313
|
session_title = str(payload.get("sessionTitle") or "").strip() or None
|
|
231
314
|
model = str(payload.get("model") or "").strip() or None
|
|
232
315
|
provider = str(payload.get("provider") or "").strip() or None
|
|
316
|
+
base_url = str(payload.get("baseUrl") or "").strip() or None
|
|
317
|
+
api_key = str(payload.get("apiKey") or "").strip() or None
|
|
233
318
|
|
|
234
319
|
inherit_base_env_for_profile()
|
|
235
320
|
|
|
@@ -248,16 +333,18 @@ def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB]
|
|
|
248
333
|
if not default_model:
|
|
249
334
|
raise ValueError("missing model")
|
|
250
335
|
|
|
336
|
+
configured_base_url = str(model_cfg.get("base_url") or "").strip() or None
|
|
251
337
|
runtime = normalize_runtime_config(
|
|
252
|
-
|
|
253
|
-
provider or model_cfg.get("provider"),
|
|
254
|
-
|
|
338
|
+
resolve_runtime_provider(
|
|
339
|
+
requested=provider or model_cfg.get("provider"),
|
|
340
|
+
explicit_api_key=api_key,
|
|
341
|
+
explicit_base_url=base_url or configured_base_url,
|
|
255
342
|
target_model=default_model,
|
|
256
343
|
)
|
|
257
344
|
)
|
|
258
|
-
if
|
|
259
|
-
runtime["base_url"] =
|
|
260
|
-
toolsets =
|
|
345
|
+
if configured_base_url and not runtime.get("base_url"):
|
|
346
|
+
runtime["base_url"] = configured_base_url
|
|
347
|
+
toolsets = resolve_toolsets(cfg)
|
|
261
348
|
|
|
262
349
|
history = read_session_history(session_id, session_db)
|
|
263
350
|
return {
|
|
@@ -412,6 +499,16 @@ def main() -> int:
|
|
|
412
499
|
task_id=agent.session_id,
|
|
413
500
|
persist_user_message=agent_inputs["user_message"],
|
|
414
501
|
)
|
|
502
|
+
if not isinstance(result, dict):
|
|
503
|
+
emit(
|
|
504
|
+
{
|
|
505
|
+
"type": "error",
|
|
506
|
+
"message": "Hermes agent returned no result."
|
|
507
|
+
if result is None
|
|
508
|
+
else f"Hermes agent returned invalid result: {type(result).__name__}.",
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
return 1
|
|
415
512
|
final_text = extract_final_text(result, accumulated_text)
|
|
416
513
|
if not final_text.strip():
|
|
417
514
|
error_text = extract_error_text(result)
|