diff --git a/apps/x/packages/core/package.json b/apps/x/packages/core/package.json index 2429204d..d660eeaa 100644 --- a/apps/x/packages/core/package.json +++ b/apps/x/packages/core/package.json @@ -5,7 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { - "build": "rm -rf dist && tsc", + "build": "rm -rf dist && tsc && mkdir -p dist/knowledge && cp src/knowledge/note_creation.md dist/knowledge/", "dev": "tsc -w" }, "dependencies": { diff --git a/apps/x/packages/core/src/agents/runtime.ts b/apps/x/packages/core/src/agents/runtime.ts index 5ddf0f13..ddc05816 100644 --- a/apps/x/packages/core/src/agents/runtime.ts +++ b/apps/x/packages/core/src/agents/runtime.ts @@ -22,6 +22,7 @@ import { IMessageQueue } from "../application/lib/message-queue.js"; import { IRunsRepo } from "../runs/repo.js"; import { IRunsLock } from "../runs/lock.js"; import { PrefixLogger } from "@x/shared"; +import { parse } from "yaml"; export interface IAgentRuntime { trigger(runId: string): Promise; @@ -243,6 +244,38 @@ export async function loadAgent(id: string): Promise> { if (id === "copilot" || id === "rowboatx") { return CopilotAgent; } + + // Special case: load note_creation agent from checked-in file + if (id === "note_creation") { + const currentDir = path.dirname(new URL(import.meta.url).pathname); + // File is copied to dist/knowledge during build + const agentFilePath = path.join(currentDir, "../knowledge/note_creation.md"); + const raw = fs.readFileSync(agentFilePath, "utf8"); + + let agent: z.infer = { + name: "note_creation", + instructions: raw, + }; + + // Parse frontmatter if present + if (raw.startsWith("---")) { + const end = raw.indexOf("\n---", 3); + if (end !== -1) { + const fm = raw.slice(3, end).trim(); + const content = raw.slice(end + 4).trim(); + const yaml = parse(fm); + const parsed = Agent.omit({ name: true, instructions: true }).parse(yaml); + agent = { + ...agent, + ...parsed, + instructions: content, + }; + } + } + + return agent; + } + const repo = container.resolve('agentsRepo'); return await repo.fetch(id); } @@ -655,7 +688,10 @@ export async function* streamAgent({ agent.instructions, tools, )) { - loopLogger.log('got llm-stream-event:', event.type) + // Only log significant events (not text-delta to reduce noise) + if (event.type !== 'text-delta') { + loopLogger.log('got llm-stream-event:', event.type); + } messageBuilder.ingest(event); yield* processEvent({ runId, diff --git a/apps/x/packages/core/src/knowledge/graph_state.ts b/apps/x/packages/core/src/knowledge/graph_state.ts index ad5ccfe3..aa6df169 100644 --- a/apps/x/packages/core/src/knowledge/graph_state.ts +++ b/apps/x/packages/core/src/knowledge/graph_state.ts @@ -101,7 +101,7 @@ export function markFileAsProcessed(filePath: string, state: GraphState): void { /** * Get list of files that need processing from a source directory - * Returns only new or changed files + * Returns only new or changed files, recursively traversing subdirectories */ export function getFilesToProcess( sourceDir: string, @@ -112,19 +112,27 @@ export function getFilesToProcess( } const filesToProcess: string[] = []; - const entries = fs.readdirSync(sourceDir); - for (const entry of entries) { - const fullPath = path.join(sourceDir, entry); - const stat = fs.statSync(fullPath); + // Recursive function to traverse directories + function traverseDirectory(dir: string) { + const entries = fs.readdirSync(dir); - if (stat.isFile() && entry.endsWith('.md')) { - if (hasFileChanged(fullPath, state)) { - filesToProcess.push(fullPath); + for (const entry of entries) { + const fullPath = path.join(dir, entry); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + // Recurse into subdirectories + traverseDirectory(fullPath); + } else if (stat.isFile() && entry.endsWith('.md')) { + if (hasFileChanged(fullPath, state)) { + filesToProcess.push(fullPath); + } } } } + traverseDirectory(sourceDir); return filesToProcess; }