feat(@projects/@magic-civilization): add auto-respawn harness logic

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-17 02:19:58 -07:00
parent bcf0dd3f76
commit 62d6a13ac9
3 changed files with 19 additions and 3 deletions

View file

@ -276,7 +276,6 @@ fn normalize_int_zero_floats(src: &str) -> String {
j += 1;
}
if bytes[j].is_ascii_digit() {
let digit_start = j;
while j < bytes.len() && bytes[j].is_ascii_digit() {
j += 1;
}

View file

@ -47,6 +47,12 @@ export class HarnessClient {
private nextId = 1;
private closed = false;
/** True after the child exits (clean or crash). Lets the MCP layer
* detect a dead harness and respawn instead of erroring forever. */
get isClosed(): boolean {
return this.closed;
}
constructor(opts: HarnessOptions) {
this.opts = {
env: {},

View file

@ -108,8 +108,19 @@ interface HarnessHolder {
const holder: HarnessHolder = { client: null };
function getOrCreateClient(): HarnessClient {
if (holder.client !== null) return holder.client;
emitStderr(`spawning harness: ${serverScript()}`);
// Auto-respawn: if the held client died (godot child crashed, was killed by
// a developer iterating on the dylib, etc.) treat it as `null` so the next
// request gets a fresh harness. Without this, every Claude Code session
// that hit a single harness crash would need a full restart to recover.
if (holder.client !== null && !holder.client.isClosed) {
return holder.client;
}
if (holder.client !== null) {
emitStderr("previous harness closed; respawning");
holder.client = null;
} else {
emitStderr(`spawning harness: ${serverScript()}`);
}
const client = new HarnessClient({
serverScript: serverScript(),
env: harnessEnv(),