Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ SLACK_HISTORY_API_MAX_LIMIT=50
SLACK_ACTIVE_TURN_RECONCILE_INTERVAL_MS=15000
SLACK_PROGRESS_REMINDER_AFTER_MS=120000
SLACK_PROGRESS_REMINDER_REPEAT_MS=120000
SESSION_ARTIFACT_INACTIVE_TTL_MS=21600000
SESSION_ARTIFACT_CLEANUP_INTERVAL_MS=3600000
SESSION_ARTIFACT_CLEANUP_MAX_PER_SWEEP=20

# Service storage
PORT=3000
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ Disposable runtime state:
- `logs/`
- `repos/`

The worker also runs a session artifact janitor for inactive sessions. By default it removes rebuildable macOS worktree artifacts like `frontend/macos/.build` after 6 hours of inactivity, while leaving the session workspace and source checkout in place.

The macOS bare-run deploy path only reuses the durable broker-owned subset that defines behavior and identity. It intentionally leaves the disposable runtime state behind and starts the VM with a clean `sessions/`, `jobs/`, `logs/`, and `repos/`.

## Logging
Expand Down
6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export interface AppConfig {
readonly slackMissedThreadRecoveryIntervalMs: number;
readonly slackProgressReminderAfterMs: number;
readonly slackProgressReminderRepeatMs: number;
readonly sessionArtifactInactiveTtlMs: number;
readonly sessionArtifactCleanupIntervalMs: number;
readonly sessionArtifactCleanupMaxPerSweep: number;
readonly stateDir: string;
readonly jobsRoot: string;
readonly sessionsRoot: string;
Expand Down Expand Up @@ -171,6 +174,9 @@ export function loadConfig(env = process.env): AppConfig {
),
slackProgressReminderAfterMs: getNumber(env, "SLACK_PROGRESS_REMINDER_AFTER_MS", 120_000),
slackProgressReminderRepeatMs: getNumber(env, "SLACK_PROGRESS_REMINDER_REPEAT_MS", 120_000),
sessionArtifactInactiveTtlMs: getNumber(env, "SESSION_ARTIFACT_INACTIVE_TTL_MS", 6 * 60 * 60 * 1_000),
sessionArtifactCleanupIntervalMs: getNumber(env, "SESSION_ARTIFACT_CLEANUP_INTERVAL_MS", 60 * 60 * 1_000),
sessionArtifactCleanupMaxPerSweep: getNumber(env, "SESSION_ARTIFACT_CLEANUP_MAX_PER_SWEEP", 20),
stateDir,
jobsRoot,
sessionsRoot,
Expand Down
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CodexRuntimeControl } from "./services/codex-runtime-control.js";
import { IsolatedMcpService } from "./services/codex/isolated-mcp-service.js";
import { GitHubAuthorMappingService } from "./services/github-author-mapping-service.js";
import { JobManager } from "./services/job-manager.js";
import { SessionArtifactJanitor } from "./services/session-artifact-janitor.js";
import { SessionManager } from "./services/session-manager.js";
import { SlackCodexBridge } from "./services/slack/slack-codex-bridge.js";
import { StateStore } from "./store/state-store.js";
Expand Down Expand Up @@ -71,6 +72,12 @@ export async function startService(): Promise<{
await bridge.acceptBackgroundJobEvent(event);
}
});
const sessionArtifactJanitor = new SessionArtifactJanitor({
sessions: sessionManager,
inactivityTtlMs: config.sessionArtifactInactiveTtlMs,
cleanupIntervalMs: config.sessionArtifactCleanupIntervalMs,
cleanupMaxPerSweep: config.sessionArtifactCleanupMaxPerSweep
});
const authProfiles = new AuthProfileService({
config
});
Expand All @@ -95,11 +102,13 @@ export async function startService(): Promise<{
try {
await bridge.start();
await jobManager.start();
await sessionArtifactJanitor.start();
await new Promise<void>((resolve, reject) => {
server.listen(config.port, () => resolve());
server.once("error", reject);
});
} catch (error) {
await sessionArtifactJanitor.stop().catch(() => {});
await jobManager.stop().catch(() => {});
await bridge.stop().catch(() => {});
if (server.listening) {
Expand All @@ -118,6 +127,7 @@ export async function startService(): Promise<{

return {
stop: async () => {
await sessionArtifactJanitor.stop();
await bridge.stop();
await jobManager.stop();
await new Promise<void>((resolve, reject) => {
Expand Down
Loading
Loading