import { join, resolve } from "node:path"; import { AuthStorage, DefaultResourceLoader, ModelRegistry, SessionManager, SettingsManager, createAgentSession, getAgentDir, type AgentSession, type ResourceLoader, } from "@earendil-works/pi-coding-agent"; import { getModel } from "@earendil-works/pi-ai"; import type { Client } from "discord.js"; import { createScheduleTools } from "./tools/schedule.js"; const PROVIDER = process.env.DISCLAW_PROVIDER ?? "anthropic"; const MODEL_ID = process.env.DISCLAW_MODEL ?? "claude-sonnet-4-6"; const TZ = process.env.TZ ?? "UTC"; export interface SharedDeps { authStorage: AuthStorage; modelRegistry: ModelRegistry; model: ReturnType; resourceLoader: ResourceLoader; settingsManager: SettingsManager; cwd: string; schedulesDir: string; /** Called whenever a schedule file is created or removed. */ onSchedulesChanged?: () => void; } export interface SessionContext { /** Discord channel ID to use as default destination for schedule_task. */ defaultDestination?: string; /** Discord user ID to use as DM fallback for schedule_task. */ defaultUserId?: string; } export interface CreateSessionOptions { sessionManager?: SessionManager; } export async function createSharedDeps( opts: { onSchedulesChanged?: () => void } = {} ): Promise { const cwd = resolve(process.cwd()); const authStorage = AuthStorage.create(); const modelRegistry = ModelRegistry.create(authStorage); const model = getModel(PROVIDER as never, MODEL_ID as never); if (!model) { throw new Error( `Model not found: ${PROVIDER}/${MODEL_ID}. Set ANTHROPIC_API_KEY (or DISCLAW_PROVIDER/DISCLAW_MODEL).` ); } const settingsManager = SettingsManager.create(cwd, getAgentDir()); const resourceLoader = new DefaultResourceLoader({ cwd, agentDir: getAgentDir(), settingsManager, additionalSkillPaths: [resolve(cwd, "skills")], appendSystemPromptOverride: (base) => [ ...base, `The user's local timezone is ${TZ}. Always reference and display times, dates, and schedules in this timezone unless the user explicitly requests a different one.`, ], }); await resourceLoader.reload(); return { authStorage, modelRegistry, model, resourceLoader, settingsManager, cwd, schedulesDir: join(cwd, "memory", "schedules"), onSchedulesChanged: opts.onSchedulesChanged, }; } export async function createSession( deps: SharedDeps, ctx: SessionContext = {}, client?: Client, opts: CreateSessionOptions = {} ): Promise { const customTools = createScheduleTools({ schedulesDir: deps.schedulesDir, defaultDestination: ctx.defaultDestination, defaultUserId: ctx.defaultUserId, onChange: deps.onSchedulesChanged, client, }); const { session } = await createAgentSession({ cwd: deps.cwd, model: deps.model, authStorage: deps.authStorage, modelRegistry: deps.modelRegistry, resourceLoader: deps.resourceLoader, settingsManager: deps.settingsManager, sessionManager: opts.sessionManager ?? SessionManager.inMemory(), customTools, }); return session; } export function describeModel(): string { return `${PROVIDER}/${MODEL_ID}`; }