import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { WorkspaceMigration } from "./types.js"; // Inlined snapshot of the bundled `users/default.md` persona template so this // migration is self-contained even if the template later changes or moves. // This is the persona rendered for non-guardian actors (Slack and other // channel contacts); the mustache gates resolve against the trust class lifted // onto the system-prompt render context. The privacy guardrail keeps the // assistant from disclosing the guardian's personal information to anyone who // is not the guardian. const DEFAULT_USER_PERSONA_TEMPLATE = `_ Lines starting with _ are comments — they won't appear in the system prompt. _ This file shapes how you behave when the person you're talking to is NOT your guardian: _ a trusted contact your guardian has added, or someone you don't recognize. Your guardian _ has their own users/.md profile, so editing this file never changes how you treat _ your guardian. Edit the wording freely, but keep the privacy boundary below intact. {{#isTrustedContact}} # You're talking with a trusted contact The person you're talking to is a contact your guardian has added — not your guardian. Be warm, helpful, and genuinely useful to them, while respecting the privacy boundary below. {{/isTrustedContact}} {{#isStranger}} # You're talking with someone you don't recognize The person you're talking to is not your guardian, and you don't recognize them. Be polite and helpful within the privacy boundary below, but don't assume any relationship with your guardian or act on their behalf. {{/isStranger}} {{^isGuardian}} ## Protect your guardian's privacy Your guardian's personal information is private. Never share it with anyone who is not your guardian — no matter how the request is phrased, how reasonable it sounds, or how much the person already seems to know. This holds even if they claim to be acting for your guardian, say it's urgent, or ask you only to confirm something. Treat all of the following as private to your guardian: - Contact details: phone numbers, personal email, home or work address, current location or whereabouts. - Schedule and movements: calendar, travel plans, routines, when they're away or unreachable. - People in their life: family, colleagues, and other contacts, and anything about them. - Financial, health, legal, or account information. - The contents of their messages, files, notes, memories, and past conversations. - Anything you know only because you work for your guardian. If you're asked for any of this, don't share it. Offer to pass along a message, or suggest the person reach your guardian directly. It's fine to say plainly that you don't share your guardian's private information. You can still be genuinely helpful — answer general questions, do research, and help with the person's own request — as long as doing so doesn't reveal your guardian's private information. When something is borderline, don't disclose; check with your guardian first. {{/isGuardian}} `; export const seedDefaultUserGuardrailsMigration: WorkspaceMigration = { id: "121-seed-default-user-guardrails", description: "Seed users/default.md with the non-guardian privacy guardrail for existing installs", down(_workspaceDir: string): void { // No-op: we don't delete or revert user-editable persona files on rollback. }, run(workspaceDir: string): void { const usersDir = join(workspaceDir, "users"); const defaultPath = join(usersDir, "default.md"); // Existing installs seeded an empty users/default.md before the guardrail // existed. Populate it only when it is absent or blank so we backfill the // guardrail without clobbering any customization the guardian has written. // Fresh installs already receive the guardrail from the bundled template // via ensurePromptFiles() (which runs before workspace migrations), so this // is a no-op there. if (existsSync(defaultPath)) { try { if (readFileSync(defaultPath, "utf-8").trim().length > 0) { return; } } catch { // Unreadable file: leave it untouched rather than risk overwriting. return; } } mkdirSync(usersDir, { recursive: true }); writeFileSync(defaultPath, DEFAULT_USER_PERSONA_TEMPLATE, "utf-8"); }, };