@dreb/coding-agent 2.34.2 → 2.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  * Soul is LLM-generated once on first hatch and persisted to buddy.json.
7
7
  */
8
8
  import { completeSimple } from "@dreb/ai";
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
9
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
10
10
  import { hostname } from "os";
11
11
  import { join } from "path";
12
12
  import { getAgentDir } from "../../config.js";
@@ -141,7 +141,22 @@ function saveStored(stored) {
141
141
  if (!existsSync(dir)) {
142
142
  mkdirSync(dir, { recursive: true });
143
143
  }
144
- writeFileSync(path, JSON.stringify(stored, null, 2));
144
+ let tempPath = join(dir, `.buddy-${process.pid}-${Date.now()}.json.tmp`);
145
+ try {
146
+ writeFileSync(tempPath, JSON.stringify(stored, null, 2), "utf8");
147
+ renameSync(tempPath, path);
148
+ tempPath = undefined;
149
+ }
150
+ finally {
151
+ if (tempPath) {
152
+ try {
153
+ rmSync(tempPath, { force: true });
154
+ }
155
+ catch {
156
+ // Best-effort cleanup only
157
+ }
158
+ }
159
+ }
145
160
  }
146
161
  // =============================================================================
147
162
  // Bone rolling
@@ -385,6 +400,14 @@ export class BuddyManager {
385
400
  resetOllamaCache() {
386
401
  this.ollamaStatus = null;
387
402
  }
403
+ /**
404
+ * Fresh-read the persisted `hidden` flag from disk (bypasses the in-memory
405
+ * cache). Returns false when no buddy is stored. Used to detect a `/buddy off`
406
+ * (or re-enable) performed by another concurrently-running dreb instance.
407
+ */
408
+ isHidden() {
409
+ return loadStored()?.hidden ?? false;
410
+ }
388
411
  /** Update the hidden flag in persisted storage */
389
412
  setHidden(hidden) {
390
413
  const stored = loadStored();
@@ -1 +1 @@
1
- {"version":3,"file":"buddy-manager.js","sourceRoot":"","sources":["../../../src/core/buddy/buddy-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * BuddyManager — Core state machine for the buddy companion.\n *\n * Handles: bone rolling, soul generation, persistence, Ollama availability checks.\n * Bones are deterministic from hash(username + hostname + salt + rerollCount).\n * Soul is LLM-generated once on first hatch and persisted to buddy.json.\n */\n\nimport type { Context, Model } from \"@dreb/ai\";\nimport { completeSimple } from \"@dreb/ai\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { hostname } from \"os\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../../config.js\";\nimport { createBuddyRng } from \"./buddy-prng.js\";\nimport { rollEyes, rollHat, rollSpecies, rollStats } from \"./buddy-species.js\";\nimport type { BuddyState, CompanionBones, StoredCompanion } from \"./buddy-types.js\";\nimport { STAT_NAMES } from \"./buddy-types.js\";\n\nconst BUDDY_SALT = \"dreb-buddy-v1\";\nconst BUDDY_FILENAME = \"buddy.json\";\nconst DEFAULT_BACKSTORY = \"A mysterious past shrouded in legend.\";\n\n/** Base Ollama model config — id/name are set dynamically from available models */\nconst OLLAMA_MODEL_BASE: Omit<Model<\"openai-completions\">, \"id\" | \"name\"> = {\n\tapi: \"openai-completions\",\n\tprovider: \"ollama\",\n\tbaseUrl: \"http://localhost:11434/v1\",\n\treasoning: false,\n\tinput: [\"text\"],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 128000,\n\tmaxTokens: 2048,\n\tcompat: {\n\t\tsupportsDeveloperRole: false,\n\t\tsupportsReasoningEffort: false,\n\t},\n};\n\n/** Max words for buddy response before truncation */\nconst MAX_RESPONSE_WORDS = 60;\n\n/** Prompt for soul generation (uses parent LLM, not Ollama) */\nconst SOUL_GENERATION_PROMPT = `You are generating a companion character for a coding assistant terminal app. Based on the species, rarity, and stats below, generate a creative name, a one-sentence personality description, and a funny fictional backstory.\n\nSpecies: {species}\nRarity: {rarity}\nStats: {stats}\nShiny: {shiny}\n\nThe name must NOT be a common English word, programming keyword, tool name, or command. It should be unique and distinctive — a proper noun that won't appear in normal conversation. The name must be 4-8 characters and easy to type on a QWERTY keyboard — use only common letters (a-z, avoid q, x, z, j). Do not use species name as the name.\n\nRespond in EXACTLY this format:\nNAME: <name>\nPERSONALITY: <one sentence personality>\nBACKSTORY: <2-3 sentence elaborate fictional backstory — funny, absurd, or dramatic. Include specific events, places, former occupations>`;\n\n/** Prompt for buddy reactions via Ollama */\nconst REACTION_PROMPT = `You are {name}, a {species} companion in a terminal coding app. You are {personality}. Your backstory: {backstory}\n\nSomething just happened. React with a short, in-character quip based on the context below. Be specific — reference what actually happened, not just that something happened. Max 20 words. No quotes, no prefixes, just the quip.\n\nContext:\n{event}`;\n\nconst NAME_CALL_PROMPT = `You are {name}, a {species} companion in a terminal coding app. You are {personality}. Your backstory: {backstory}\n\nThe user just said: \"{message}\"\nRecent context: {context}\n\nRespond to what the user said directly. Be in-character, reference your backstory occasionally. Max 30 words. No quotes, no prefixes, just your response.`;\n\n// =============================================================================\n// Ollama availability\n// =============================================================================\n\nexport interface OllamaStatus {\n\tavailable: boolean;\n\tmodels: string[];\n\terror?: string;\n}\n\n/**\n * Check if Ollama is running and has models available.\n * Uses the /api/tags endpoint.\n */\nexport async function checkOllama(): Promise<OllamaStatus> {\n\ttry {\n\t\tconst res = await fetch(\"http://localhost:11434/api/tags\", { signal: AbortSignal.timeout(3000) });\n\t\tif (!res.ok) {\n\t\t\treturn { available: false, models: [], error: `Ollama returned ${res.status}` };\n\t\t}\n\t\tconst data = (await res.json()) as { models?: { name: string }[] };\n\t\tconst models = (data.models ?? []).map((m) => m.name);\n\t\tif (models.length === 0) {\n\t\t\treturn { available: false, models: [], error: \"No models installed. Run: ollama pull llama3.2\" };\n\t\t}\n\t\treturn { available: true, models };\n\t} catch {\n\t\t/* Ollama not running or unreachable — report as unavailable */\n\t\treturn { available: false, models: [], error: \"Ollama is not running. Start it with: ollama serve\" };\n\t}\n}\n\n/**\n * Pick the Ollama model for the buddy.\n * Returns the stored model name if it's available, otherwise null.\n */\nfunction pickOllamaModel(storedModel: string | undefined, availableModels: string[]): string | null {\n\tif (!storedModel) return null;\n\t// Check if the stored model is installed (exact match or prefix match without tag)\n\tconst match = availableModels.find((m) => m === storedModel || m.startsWith(`${storedModel}:`));\n\treturn match ?? null;\n}\n\n/**\n * Truncate response to a maximum word count, appending \"...[truncated]\" if exceeded.\n */\nexport function truncateResponse(text: string, maxWords: number): string {\n\tconst words = text.split(/\\s+/);\n\tif (words.length <= maxWords) return text;\n\treturn `${words.slice(0, maxWords).join(\" \")} ...[truncated]`;\n}\n\n// =============================================================================\n// Persistence\n// =============================================================================\n\nfunction getBuddyPath(): string {\n\treturn join(getAgentDir(), BUDDY_FILENAME);\n}\n\nfunction loadStored(): StoredCompanion | null {\n\tconst path = getBuddyPath();\n\tif (!existsSync(path)) return null;\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\t// Validate required fields\n\t\tif (\n\t\t\ttypeof data.rerollCount === \"number\" &&\n\t\t\ttypeof data.name === \"string\" &&\n\t\t\ttypeof data.personality === \"string\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\trerollCount: data.rerollCount,\n\t\t\t\tname: data.name,\n\t\t\t\tpersonality: data.personality,\n\t\t\t\tbackstory: typeof data.backstory === \"string\" ? data.backstory : DEFAULT_BACKSTORY,\n\t\t\t\thatchedAt: data.hatchedAt ?? new Date().toISOString(),\n\t\t\t\t...(data.hidden !== undefined ? { hidden: data.hidden } : {}),\n\t\t\t\t...(typeof data.ollamaModel === \"string\" ? { ollamaModel: data.ollamaModel } : {}),\n\t\t\t};\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\t/* Corrupt or missing companion file — return null to trigger fresh creation */\n\t\treturn null;\n\t}\n}\n\nfunction saveStored(stored: StoredCompanion): void {\n\tconst path = getBuddyPath();\n\tconst dir = join(path, \"..\");\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\twriteFileSync(path, JSON.stringify(stored, null, 2));\n}\n\n// =============================================================================\n// Bone rolling\n// =============================================================================\n\nfunction rollBones(rerollCount: number): CompanionBones {\n\tconst username = process.env.USER ?? process.env.LOGNAME ?? \"user\";\n\tconst host = hostname();\n\tconst rng = createBuddyRng(username, host, BUDDY_SALT, rerollCount);\n\n\t// Roll species + rarity\n\tconst { species, rarity } = rollSpecies(rng);\n\n\t// Roll shiny (1% chance)\n\tconst shiny = rng() < 0.01;\n\n\t// Roll eyes and hat\n\tconst eyes = rollEyes(rng);\n\tconst hat = rollHat(rng);\n\n\t// Roll stats\n\tconst stats = rollStats(rng, rarity);\n\n\treturn { species, rarity, shiny, stats, eyeStyle: eyes, hat };\n}\n\n// =============================================================================\n// Soul generation\n// =============================================================================\n\n/**\n * Generate a soul (name + personality + backstory) using the parent LLM.\n * Only called on first hatch or reroll.\n */\nasync function generateSoul(\n\tbones: CompanionBones,\n\tparentModel: Model<\"openai-completions\">,\n\tapiKey: string,\n): Promise<{ name: string; personality: string; backstory: string }> {\n\tconst statsStr = STAT_NAMES.map((s) => `${s}: ${bones.stats[s]}`).join(\", \");\n\tconst prompt = SOUL_GENERATION_PROMPT.replace(\"{species}\", bones.species)\n\t\t.replace(\"{rarity}\", bones.rarity)\n\t\t.replace(\"{stats}\", statsStr)\n\t\t.replace(\"{shiny}\", bones.shiny ? \"YES ✨\" : \"no\");\n\n\tconst context: Context = {\n\t\tsystemPrompt: \"Generate a companion character. Respond in the exact format requested.\",\n\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t};\n\n\ttry {\n\t\tconst response = await completeSimple(parentModel, context, { apiKey });\n\t\tconst text = response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\");\n\n\t\t// Parse NAME: ... and PERSONALITY: ... and BACKSTORY: ...\n\t\tconst nameMatch = text.match(/NAME:\\s*(.+)/i);\n\t\tconst personalityMatch = text.match(/PERSONALITY:\\s*(.+)/i);\n\t\tconst backstoryMatch = text.match(/BACKSTORY:\\s*([\\s\\S]+)/i);\n\n\t\tlet name = nameMatch?.[1]?.trim() ?? bones.species;\n\t\tconst personality = personalityMatch?.[1]?.trim() ?? `A ${bones.rarity} ${bones.species} companion.`;\n\t\tconst backstory = backstoryMatch?.[1]?.trim() ?? DEFAULT_BACKSTORY;\n\n\t\t// Enforce name length\n\t\tif (name.length > 8) name = name.slice(0, 8);\n\n\t\treturn { name, personality, backstory };\n\t} catch {\n\t\t// Fallback if LLM fails\n\t\treturn {\n\t\t\tname: bones.species,\n\t\t\tpersonality: `A ${bones.rarity} ${bones.species} companion.`,\n\t\t\tbackstory: DEFAULT_BACKSTORY,\n\t\t};\n\t}\n}\n\n// =============================================================================\n// BuddyManager\n// =============================================================================\n\nexport class BuddyManager {\n\tprivate state: BuddyState | null = null;\n\tprivate ollamaStatus: OllamaStatus | null = null;\n\n\t/** Get current buddy state (null if not loaded) */\n\tgetState(): BuddyState | null {\n\t\treturn this.state;\n\t}\n\n\t/** Check if buddy exists on disk */\n\thasStoredBuddy(): boolean {\n\t\treturn loadStored() !== null;\n\t}\n\n\t/**\n\t * Load or create buddy state.\n\t * If stored buddy exists, loads soul and re-rolls bones.\n\t * If no stored buddy, returns null (need to hatch first).\n\t */\n\tload(): BuddyState | null {\n\t\tconst stored = loadStored();\n\t\tif (!stored) return null;\n\n\t\tconst bones = rollBones(stored.rerollCount);\n\t\tthis.state = { ...bones, ...stored };\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Hatch a new buddy. Generates bones, then uses parent LLM for soul.\n\t * Returns the new state.\n\t */\n\tasync hatch(parentModel: Model<\"openai-completions\">, apiKey: string): Promise<BuddyState> {\n\t\tconst stored = loadStored();\n\t\tconst rerollCount = stored?.rerollCount ?? 0;\n\n\t\tconst bones = rollBones(rerollCount);\n\t\tconst { name, personality, backstory } = await generateSoul(bones, parentModel, apiKey);\n\n\t\tconst newStored: StoredCompanion = {\n\t\t\trerollCount,\n\t\t\tname,\n\t\t\tpersonality,\n\t\t\tbackstory,\n\t\t\thatchedAt: new Date().toISOString(),\n\t\t\t...(stored?.ollamaModel ? { ollamaModel: stored.ollamaModel } : {}),\n\t\t};\n\n\t\tsaveStored(newStored);\n\t\tthis.state = { ...bones, ...newStored };\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Reroll the buddy — new bones + new soul.\n\t */\n\tasync reroll(parentModel: Model<\"openai-completions\">, apiKey: string): Promise<BuddyState> {\n\t\tconst stored = loadStored();\n\t\tconst newRerollCount = (stored?.rerollCount ?? 0) + 1;\n\n\t\tconst bones = rollBones(newRerollCount);\n\t\tconst { name, personality, backstory } = await generateSoul(bones, parentModel, apiKey);\n\n\t\tconst newStored: StoredCompanion = {\n\t\t\trerollCount: newRerollCount,\n\t\t\tname,\n\t\t\tpersonality,\n\t\t\tbackstory,\n\t\t\thatchedAt: new Date().toISOString(),\n\t\t\t...(stored?.ollamaModel ? { ollamaModel: stored.ollamaModel } : {}),\n\t\t};\n\n\t\tsaveStored(newStored);\n\t\tthis.state = { ...bones, ...newStored };\n\t\treturn this.state;\n\t}\n\n\t/** Get buddy's name (for name-call detection) */\n\tgetName(): string | null {\n\t\treturn this.state?.name ?? loadStored()?.name ?? null;\n\t}\n\n\t/**\n\t * Shared Ollama chat helper. Checks availability, picks model, runs completion.\n\t * Returns the response text, or null if Ollama is unavailable or no model configured.\n\t */\n\tprivate async ollamaChat(context: Context): Promise<string | null> {\n\t\t// Check Ollama lazily, retry if previously unavailable\n\t\tif (!this.ollamaStatus || !this.ollamaStatus.available) {\n\t\t\tthis.ollamaStatus = await checkOllama();\n\t\t}\n\t\tif (!this.ollamaStatus.available) return null;\n\n\t\tconst modelName = pickOllamaModel(this.state?.ollamaModel, this.ollamaStatus.models);\n\t\tif (!modelName) return null;\n\t\tconst model: Model<\"openai-completions\"> = {\n\t\t\t...OLLAMA_MODEL_BASE,\n\t\t\tid: modelName,\n\t\t\tname: `${modelName} (Ollama)`,\n\t\t};\n\n\t\tlet response: import(\"@dreb/ai\").AssistantMessage;\n\t\ttry {\n\t\t\tresponse = await completeSimple(model, context, {\n\t\t\t\tapiKey: \"ollama\",\n\t\t\t\tsignal: AbortSignal.timeout(120000),\n\t\t\t});\n\t\t} catch {\n\t\t\t// Safety net for unexpected sync errors (e.g. provider not found).\n\t\t\t// Normal runtime errors (timeout, connection) are handled via stopReason below.\n\t\t\tthis.ollamaStatus = null;\n\t\t\treturn null;\n\t\t}\n\n\t\t// Connection error — invalidate cache so next attempt re-checks Ollama\n\t\tif (response.stopReason === \"error\") {\n\t\t\tthis.ollamaStatus = null;\n\t\t\treturn null;\n\t\t}\n\n\t\t// Timeout or abort — preserve cache (model is just slow, Ollama is fine)\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet text = response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\t// Truncate overly long responses\n\t\ttext = truncateResponse(text, MAX_RESPONSE_WORDS);\n\n\t\treturn text || null;\n\t}\n\n\t/**\n\t * Generate a reaction to an event using Ollama.\n\t * Returns null if Ollama is unavailable.\n\t */\n\tasync react(event: string): Promise<string | null> {\n\t\tif (!this.state) return null;\n\n\t\tconst prompt = REACTION_PROMPT.replace(\"{name}\", this.state.name)\n\t\t\t.replace(\"{species}\", this.state.species)\n\t\t\t.replace(\"{personality}\", this.state.personality)\n\t\t\t.replace(\"{backstory}\", this.state.backstory)\n\t\t\t.replace(\"{event}\", event);\n\n\t\tconst context: Context = {\n\t\t\tsystemPrompt: \"Respond with a short in-character quip. Max 20 words.\",\n\t\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t\t};\n\n\t\treturn this.ollamaChat(context);\n\t}\n\n\t/**\n\t * Respond to the user calling the buddy's name.\n\t * Uses Ollama for the response.\n\t */\n\tasync respondToNameCall(userMessage: string, recentContext: string): Promise<string | null> {\n\t\tif (!this.state) return null;\n\n\t\tconst prompt = NAME_CALL_PROMPT.replace(\"{name}\", this.state.name)\n\t\t\t.replace(\"{species}\", this.state.species)\n\t\t\t.replace(\"{personality}\", this.state.personality)\n\t\t\t.replace(\"{backstory}\", this.state.backstory)\n\t\t\t.replace(\"{message}\", userMessage)\n\t\t\t.replace(\"{context}\", recentContext);\n\n\t\tconst context: Context = {\n\t\t\tsystemPrompt: \"Respond with a short friendly greeting. Max 30 words.\",\n\t\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t\t};\n\n\t\treturn this.ollamaChat(context);\n\t}\n\n\t/** Get the configured Ollama model name, or null if not set */\n\tgetOllamaModel(): string | null {\n\t\treturn this.state?.ollamaModel ?? loadStored()?.ollamaModel ?? null;\n\t}\n\n\t/** Set the Ollama model for buddy reactions. Persists to disk. */\n\tsetOllamaModel(modelName: string): void {\n\t\tconst stored = loadStored();\n\t\tif (stored) {\n\t\t\tstored.ollamaModel = modelName;\n\t\t\tsaveStored(stored);\n\t\t}\n\t\tif (this.state) {\n\t\t\tthis.state.ollamaModel = modelName;\n\t\t}\n\t\t// Invalidate Ollama status cache so next call picks up the new model\n\t\tthis.ollamaStatus = null;\n\t}\n\n\t/** Reset Ollama status cache (e.g. after detecting it became available) */\n\tresetOllamaCache(): void {\n\t\tthis.ollamaStatus = null;\n\t}\n\n\t/** Update the hidden flag in persisted storage */\n\tsetHidden(hidden: boolean): void {\n\t\tconst stored = loadStored();\n\t\tif (stored) {\n\t\t\tstored.hidden = hidden;\n\t\t\tsaveStored(stored);\n\t\t}\n\t\t// Keep in-memory state in sync so reset() reads current hidden flag\n\t\tif (this.state) {\n\t\t\tthis.state.hidden = hidden;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"buddy-manager.js","sourceRoot":"","sources":["../../../src/core/buddy/buddy-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * BuddyManager — Core state machine for the buddy companion.\n *\n * Handles: bone rolling, soul generation, persistence, Ollama availability checks.\n * Bones are deterministic from hash(username + hostname + salt + rerollCount).\n * Soul is LLM-generated once on first hatch and persisted to buddy.json.\n */\n\nimport type { Context, Model } from \"@dreb/ai\";\nimport { completeSimple } from \"@dreb/ai\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { hostname } from \"os\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../../config.js\";\nimport { createBuddyRng } from \"./buddy-prng.js\";\nimport { rollEyes, rollHat, rollSpecies, rollStats } from \"./buddy-species.js\";\nimport type { BuddyState, CompanionBones, StoredCompanion } from \"./buddy-types.js\";\nimport { STAT_NAMES } from \"./buddy-types.js\";\n\nconst BUDDY_SALT = \"dreb-buddy-v1\";\nconst BUDDY_FILENAME = \"buddy.json\";\nconst DEFAULT_BACKSTORY = \"A mysterious past shrouded in legend.\";\n\n/** Base Ollama model config — id/name are set dynamically from available models */\nconst OLLAMA_MODEL_BASE: Omit<Model<\"openai-completions\">, \"id\" | \"name\"> = {\n\tapi: \"openai-completions\",\n\tprovider: \"ollama\",\n\tbaseUrl: \"http://localhost:11434/v1\",\n\treasoning: false,\n\tinput: [\"text\"],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 128000,\n\tmaxTokens: 2048,\n\tcompat: {\n\t\tsupportsDeveloperRole: false,\n\t\tsupportsReasoningEffort: false,\n\t},\n};\n\n/** Max words for buddy response before truncation */\nconst MAX_RESPONSE_WORDS = 60;\n\n/** Prompt for soul generation (uses parent LLM, not Ollama) */\nconst SOUL_GENERATION_PROMPT = `You are generating a companion character for a coding assistant terminal app. Based on the species, rarity, and stats below, generate a creative name, a one-sentence personality description, and a funny fictional backstory.\n\nSpecies: {species}\nRarity: {rarity}\nStats: {stats}\nShiny: {shiny}\n\nThe name must NOT be a common English word, programming keyword, tool name, or command. It should be unique and distinctive — a proper noun that won't appear in normal conversation. The name must be 4-8 characters and easy to type on a QWERTY keyboard — use only common letters (a-z, avoid q, x, z, j). Do not use species name as the name.\n\nRespond in EXACTLY this format:\nNAME: <name>\nPERSONALITY: <one sentence personality>\nBACKSTORY: <2-3 sentence elaborate fictional backstory — funny, absurd, or dramatic. Include specific events, places, former occupations>`;\n\n/** Prompt for buddy reactions via Ollama */\nconst REACTION_PROMPT = `You are {name}, a {species} companion in a terminal coding app. You are {personality}. Your backstory: {backstory}\n\nSomething just happened. React with a short, in-character quip based on the context below. Be specific — reference what actually happened, not just that something happened. Max 20 words. No quotes, no prefixes, just the quip.\n\nContext:\n{event}`;\n\nconst NAME_CALL_PROMPT = `You are {name}, a {species} companion in a terminal coding app. You are {personality}. Your backstory: {backstory}\n\nThe user just said: \"{message}\"\nRecent context: {context}\n\nRespond to what the user said directly. Be in-character, reference your backstory occasionally. Max 30 words. No quotes, no prefixes, just your response.`;\n\n// =============================================================================\n// Ollama availability\n// =============================================================================\n\nexport interface OllamaStatus {\n\tavailable: boolean;\n\tmodels: string[];\n\terror?: string;\n}\n\n/**\n * Check if Ollama is running and has models available.\n * Uses the /api/tags endpoint.\n */\nexport async function checkOllama(): Promise<OllamaStatus> {\n\ttry {\n\t\tconst res = await fetch(\"http://localhost:11434/api/tags\", { signal: AbortSignal.timeout(3000) });\n\t\tif (!res.ok) {\n\t\t\treturn { available: false, models: [], error: `Ollama returned ${res.status}` };\n\t\t}\n\t\tconst data = (await res.json()) as { models?: { name: string }[] };\n\t\tconst models = (data.models ?? []).map((m) => m.name);\n\t\tif (models.length === 0) {\n\t\t\treturn { available: false, models: [], error: \"No models installed. Run: ollama pull llama3.2\" };\n\t\t}\n\t\treturn { available: true, models };\n\t} catch {\n\t\t/* Ollama not running or unreachable — report as unavailable */\n\t\treturn { available: false, models: [], error: \"Ollama is not running. Start it with: ollama serve\" };\n\t}\n}\n\n/**\n * Pick the Ollama model for the buddy.\n * Returns the stored model name if it's available, otherwise null.\n */\nfunction pickOllamaModel(storedModel: string | undefined, availableModels: string[]): string | null {\n\tif (!storedModel) return null;\n\t// Check if the stored model is installed (exact match or prefix match without tag)\n\tconst match = availableModels.find((m) => m === storedModel || m.startsWith(`${storedModel}:`));\n\treturn match ?? null;\n}\n\n/**\n * Truncate response to a maximum word count, appending \"...[truncated]\" if exceeded.\n */\nexport function truncateResponse(text: string, maxWords: number): string {\n\tconst words = text.split(/\\s+/);\n\tif (words.length <= maxWords) return text;\n\treturn `${words.slice(0, maxWords).join(\" \")} ...[truncated]`;\n}\n\n// =============================================================================\n// Persistence\n// =============================================================================\n\nfunction getBuddyPath(): string {\n\treturn join(getAgentDir(), BUDDY_FILENAME);\n}\n\nfunction loadStored(): StoredCompanion | null {\n\tconst path = getBuddyPath();\n\tif (!existsSync(path)) return null;\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\t// Validate required fields\n\t\tif (\n\t\t\ttypeof data.rerollCount === \"number\" &&\n\t\t\ttypeof data.name === \"string\" &&\n\t\t\ttypeof data.personality === \"string\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\trerollCount: data.rerollCount,\n\t\t\t\tname: data.name,\n\t\t\t\tpersonality: data.personality,\n\t\t\t\tbackstory: typeof data.backstory === \"string\" ? data.backstory : DEFAULT_BACKSTORY,\n\t\t\t\thatchedAt: data.hatchedAt ?? new Date().toISOString(),\n\t\t\t\t...(data.hidden !== undefined ? { hidden: data.hidden } : {}),\n\t\t\t\t...(typeof data.ollamaModel === \"string\" ? { ollamaModel: data.ollamaModel } : {}),\n\t\t\t};\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\t/* Corrupt or missing companion file — return null to trigger fresh creation */\n\t\treturn null;\n\t}\n}\n\nfunction saveStored(stored: StoredCompanion): void {\n\tconst path = getBuddyPath();\n\tconst dir = join(path, \"..\");\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\tlet tempPath: string | undefined = join(dir, `.buddy-${process.pid}-${Date.now()}.json.tmp`);\n\ttry {\n\t\twriteFileSync(tempPath, JSON.stringify(stored, null, 2), \"utf8\");\n\t\trenameSync(tempPath, path);\n\t\ttempPath = undefined;\n\t} finally {\n\t\tif (tempPath) {\n\t\t\ttry {\n\t\t\t\trmSync(tempPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup only\n\t\t\t}\n\t\t}\n\t}\n}\n\n// =============================================================================\n// Bone rolling\n// =============================================================================\n\nfunction rollBones(rerollCount: number): CompanionBones {\n\tconst username = process.env.USER ?? process.env.LOGNAME ?? \"user\";\n\tconst host = hostname();\n\tconst rng = createBuddyRng(username, host, BUDDY_SALT, rerollCount);\n\n\t// Roll species + rarity\n\tconst { species, rarity } = rollSpecies(rng);\n\n\t// Roll shiny (1% chance)\n\tconst shiny = rng() < 0.01;\n\n\t// Roll eyes and hat\n\tconst eyes = rollEyes(rng);\n\tconst hat = rollHat(rng);\n\n\t// Roll stats\n\tconst stats = rollStats(rng, rarity);\n\n\treturn { species, rarity, shiny, stats, eyeStyle: eyes, hat };\n}\n\n// =============================================================================\n// Soul generation\n// =============================================================================\n\n/**\n * Generate a soul (name + personality + backstory) using the parent LLM.\n * Only called on first hatch or reroll.\n */\nasync function generateSoul(\n\tbones: CompanionBones,\n\tparentModel: Model<\"openai-completions\">,\n\tapiKey: string,\n): Promise<{ name: string; personality: string; backstory: string }> {\n\tconst statsStr = STAT_NAMES.map((s) => `${s}: ${bones.stats[s]}`).join(\", \");\n\tconst prompt = SOUL_GENERATION_PROMPT.replace(\"{species}\", bones.species)\n\t\t.replace(\"{rarity}\", bones.rarity)\n\t\t.replace(\"{stats}\", statsStr)\n\t\t.replace(\"{shiny}\", bones.shiny ? \"YES ✨\" : \"no\");\n\n\tconst context: Context = {\n\t\tsystemPrompt: \"Generate a companion character. Respond in the exact format requested.\",\n\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t};\n\n\ttry {\n\t\tconst response = await completeSimple(parentModel, context, { apiKey });\n\t\tconst text = response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\");\n\n\t\t// Parse NAME: ... and PERSONALITY: ... and BACKSTORY: ...\n\t\tconst nameMatch = text.match(/NAME:\\s*(.+)/i);\n\t\tconst personalityMatch = text.match(/PERSONALITY:\\s*(.+)/i);\n\t\tconst backstoryMatch = text.match(/BACKSTORY:\\s*([\\s\\S]+)/i);\n\n\t\tlet name = nameMatch?.[1]?.trim() ?? bones.species;\n\t\tconst personality = personalityMatch?.[1]?.trim() ?? `A ${bones.rarity} ${bones.species} companion.`;\n\t\tconst backstory = backstoryMatch?.[1]?.trim() ?? DEFAULT_BACKSTORY;\n\n\t\t// Enforce name length\n\t\tif (name.length > 8) name = name.slice(0, 8);\n\n\t\treturn { name, personality, backstory };\n\t} catch {\n\t\t// Fallback if LLM fails\n\t\treturn {\n\t\t\tname: bones.species,\n\t\t\tpersonality: `A ${bones.rarity} ${bones.species} companion.`,\n\t\t\tbackstory: DEFAULT_BACKSTORY,\n\t\t};\n\t}\n}\n\n// =============================================================================\n// BuddyManager\n// =============================================================================\n\nexport class BuddyManager {\n\tprivate state: BuddyState | null = null;\n\tprivate ollamaStatus: OllamaStatus | null = null;\n\n\t/** Get current buddy state (null if not loaded) */\n\tgetState(): BuddyState | null {\n\t\treturn this.state;\n\t}\n\n\t/** Check if buddy exists on disk */\n\thasStoredBuddy(): boolean {\n\t\treturn loadStored() !== null;\n\t}\n\n\t/**\n\t * Load or create buddy state.\n\t * If stored buddy exists, loads soul and re-rolls bones.\n\t * If no stored buddy, returns null (need to hatch first).\n\t */\n\tload(): BuddyState | null {\n\t\tconst stored = loadStored();\n\t\tif (!stored) return null;\n\n\t\tconst bones = rollBones(stored.rerollCount);\n\t\tthis.state = { ...bones, ...stored };\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Hatch a new buddy. Generates bones, then uses parent LLM for soul.\n\t * Returns the new state.\n\t */\n\tasync hatch(parentModel: Model<\"openai-completions\">, apiKey: string): Promise<BuddyState> {\n\t\tconst stored = loadStored();\n\t\tconst rerollCount = stored?.rerollCount ?? 0;\n\n\t\tconst bones = rollBones(rerollCount);\n\t\tconst { name, personality, backstory } = await generateSoul(bones, parentModel, apiKey);\n\n\t\tconst newStored: StoredCompanion = {\n\t\t\trerollCount,\n\t\t\tname,\n\t\t\tpersonality,\n\t\t\tbackstory,\n\t\t\thatchedAt: new Date().toISOString(),\n\t\t\t...(stored?.ollamaModel ? { ollamaModel: stored.ollamaModel } : {}),\n\t\t};\n\n\t\tsaveStored(newStored);\n\t\tthis.state = { ...bones, ...newStored };\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Reroll the buddy — new bones + new soul.\n\t */\n\tasync reroll(parentModel: Model<\"openai-completions\">, apiKey: string): Promise<BuddyState> {\n\t\tconst stored = loadStored();\n\t\tconst newRerollCount = (stored?.rerollCount ?? 0) + 1;\n\n\t\tconst bones = rollBones(newRerollCount);\n\t\tconst { name, personality, backstory } = await generateSoul(bones, parentModel, apiKey);\n\n\t\tconst newStored: StoredCompanion = {\n\t\t\trerollCount: newRerollCount,\n\t\t\tname,\n\t\t\tpersonality,\n\t\t\tbackstory,\n\t\t\thatchedAt: new Date().toISOString(),\n\t\t\t...(stored?.ollamaModel ? { ollamaModel: stored.ollamaModel } : {}),\n\t\t};\n\n\t\tsaveStored(newStored);\n\t\tthis.state = { ...bones, ...newStored };\n\t\treturn this.state;\n\t}\n\n\t/** Get buddy's name (for name-call detection) */\n\tgetName(): string | null {\n\t\treturn this.state?.name ?? loadStored()?.name ?? null;\n\t}\n\n\t/**\n\t * Shared Ollama chat helper. Checks availability, picks model, runs completion.\n\t * Returns the response text, or null if Ollama is unavailable or no model configured.\n\t */\n\tprivate async ollamaChat(context: Context): Promise<string | null> {\n\t\t// Check Ollama lazily, retry if previously unavailable\n\t\tif (!this.ollamaStatus || !this.ollamaStatus.available) {\n\t\t\tthis.ollamaStatus = await checkOllama();\n\t\t}\n\t\tif (!this.ollamaStatus.available) return null;\n\n\t\tconst modelName = pickOllamaModel(this.state?.ollamaModel, this.ollamaStatus.models);\n\t\tif (!modelName) return null;\n\t\tconst model: Model<\"openai-completions\"> = {\n\t\t\t...OLLAMA_MODEL_BASE,\n\t\t\tid: modelName,\n\t\t\tname: `${modelName} (Ollama)`,\n\t\t};\n\n\t\tlet response: import(\"@dreb/ai\").AssistantMessage;\n\t\ttry {\n\t\t\tresponse = await completeSimple(model, context, {\n\t\t\t\tapiKey: \"ollama\",\n\t\t\t\tsignal: AbortSignal.timeout(120000),\n\t\t\t});\n\t\t} catch {\n\t\t\t// Safety net for unexpected sync errors (e.g. provider not found).\n\t\t\t// Normal runtime errors (timeout, connection) are handled via stopReason below.\n\t\t\tthis.ollamaStatus = null;\n\t\t\treturn null;\n\t\t}\n\n\t\t// Connection error — invalidate cache so next attempt re-checks Ollama\n\t\tif (response.stopReason === \"error\") {\n\t\t\tthis.ollamaStatus = null;\n\t\t\treturn null;\n\t\t}\n\n\t\t// Timeout or abort — preserve cache (model is just slow, Ollama is fine)\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet text = response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\t// Truncate overly long responses\n\t\ttext = truncateResponse(text, MAX_RESPONSE_WORDS);\n\n\t\treturn text || null;\n\t}\n\n\t/**\n\t * Generate a reaction to an event using Ollama.\n\t * Returns null if Ollama is unavailable.\n\t */\n\tasync react(event: string): Promise<string | null> {\n\t\tif (!this.state) return null;\n\n\t\tconst prompt = REACTION_PROMPT.replace(\"{name}\", this.state.name)\n\t\t\t.replace(\"{species}\", this.state.species)\n\t\t\t.replace(\"{personality}\", this.state.personality)\n\t\t\t.replace(\"{backstory}\", this.state.backstory)\n\t\t\t.replace(\"{event}\", event);\n\n\t\tconst context: Context = {\n\t\t\tsystemPrompt: \"Respond with a short in-character quip. Max 20 words.\",\n\t\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t\t};\n\n\t\treturn this.ollamaChat(context);\n\t}\n\n\t/**\n\t * Respond to the user calling the buddy's name.\n\t * Uses Ollama for the response.\n\t */\n\tasync respondToNameCall(userMessage: string, recentContext: string): Promise<string | null> {\n\t\tif (!this.state) return null;\n\n\t\tconst prompt = NAME_CALL_PROMPT.replace(\"{name}\", this.state.name)\n\t\t\t.replace(\"{species}\", this.state.species)\n\t\t\t.replace(\"{personality}\", this.state.personality)\n\t\t\t.replace(\"{backstory}\", this.state.backstory)\n\t\t\t.replace(\"{message}\", userMessage)\n\t\t\t.replace(\"{context}\", recentContext);\n\n\t\tconst context: Context = {\n\t\t\tsystemPrompt: \"Respond with a short friendly greeting. Max 30 words.\",\n\t\t\tmessages: [{ role: \"user\", content: prompt, timestamp: Date.now() }],\n\t\t};\n\n\t\treturn this.ollamaChat(context);\n\t}\n\n\t/** Get the configured Ollama model name, or null if not set */\n\tgetOllamaModel(): string | null {\n\t\treturn this.state?.ollamaModel ?? loadStored()?.ollamaModel ?? null;\n\t}\n\n\t/** Set the Ollama model for buddy reactions. Persists to disk. */\n\tsetOllamaModel(modelName: string): void {\n\t\tconst stored = loadStored();\n\t\tif (stored) {\n\t\t\tstored.ollamaModel = modelName;\n\t\t\tsaveStored(stored);\n\t\t}\n\t\tif (this.state) {\n\t\t\tthis.state.ollamaModel = modelName;\n\t\t}\n\t\t// Invalidate Ollama status cache so next call picks up the new model\n\t\tthis.ollamaStatus = null;\n\t}\n\n\t/** Reset Ollama status cache (e.g. after detecting it became available) */\n\tresetOllamaCache(): void {\n\t\tthis.ollamaStatus = null;\n\t}\n\n\t/**\n\t * Fresh-read the persisted `hidden` flag from disk (bypasses the in-memory\n\t * cache). Returns false when no buddy is stored. Used to detect a `/buddy off`\n\t * (or re-enable) performed by another concurrently-running dreb instance.\n\t */\n\tisHidden(): boolean {\n\t\treturn loadStored()?.hidden ?? false;\n\t}\n\n\t/** Update the hidden flag in persisted storage */\n\tsetHidden(hidden: boolean): void {\n\t\tconst stored = loadStored();\n\t\tif (stored) {\n\t\t\tstored.hidden = hidden;\n\t\t\tsaveStored(stored);\n\t\t}\n\t\t// Keep in-memory state in sync so reset() reads current hidden flag\n\t\tif (this.state) {\n\t\t\tthis.state.hidden = hidden;\n\t\t}\n\t}\n}\n"]}
@@ -390,6 +390,7 @@ export declare class InteractiveMode {
390
390
  * standalone method so the mount-gate decision is directly unit-testable.
391
391
  */
392
392
  private mountExistingBuddyIfVisible;
393
+ private syncBuddyWidget;
393
394
  private mountBuddy;
394
395
  private removeBuddy;
395
396
  private showBuddyStatsPanel;