fix(agent_api): wire up module lifecycle — auto-start, poll, port 4329

- Call check_cmdline_and_start() from register_types so --agent-api flag works
- Connect poll() to SceneTree::process_frame via deferred callable so
  screenshot/depth/command requests get serviced on the main thread
- Default port changed to 4329 to avoid conflict with C# AgentServer on 4328
- Clean disconnect on stop()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 05:01:38 +01:00
parent d291dcdc74
commit 7fb933a4b9
3 changed files with 26 additions and 4 deletions
+22 -2
View File
@@ -31,7 +31,7 @@ AgentServer::~AgentServer() {
}
void AgentServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("start", "port", "token"), &AgentServer::start, DEFVAL(4328), DEFVAL(""));
ClassDB::bind_method(D_METHOD("start", "port", "token"), &AgentServer::start, DEFVAL(4329), DEFVAL(""));
ClassDB::bind_method(D_METHOD("stop"), &AgentServer::stop);
ClassDB::bind_method(D_METHOD("is_running"), &AgentServer::is_running);
ClassDB::bind_method(D_METHOD("get_port"), &AgentServer::get_port);
@@ -58,6 +58,20 @@ void AgentServer::start(int p_port, const String &p_token) {
running = true;
server_thread.start(_server_thread_func, this);
print_line(vformat("AgentServer: Listening on http://0.0.0.0:%d", port));
// Connect poll() to the SceneTree's process_frame signal so screenshot/depth/command
// requests get serviced on the main thread each frame. Deferred because SceneTree
// may not exist yet during module init.
callable_mp(this, &AgentServer::_connect_to_scene_tree).call_deferred();
}
void AgentServer::_connect_to_scene_tree() {
SceneTree *tree = SceneTree::get_singleton();
if (tree) {
tree->connect("process_frame", callable_mp(this, &AgentServer::poll));
} else {
ERR_PRINT("AgentServer: SceneTree not available, poll() won't run — screenshots will timeout.");
}
}
void AgentServer::stop() {
@@ -68,6 +82,12 @@ void AgentServer::stop() {
running = false;
server_thread.wait_to_finish();
// Disconnect from SceneTree if connected.
SceneTree *tree = SceneTree::get_singleton();
if (tree && tree->is_connected("process_frame", callable_mp(this, &AgentServer::poll))) {
tree->disconnect("process_frame", callable_mp(this, &AgentServer::poll));
}
{
MutexLock lock(sse_mutex);
sse_clients.clear();
@@ -81,7 +101,7 @@ void AgentServer::check_cmdline_and_start() {
// Check --agent-api flag or AGENT_API env var.
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
bool should_start = false;
int custom_port = 4328;
int custom_port = 4329;
String custom_token;
for (const String &arg : cmdline_args) {
+3 -2
View File
@@ -41,7 +41,7 @@ private:
Ref<TCPServer> tcp_server;
Thread server_thread;
bool running = false;
int port = 4328;
int port = 4329;
String auth_token;
// SSE clients for log/event streaming.
@@ -93,6 +93,7 @@ private:
// Server thread function.
static void _server_thread_func(void *p_userdata);
void _server_loop();
void _connect_to_scene_tree();
void _handle_connection(Ref<StreamPeerTCP> p_peer);
// HTTP parsing.
@@ -150,7 +151,7 @@ public:
AgentServer();
~AgentServer();
void start(int p_port = 4328, const String &p_token = "");
void start(int p_port = 4329, const String &p_token = "");
void stop();
bool is_running() const { return running; }
int get_port() const { return port; }
+1
View File
@@ -15,6 +15,7 @@ void initialize_agent_api_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(AgentServer);
agent_server_singleton = memnew(AgentServer);
Engine::get_singleton()->add_singleton(Engine::Singleton("AgentServer", AgentServer::get_singleton()));
AgentServer::check_cmdline_and_start();
}
void uninitialize_agent_api_module(ModuleInitializationLevel p_level) {