Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cloudflare-gastown/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.dev.vars
container/dist/
dist-types/

# Sentry Config File
.sentryclirc
dist
27 changes: 27 additions & 0 deletions cloudflare-gastown/container/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Structured logger for the container process.
*
* Outputs JSON to console.* so entries appear in Workers Logs
* (captured via `observability: { enabled: true }` in wrangler.jsonc).
* Keep this module dependency-free and synchronous.
*/

function flatten(data?: unknown): Record<string, unknown> {
if (!data || typeof data !== 'object') return {};
return data as Record<string, unknown>;
}

export const log = {
info: (msg: string, data?: unknown) =>
console.log(
JSON.stringify({ level: 'info', msg, ...flatten(data), ts: new Date().toISOString() })
),
warn: (msg: string, data?: unknown) =>
console.warn(
JSON.stringify({ level: 'warn', msg, ...flatten(data), ts: new Date().toISOString() })
),
error: (msg: string, data?: unknown) =>
console.error(
JSON.stringify({ level: 'error', msg, ...flatten(data), ts: new Date().toISOString() })
),
};
8 changes: 8 additions & 0 deletions cloudflare-gastown/container/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { startControlServer } from './control-server';
import { log } from './logger';

log.info('container.cold_start', { uptime: 0, ts: new Date().toISOString() });

process.on('uncaughtException', err => {
log.error('container.uncaught_exception', { error: err.message, stack: err.stack });
process.exit(1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Immediate exit can drop the crash log

process.exit(1) terminates the process synchronously, so the container.uncaught_exception entry emitted just above is not guaranteed to flush to stderr/Workers Logs first. That makes the only structured signal for this failure mode easy to lose during postmortems.

});

startControlServer();
73 changes: 58 additions & 15 deletions cloudflare-gastown/container/src/process-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { createKilo, type KiloClient } from '@kilocode/sdk';
import { z } from 'zod';
import type { ManagedAgent, StartAgentRequest } from './types';
import { reportAgentCompleted } from './completion-reporter';
import { log } from './logger';

const MANAGER_LOG = '[process-manager]';

Expand Down Expand Up @@ -272,24 +273,50 @@ async function subscribeToEvents(
const isTerminal = event.type === 'session.idle' && request.role !== 'mayor';

if (isTerminal) {
console.log(
`${MANAGER_LOG} Completion detected for agent ${agent.agentId} (${agent.name}) event=${event.type}`
);
log.info('agent.exit', {
agentId: agent.agentId,
name: agent.name,
reason: 'completed',
exitReason: 'completed',
});
agent.status = 'exited';
Comment thread
jrf0110 marked this conversation as resolved.
agent.exitReason = 'completed';
broadcastEvent(agent.agentId, 'agent.exited', { reason: 'completed' });
void reportAgentCompleted(agent, 'completed');

// Release SDK session so the server can shut down when idle
const inst = sdkInstances.get(agent.workdir);
if (inst) {
inst.sessionCount--;
if (inst.sessionCount <= 0) {
inst.server.close();
sdkInstances.delete(agent.workdir);
}
}
break;
}
}
} catch (err) {
if (!controller.signal.aborted) {
console.error(`${MANAGER_LOG} Event stream error for agent ${agent.agentId}:`, err);
log.error('agent.stream_error', {
Comment thread
jrf0110 marked this conversation as resolved.
agentId: agent.agentId,
error: err instanceof Error ? err.message : String(err),
});
if (agent.status === 'running') {
agent.status = 'failed';
agent.exitReason = 'Event stream error';
broadcastEvent(agent.agentId, 'agent.exited', { reason: 'stream error' });
void reportAgentCompleted(agent, 'failed', 'Event stream error');

// Release SDK session on stream error (same cleanup as normal completion)
const inst = sdkInstances.get(agent.workdir);
if (inst) {
inst.sessionCount--;
if (inst.sessionCount <= 0) {
inst.server.close();
sdkInstances.delete(agent.workdir);
}
}
}
}
} finally {
Expand Down Expand Up @@ -390,9 +417,13 @@ export async function startAgent(
}
agent.messageCount = 1;

console.log(
`${MANAGER_LOG} Started agent ${request.name} (${request.agentId}) session=${sessionId} port=${port}`
);
log.info('agent.start', {
agentId: request.agentId,
role: request.role,
name: request.name,
sessionId,
port,
});

return agent;
} catch (err) {
Expand Down Expand Up @@ -433,11 +464,15 @@ export async function stopAgent(agentId: string): Promise<void> {
}
}
} catch (err) {
console.warn(`${MANAGER_LOG} Failed to abort session for agent ${agentId}:`, err);
log.warn('agent.stop_failed', {
agentId,
error: err instanceof Error ? err.message : String(err),
});
}

agent.status = 'exited';
agent.exitReason = 'stopped';
log.info('agent.exit', { agentId, reason: 'stopped', exitReason: 'stopped' });
broadcastEvent(agentId, 'agent.exited', { reason: 'stopped' });
}

Expand All @@ -454,13 +489,21 @@ export async function sendMessage(agentId: string, prompt: string): Promise<void
const instance = sdkInstances.get(agent.workdir);
if (!instance) throw new Error(`No SDK instance for agent ${agentId}`);

await instance.client.session.prompt({
path: { id: agent.sessionId },
body: {
parts: [{ type: 'text', text: prompt }],
...(agent.model ? { model: { providerID: 'kilo', modelID: agent.model } } : {}),
},
});
try {
await instance.client.session.prompt({
path: { id: agent.sessionId },
body: {
parts: [{ type: 'text', text: prompt }],
...(agent.model ? { model: { providerID: 'kilo', modelID: agent.model } } : {}),
},
});
} catch (err) {
log.error('agent.send_failed', {
agentId,
error: err instanceof Error ? err.message : String(err),
});
throw err;
}

agent.messageCount++;
agent.lastActivityAt = new Date().toISOString();
Expand Down
Loading
Loading