refactor(mom): use SettingsManager with workspace storage

fixes #1444
This commit is contained in:
Mario Zechner 2026-03-02 19:38:07 +01:00
parent 0c23a3415e
commit 46bfa7931e
2 changed files with 25 additions and 142 deletions

View file

@ -16,7 +16,7 @@ import { existsSync, readFileSync } from "fs";
import { mkdir, writeFile } from "fs/promises";
import { homedir } from "os";
import { join } from "path";
import { MomSettingsManager, syncLogToSessionManager } from "./context.js";
import { createMomSettingsManager, syncLogToSessionManager } from "./context.js";
import * as log from "./log.js";
import { createExecutor, type SandboxConfig } from "./sandbox.js";
import type { ChannelInfo, SlackContext, UserInfo } from "./slack.js";
@ -424,7 +424,7 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi
// Use a fixed context.jsonl file per channel (not timestamped like coding-agent)
const contextFile = join(channelDir, "context.jsonl");
const sessionManager = SessionManager.open(contextFile, channelDir);
const settingsManager = new MomSettingsManager(join(channelDir, ".."));
const settingsManager = createMomSettingsManager(join(channelDir, ".."));
// Create AuthStorage and ModelRegistry
// Auth stored outside workspace so agent can't access it
@ -469,7 +469,7 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi
const session = new AgentSession({
agent,
sessionManager,
settingsManager: settingsManager as any,
settingsManager,
cwd: process.cwd(),
modelRegistry,
resourceLoader,

View file

@ -7,11 +7,11 @@
*
* This module provides:
* - syncLogToSessionManager: Syncs messages from log.jsonl to SessionManager
* - MomSettingsManager: Simple settings for mom (compaction, retry, model preferences)
* - createMomSettingsManager: Creates a SettingsManager backed by workspace settings.json
*/
import type { UserMessage } from "@mariozechner/pi-ai";
import type { SessionManager, SessionMessageEntry } from "@mariozechner/pi-coding-agent";
import { type SessionManager, type SessionMessageEntry, SettingsManager } from "@mariozechner/pi-coding-agent";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
@ -142,156 +142,39 @@ export function syncLogToSessionManager(
}
// ============================================================================
// MomSettingsManager - Simple settings for mom
// Settings manager for mom
// ============================================================================
export interface MomCompactionSettings {
enabled: boolean;
reserveTokens: number;
keepRecentTokens: number;
}
type MomSettingsStorage = Parameters<typeof SettingsManager.fromStorage>[0];
export interface MomRetrySettings {
enabled: boolean;
maxRetries: number;
baseDelayMs: number;
}
export interface MomSettings {
defaultProvider?: string;
defaultModel?: string;
defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high";
compaction?: Partial<MomCompactionSettings>;
retry?: Partial<MomRetrySettings>;
}
const DEFAULT_COMPACTION: MomCompactionSettings = {
enabled: true,
reserveTokens: 16384,
keepRecentTokens: 20000,
};
const DEFAULT_RETRY: MomRetrySettings = {
enabled: true,
maxRetries: 3,
baseDelayMs: 2000,
};
/**
* Settings manager for mom.
* Stores settings in the workspace root directory.
*/
export class MomSettingsManager {
class WorkspaceSettingsStorage implements MomSettingsStorage {
private settingsPath: string;
private settings: MomSettings;
constructor(workspaceDir: string) {
this.settingsPath = join(workspaceDir, "settings.json");
this.settings = this.load();
}
private load(): MomSettings {
if (!existsSync(this.settingsPath)) {
return {};
withLock(scope: "global" | "project", fn: (current: string | undefined) => string | undefined): void {
if (scope === "project") {
// Mom stores all settings in a single workspace file.
fn(undefined);
return;
}
try {
const content = readFileSync(this.settingsPath, "utf-8");
return JSON.parse(content);
} catch {
return {};
}
const current = existsSync(this.settingsPath) ? readFileSync(this.settingsPath, "utf-8") : undefined;
const next = fn(current);
if (next === undefined) {
return;
}
private save(): void {
try {
const dir = dirname(this.settingsPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2), "utf-8");
} catch (error) {
console.error(`Warning: Could not save settings file: ${error}`);
writeFileSync(this.settingsPath, next, "utf-8");
}
}
getCompactionSettings(): MomCompactionSettings {
return {
...DEFAULT_COMPACTION,
...this.settings.compaction,
};
}
getCompactionEnabled(): boolean {
return this.settings.compaction?.enabled ?? DEFAULT_COMPACTION.enabled;
}
setCompactionEnabled(enabled: boolean): void {
this.settings.compaction = { ...this.settings.compaction, enabled };
this.save();
}
getRetrySettings(): MomRetrySettings {
return {
...DEFAULT_RETRY,
...this.settings.retry,
};
}
getRetryEnabled(): boolean {
return this.settings.retry?.enabled ?? DEFAULT_RETRY.enabled;
}
setRetryEnabled(enabled: boolean): void {
this.settings.retry = { ...this.settings.retry, enabled };
this.save();
}
getDefaultModel(): string | undefined {
return this.settings.defaultModel;
}
getDefaultProvider(): string | undefined {
return this.settings.defaultProvider;
}
setDefaultModelAndProvider(provider: string, modelId: string): void {
this.settings.defaultProvider = provider;
this.settings.defaultModel = modelId;
this.save();
}
getDefaultThinkingLevel(): string {
return this.settings.defaultThinkingLevel || "off";
}
setDefaultThinkingLevel(level: string): void {
this.settings.defaultThinkingLevel = level as MomSettings["defaultThinkingLevel"];
this.save();
}
// Compatibility methods for AgentSession
getSteeringMode(): "all" | "one-at-a-time" {
return "one-at-a-time"; // Mom processes one message at a time
}
setSteeringMode(_mode: "all" | "one-at-a-time"): void {
// No-op for mom
}
getFollowUpMode(): "all" | "one-at-a-time" {
return "one-at-a-time"; // Mom processes one message at a time
}
setFollowUpMode(_mode: "all" | "one-at-a-time"): void {
// No-op for mom
}
getHookPaths(): string[] {
return []; // Mom doesn't use hooks
}
getHookTimeout(): number {
return 30000;
}
export function createMomSettingsManager(workspaceDir: string): SettingsManager {
return SettingsManager.fromStorage(new WorkspaceSettingsStorage(workspaceDir));
}