refactor(agent): remove legacy memory subsystem
This commit is contained in:
parent
700e64342c
commit
0f5bd5fff1
26 changed files with 15 additions and 599 deletions
|
|
@ -48,7 +48,6 @@ ${cyan("Profile Structure:")}
|
|||
- soul.md Agent identity, personality and behavior
|
||||
- user.md Information about the user
|
||||
- workspace.md Workspace rules and conventions
|
||||
- memory.md Persistent knowledge
|
||||
|
||||
${cyan("Examples:")}
|
||||
${dim("# Create a new profile")}
|
||||
|
|
@ -94,7 +93,6 @@ function cmdNew(profileId: string | undefined) {
|
|||
console.log(" - soul.md (identity, personality and behavior)");
|
||||
console.log(" - user.md (information about the user)");
|
||||
console.log(" - workspace.md (workspace rules and conventions)");
|
||||
console.log(" - memory.md (persistent knowledge)");
|
||||
console.log("");
|
||||
console.log("Run interactive setup to personalize your agent:");
|
||||
console.log(` multica profile setup ${profileId}`);
|
||||
|
|
@ -165,11 +163,6 @@ function cmdShow(profileId: string | undefined) {
|
|||
console.log("");
|
||||
}
|
||||
|
||||
if (profile.memory) {
|
||||
console.log(`${green("=== memory.md ===")}`);
|
||||
console.log(profile.memory.trim());
|
||||
console.log("");
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdEdit(profileId: string | undefined) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ function printUsage() {
|
|||
console.log(`${cyan("Usage:")} pnpm agent:interactive [options]`);
|
||||
console.log("");
|
||||
console.log(`${cyan("Options:")}`);
|
||||
console.log(` ${yellow("--profile")} ID Load agent profile (identity, soul, tools, memory)`);
|
||||
console.log(` ${yellow("--profile")} ID Load agent profile (identity, soul, tools)`);
|
||||
console.log(` ${yellow("--provider")} NAME LLM provider (e.g., openai, anthropic, kimi)`);
|
||||
console.log(` ${yellow("--model")} NAME Model name`);
|
||||
console.log(` ${yellow("--system")} TEXT System prompt (ignored if --profile is set)`);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ function printUsage() {
|
|||
console.log(" echo \"your prompt\" | pnpm agent:cli");
|
||||
console.log("");
|
||||
console.log("Options:");
|
||||
console.log(" --profile ID Load agent profile (identity, soul, tools, memory)");
|
||||
console.log(" --profile ID Load agent profile (identity, soul, tools)");
|
||||
console.log(" --provider NAME LLM provider (e.g., openai, anthropic, kimi)");
|
||||
console.log(" --model NAME Model name");
|
||||
console.log(" --api-key KEY API key (overrides environment variable)");
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ function cmdNew(profileId: string | undefined) {
|
|||
console.log(" - soul.md (identity, personality and behavior)");
|
||||
console.log(" - user.md (information about the user)");
|
||||
console.log(" - workspace.md (workspace rules and conventions)");
|
||||
console.log(" - memory.md (persistent knowledge)");
|
||||
console.log("");
|
||||
console.log("Edit these files to customize your agent, then run:");
|
||||
console.log(` pnpm agent:cli --profile ${profileId} "Hello"`);
|
||||
|
|
@ -137,11 +136,6 @@ function cmdShow(profileId: string | undefined) {
|
|||
console.log("");
|
||||
}
|
||||
|
||||
if (profile.memory) {
|
||||
console.log("=== memory.md ===");
|
||||
console.log(profile.memory.trim());
|
||||
console.log("");
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdEdit(profileId: string | undefined) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const TOOL_GROUPS: Record<string, string[]> = {
|
|||
'group:fs': ['read', 'write', 'edit', 'glob'],
|
||||
'group:runtime': ['exec', 'process'],
|
||||
'group:web': ['web_search', 'web_fetch'],
|
||||
'group:memory': ['memory_search'],
|
||||
'group:subagent': ['delegate'],
|
||||
'group:cron': ['cron'],
|
||||
}
|
||||
|
|
@ -22,7 +21,6 @@ const ALL_KNOWN_TOOLS = [
|
|||
...TOOL_GROUPS['group:fs'],
|
||||
...TOOL_GROUPS['group:runtime'],
|
||||
...TOOL_GROUPS['group:web'],
|
||||
...TOOL_GROUPS['group:memory'],
|
||||
...TOOL_GROUPS['group:subagent'],
|
||||
...TOOL_GROUPS['group:cron'],
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
FolderOpen,
|
||||
Code,
|
||||
Globe,
|
||||
Brain,
|
||||
ChevronRight,
|
||||
Loader2,
|
||||
Clock,
|
||||
|
|
@ -25,7 +24,6 @@ const GROUP_NAMES: Record<string, string> = {
|
|||
fs: 'File System',
|
||||
runtime: 'Runtime',
|
||||
web: 'Web',
|
||||
memory: 'Memory',
|
||||
subagent: 'Subagent',
|
||||
cron: 'Cron',
|
||||
other: 'Other',
|
||||
|
|
@ -36,7 +34,6 @@ const GROUP_DESCRIPTIONS: Record<string, string> = {
|
|||
fs: 'Read, write, and manage files',
|
||||
runtime: 'Execute code and commands',
|
||||
web: 'Fetch and interact with web content',
|
||||
memory: 'Store and recall information',
|
||||
subagent: 'Delegate tasks to sub-agents',
|
||||
cron: 'Schedule recurring tasks',
|
||||
other: 'Miscellaneous tools',
|
||||
|
|
@ -47,7 +44,6 @@ const GROUP_ICONS: Record<string, typeof FolderOpen> = {
|
|||
fs: FolderOpen,
|
||||
runtime: Code,
|
||||
web: Globe,
|
||||
memory: Brain,
|
||||
subagent: Users,
|
||||
cron: Clock,
|
||||
other: Code,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@ const TOOL_DESCRIPTIONS: Record<string, string> = {
|
|||
process: 'Manage background processes',
|
||||
web_fetch: 'Fetch content from URLs',
|
||||
web_search: 'Search the web via Devv Search',
|
||||
memory_get: 'Get stored memory value',
|
||||
memory_set: 'Store a memory value',
|
||||
memory_delete: 'Delete a memory value',
|
||||
memory_list: 'List all memory keys',
|
||||
memory_search: 'Search memory files for keywords',
|
||||
cron: 'Create and manage scheduled tasks',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ multica skills remove <name>
|
|||
|
||||
- base coding tools (`read/write/edit/...`)
|
||||
- extended tools (`exec`, `process`, `glob`, `web_fetch`, `web_search`, `data`, `cron`, `delegate`)
|
||||
- conditional tools (`memory_search`, `send_file`)
|
||||
- conditional tools (`send_file`)
|
||||
|
||||
Tool errors are wrapped into structured tool results instead of crashing runs.
|
||||
|
||||
|
|
@ -52,7 +52,6 @@ Supported group aliases:
|
|||
- `group:fs` -> `read, write, edit, glob`
|
||||
- `group:runtime` -> `exec, process`
|
||||
- `group:web` -> `web_search, web_fetch`
|
||||
- `group:memory` -> `memory_search`
|
||||
- `group:subagent` -> `delegate`
|
||||
- `group:cron` -> `cron`
|
||||
- `group:data` -> `data`
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ export function createAgentProfile(
|
|||
profile.soul = DEFAULT_TEMPLATES.soul;
|
||||
profile.user = DEFAULT_TEMPLATES.user;
|
||||
profile.workspace = DEFAULT_TEMPLATES.workspace;
|
||||
profile.memory = DEFAULT_TEMPLATES.memory;
|
||||
profile.heartbeat = DEFAULT_TEMPLATES.heartbeat;
|
||||
profile.config = { name: "Multica" };
|
||||
|
||||
|
|
@ -151,7 +150,6 @@ export class ProfileManager {
|
|||
soul: profile.soul,
|
||||
user: profile.user,
|
||||
workspace: profile.workspace,
|
||||
memory: profile.memory,
|
||||
heartbeat: profile.heartbeat,
|
||||
config: profile.config,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ describe("storage", () => {
|
|||
writeFileSync(join(dir, "soul.md"), "Soul content");
|
||||
writeFileSync(join(dir, "user.md"), "User content");
|
||||
writeFileSync(join(dir, "workspace.md"), "Workspace content");
|
||||
writeFileSync(join(dir, "memory.md"), "Memory content");
|
||||
|
||||
const profile = loadProfile(profileId, { baseDir: testBaseDir });
|
||||
|
||||
|
|
@ -161,7 +160,6 @@ describe("storage", () => {
|
|||
expect(profile.soul).toBe("Soul content");
|
||||
expect(profile.user).toBe("User content");
|
||||
expect(profile.workspace).toBe("Workspace content");
|
||||
expect(profile.memory).toBe("Memory content");
|
||||
});
|
||||
|
||||
it("should return undefined for missing files", () => {
|
||||
|
|
@ -177,7 +175,6 @@ describe("storage", () => {
|
|||
expect(profile.soul).toBe("Soul only");
|
||||
expect(profile.user).toBeUndefined();
|
||||
expect(profile.workspace).toBeUndefined();
|
||||
expect(profile.memory).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle non-existent profile", () => {
|
||||
|
|
@ -195,7 +192,6 @@ describe("storage", () => {
|
|||
soul: "Soul data",
|
||||
user: "User data",
|
||||
workspace: "Workspace data",
|
||||
memory: "Memory data",
|
||||
};
|
||||
|
||||
saveProfile(profile, { baseDir: testBaseDir });
|
||||
|
|
@ -204,7 +200,6 @@ describe("storage", () => {
|
|||
expect(readFileSync(join(dir, "soul.md"), "utf-8")).toBe("Soul data");
|
||||
expect(readFileSync(join(dir, "user.md"), "utf-8")).toBe("User data");
|
||||
expect(readFileSync(join(dir, "workspace.md"), "utf-8")).toBe("Workspace data");
|
||||
expect(readFileSync(join(dir, "memory.md"), "utf-8")).toBe("Memory data");
|
||||
});
|
||||
|
||||
it("should only save defined fields", () => {
|
||||
|
|
@ -213,7 +208,6 @@ describe("storage", () => {
|
|||
soul: "Soul only",
|
||||
user: undefined,
|
||||
workspace: undefined,
|
||||
memory: undefined,
|
||||
};
|
||||
|
||||
saveProfile(profile, { baseDir: testBaseDir });
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ export function loadProfile(profileId: string, options?: StorageOptions): AgentP
|
|||
soul: readProfileFile(profileId, PROFILE_FILES.soul, options),
|
||||
user: readProfileFile(profileId, PROFILE_FILES.user, options),
|
||||
workspace: readProfileFile(profileId, PROFILE_FILES.workspace, options),
|
||||
memory: readProfileFile(profileId, PROFILE_FILES.memory, options),
|
||||
heartbeat: readProfileFile(profileId, PROFILE_FILES.heartbeat, options),
|
||||
config: readProfileConfig(profileId, options),
|
||||
};
|
||||
|
|
@ -102,7 +101,7 @@ export function loadProfile(profileId: string, options?: StorageOptions): AgentP
|
|||
|
||||
/** 保存 AgentProfile(只写入非空字段) */
|
||||
export function saveProfile(profile: AgentProfile, options?: StorageOptions): void {
|
||||
const { id, soul, user, workspace, memory, heartbeat, config } = profile;
|
||||
const { id, soul, user, workspace, heartbeat, config } = profile;
|
||||
|
||||
if (soul !== undefined) {
|
||||
writeProfileFile(id, PROFILE_FILES.soul, soul, options);
|
||||
|
|
@ -113,9 +112,6 @@ export function saveProfile(profile: AgentProfile, options?: StorageOptions): vo
|
|||
if (workspace !== undefined) {
|
||||
writeProfileFile(id, PROFILE_FILES.workspace, workspace, options);
|
||||
}
|
||||
if (memory !== undefined) {
|
||||
writeProfileFile(id, PROFILE_FILES.memory, memory, options);
|
||||
}
|
||||
if (heartbeat !== undefined) {
|
||||
writeProfileFile(id, PROFILE_FILES.heartbeat, heartbeat, options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ _You're not a chatbot. You're becoming someone._
|
|||
|
||||
## Continuity
|
||||
|
||||
Each session, you wake up fresh. These files are your memory. Read them. Update them. They're how you persist.
|
||||
Each session, you wake up fresh. These files are your continuity. Read them. Update them. They're how you persist.
|
||||
|
||||
If you change this file, tell the user — it's your soul, and they should know.
|
||||
|
||||
|
|
@ -71,7 +71,6 @@ Your profile directory contains these files (use \`edit\` or \`write\` to update
|
|||
| \`soul.md\` | Who you are, your identity and values | Rarely — tell user if you do |
|
||||
| \`user.md\` | About your human | As you learn about them |
|
||||
| \`workspace.md\` | This file — your rules | When you discover better conventions |
|
||||
| \`memory.md\` | Long-term knowledge | Regularly — capture what matters |
|
||||
| \`heartbeat.md\` | Background check instructions | When heartbeat behavior should change |
|
||||
|
||||
## Every Session
|
||||
|
|
@ -80,41 +79,10 @@ Before doing anything else:
|
|||
|
||||
1. Read \`soul.md\` — this is who you are
|
||||
2. Read \`user.md\` — this is who you're helping
|
||||
3. Check \`memory.md\` for context
|
||||
3. Read \`workspace.md\` — these are your operating conventions
|
||||
|
||||
Don't ask permission. Just do it.
|
||||
|
||||
## Memory
|
||||
|
||||
You wake up fresh each session. These files are your continuity:
|
||||
|
||||
- **Long-term:** \`MEMORY.md\` — your curated memories, lessons learned
|
||||
- **Daily notes:** \`memory/YYYY-MM-DD.md\` — raw logs of what happened (optional)
|
||||
- **Heartbeat:** \`heartbeat.md\` — periodic check loop instructions
|
||||
|
||||
Capture what matters. Decisions, context, things to remember.
|
||||
|
||||
### 📝 Write It Down - No "Mental Notes"!
|
||||
|
||||
⚠️ **CRITICAL**: You CANNOT "remember" things mentally. Your memory resets each session. If you don't write it to a file, it's gone.
|
||||
|
||||
**Which file to edit:**
|
||||
- \`user.md\` — About your human: name, preferences, habits, context, anything personal
|
||||
- \`memory.md\` — Your learnings: decisions made, lessons learned, important context
|
||||
- \`workspace.md\` — Your rules: conventions, workflows, how you should operate
|
||||
- \`soul.md\` — Your identity: only change if user wants to reshape who you are
|
||||
- \`heartbeat.md\` — Periodic background checks and alert rules
|
||||
|
||||
**Rules:**
|
||||
- **DO NOT** say "I'll remember that" without ACTUALLY calling \`edit\` or \`write\` on a file
|
||||
- **DO NOT** make "mental notes" — they don't exist
|
||||
- When you learn something about the user (name, preference, habit) → IMMEDIATELY update \`user.md\`
|
||||
- When you learn a lesson, make a decision, or gain context → IMMEDIATELY update \`memory.md\`
|
||||
- When you discover a better workflow or convention → update \`workspace.md\`
|
||||
- When you make a mistake → document it so future-you doesn't repeat it
|
||||
|
||||
**Text > Brain** 📝
|
||||
|
||||
## Safety
|
||||
|
||||
- Don't exfiltrate private data. Ever.
|
||||
|
|
@ -140,17 +108,6 @@ Capture what matters. Decisions, context, things to remember.
|
|||
## Make It Yours
|
||||
|
||||
This is a starting point. Add your own conventions, style, and rules as you figure out what works.
|
||||
`,
|
||||
|
||||
memory: `# Memory
|
||||
|
||||
_(Persistent knowledge will be stored here. Update this as you learn.)_
|
||||
|
||||
## Key Decisions
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
## Important Context
|
||||
`,
|
||||
|
||||
heartbeat: `# heartbeat.md
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export const PROFILE_FILES = {
|
|||
soul: "soul.md",
|
||||
user: "user.md",
|
||||
workspace: "workspace.md",
|
||||
memory: "memory.md",
|
||||
heartbeat: "heartbeat.md",
|
||||
config: "config.json",
|
||||
} as const;
|
||||
|
|
@ -56,8 +55,6 @@ export interface AgentProfile {
|
|||
user?: string | undefined;
|
||||
/** Workspace guidelines - behavior rules and conventions */
|
||||
workspace?: string | undefined;
|
||||
/** Persistent memory - long-term knowledge base */
|
||||
memory?: string | undefined;
|
||||
/** Periodic heartbeat instructions */
|
||||
heartbeat?: string | undefined;
|
||||
/** Profile configuration (from config.json) */
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@ export class Agent {
|
|||
});
|
||||
|
||||
// Load Agent Profile (if profileId is specified)
|
||||
// Every Agent should have a Profile for memory, tools config, and other settings
|
||||
// Every Agent should have a Profile for tools config and other settings
|
||||
if (options.profileId) {
|
||||
this.profile = new ProfileManager({
|
||||
profileId: options.profileId,
|
||||
|
|
@ -644,7 +644,6 @@ export class Agent {
|
|||
// Merge Profile tools config with options.tools (options takes precedence)
|
||||
const profileToolsConfig = this.profile?.getToolsConfig();
|
||||
const mergedToolsConfig = mergeToolsConfig(profileToolsConfig, options.tools);
|
||||
const profileDir = this.profile?.getProfileDir();
|
||||
// Use this.sessionId (which may be auto-generated) instead of options.sessionId
|
||||
// (which may be undefined). Without this, delegate tool has no session context.
|
||||
this.toolsOptions = mergedToolsConfig
|
||||
|
|
@ -653,7 +652,6 @@ export class Agent {
|
|||
sessionId: this.sessionId,
|
||||
cwd: effectiveCwd,
|
||||
tools: mergedToolsConfig,
|
||||
profileDir,
|
||||
provider: this.resolvedProvider,
|
||||
runLog: this.runLog,
|
||||
onExecApprovalNeeded: this.guardedExecApproval,
|
||||
|
|
@ -662,7 +660,6 @@ export class Agent {
|
|||
...options,
|
||||
sessionId: this.sessionId,
|
||||
cwd: effectiveCwd,
|
||||
profileDir,
|
||||
provider: this.resolvedProvider,
|
||||
runLog: this.runLog,
|
||||
onExecApprovalNeeded: this.guardedExecApproval,
|
||||
|
|
@ -1883,7 +1880,6 @@ export class Agent {
|
|||
soul: profile.soul,
|
||||
user: profile.user,
|
||||
workspace: profile.workspace,
|
||||
memory: profile.memory,
|
||||
heartbeat: profile.heartbeat,
|
||||
config: profile.config,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ const PROFILE = {
|
|||
soul: "# Soul\nYou are a helpful coding assistant.",
|
||||
user: "# User\nName: Alice",
|
||||
workspace: "# Workspace\nFollow conventional commits.",
|
||||
memory: "# Memory\nUser prefers TypeScript.",
|
||||
config: { name: "TestAgent" },
|
||||
};
|
||||
|
||||
|
|
@ -17,12 +16,11 @@ describe("buildSystemPrompt", () => {
|
|||
// ── Full mode ─────────────────────────────────────────────────────────
|
||||
|
||||
it("full mode includes workspace section only (progressive disclosure)", () => {
|
||||
// Soul, user, memory are read on-demand by the agent
|
||||
// Soul and user are read on-demand by the agent
|
||||
const result = buildSystemPrompt({ mode: "full", profile: PROFILE });
|
||||
expect(result).not.toContain("# Soul");
|
||||
expect(result).not.toContain("# User");
|
||||
expect(result).toContain("# Workspace");
|
||||
expect(result).not.toContain("# Memory");
|
||||
});
|
||||
|
||||
it("full mode includes safety constitution", () => {
|
||||
|
|
@ -82,7 +80,6 @@ describe("buildSystemPrompt", () => {
|
|||
expect(result).toContain("/home/user/.super-multica/agent-profiles/test");
|
||||
expect(result).toContain("soul.md");
|
||||
expect(result).toContain("user.md");
|
||||
expect(result).toContain("memory.md");
|
||||
});
|
||||
|
||||
it("full mode excludes subagent section", () => {
|
||||
|
|
@ -100,7 +97,6 @@ describe("buildSystemPrompt", () => {
|
|||
expect(result).not.toContain("# Soul");
|
||||
expect(result).not.toContain("# User");
|
||||
expect(result).not.toContain("# Workspace");
|
||||
expect(result).not.toContain("# Memory");
|
||||
});
|
||||
|
||||
it("minimal mode includes safety constitution", () => {
|
||||
|
|
@ -246,7 +242,7 @@ describe("buildSystemPromptWithReport", () => {
|
|||
const identity = report.sections.find((s) => s.name === "identity");
|
||||
expect(identity?.included).toBe(true);
|
||||
|
||||
// User and memory are excluded (progressive disclosure)
|
||||
// User is excluded (progressive disclosure)
|
||||
const user = report.sections.find((s) => s.name === "user");
|
||||
expect(user?.included).toBe(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
buildConditionalToolSections,
|
||||
buildExtraPromptSection,
|
||||
buildIdentitySection,
|
||||
buildMemoryFileSection,
|
||||
buildProfileDirSection,
|
||||
buildRuntimeSection,
|
||||
buildSafetySection,
|
||||
|
|
@ -74,7 +73,6 @@ export function buildSystemPromptWithReport(options: SystemPromptOptions): {
|
|||
{ name: "workspace", lines: buildWorkspaceSection(profile, mode, profileDir, workspaceDir),
|
||||
...(workspaceTruncated ? { truncated: true, originalChars: workspaceOriginalChars } : {}),
|
||||
},
|
||||
{ name: "memory", lines: buildMemoryFileSection(profile, mode) },
|
||||
{ name: "heartbeat", lines: buildHeartbeatSection(profile, mode) },
|
||||
{ name: "safety", lines: buildSafetySection(includeSafety) },
|
||||
{ name: "tooling", lines: buildToolingSummary(tools, mode) },
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";
|
|||
import {
|
||||
buildConditionalToolSections,
|
||||
buildIdentitySection,
|
||||
buildMemoryFileSection,
|
||||
buildProfileDirSection,
|
||||
buildRuntimeSection,
|
||||
buildSafetySection,
|
||||
|
|
@ -63,7 +62,6 @@ describe("buildWorkspaceSection", () => {
|
|||
expect(text).toContain("/path/to/profile");
|
||||
expect(text).toContain("soul.md");
|
||||
expect(text).toContain("user.md");
|
||||
expect(text).toContain("memory.md");
|
||||
expect(text).toContain("Rules here");
|
||||
});
|
||||
|
||||
|
|
@ -78,14 +76,6 @@ describe("buildWorkspaceSection", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("buildMemoryFileSection", () => {
|
||||
it("returns empty in all modes (progressive disclosure)", () => {
|
||||
// Memory content is no longer injected - agent reads memory.md on demand
|
||||
expect(buildMemoryFileSection({ memory: "Key facts" }, "full")).toEqual([]);
|
||||
expect(buildMemoryFileSection({ memory: "data" }, "minimal")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildSafetySection", () => {
|
||||
it("returns safety text when enabled", () => {
|
||||
const result = buildSafetySection(true);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ const CORE_TOOL_SUMMARIES: Record<string, string> = {
|
|||
process: "Manage background exec sessions",
|
||||
web_search: "Search the web via Devv Search",
|
||||
web_fetch: "Fetch and extract readable content from a URL",
|
||||
memory_search: "Search memory files by keyword",
|
||||
delegate: "Run tasks in parallel via sub-agents",
|
||||
data: "Query structured financial and market data",
|
||||
};
|
||||
|
|
@ -42,7 +41,6 @@ const TOOL_ORDER = [
|
|||
"process",
|
||||
"web_search",
|
||||
"web_fetch",
|
||||
"memory_search",
|
||||
"delegate",
|
||||
"data",
|
||||
];
|
||||
|
|
@ -126,7 +124,7 @@ export function buildUserSection(
|
|||
/**
|
||||
* Workspace section — workspace.md content with profile directory path.
|
||||
* This is the primary profile content injected into system prompt.
|
||||
* Other profile files (soul.md, user.md, memory.md) are read on demand.
|
||||
* Other profile files (soul.md, user.md) are read on demand.
|
||||
*/
|
||||
export function buildWorkspaceSection(
|
||||
profile: ProfileContent | undefined,
|
||||
|
|
@ -155,13 +153,12 @@ export function buildWorkspaceSection(
|
|||
"## Profile",
|
||||
"",
|
||||
`Your profile directory: \`${profileDir}\``,
|
||||
"Use this as the base path for profile files (soul.md, user.md, memory.md, heartbeat.md, memory/*.md).",
|
||||
"Use this as the base path for profile files (soul.md, user.md, workspace.md, heartbeat.md).",
|
||||
"",
|
||||
"Profile files:",
|
||||
"- `soul.md` — Your identity and values",
|
||||
"- `user.md` — Information about your user",
|
||||
"- `workspace.md` — Guidelines and conventions (below)",
|
||||
"- `memory.md` — Persistent knowledge",
|
||||
"- `heartbeat.md` — Background heartbeat loop instructions",
|
||||
"",
|
||||
);
|
||||
|
|
@ -176,18 +173,6 @@ export function buildWorkspaceSection(
|
|||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory section — no longer injected into system prompt.
|
||||
* Agent reads memory.md on demand from profile directory.
|
||||
*/
|
||||
export function buildMemoryFileSection(
|
||||
_profile: ProfileContent | undefined,
|
||||
_mode: SystemPromptMode,
|
||||
): string[] {
|
||||
// Progressive disclosure: agent reads memory.md on demand
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Heartbeat section — full mode only.
|
||||
* Keeps heartbeat protocol explicit in the agent instructions.
|
||||
|
|
@ -298,21 +283,6 @@ export function buildConditionalToolSections(
|
|||
const toolSet = new Set(tools.map((t) => t.toLowerCase()));
|
||||
const lines: string[] = [];
|
||||
|
||||
// Memory tools
|
||||
if (toolSet.has("memory_search")) {
|
||||
lines.push(
|
||||
"## Memory Recall",
|
||||
"Before answering anything about prior work, decisions, dates, people, preferences, or todos:",
|
||||
"1. Run `memory_search` to find relevant entries in memory files",
|
||||
"2. Use `read` to pull needed context",
|
||||
"",
|
||||
"To update memory, use `edit` on the appropriate file:",
|
||||
"- `memory.md` — Long-term knowledge (decisions, preferences, important context)",
|
||||
"- `memory/YYYY-MM-DD.md` — Daily logs and session notes",
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
// Delegate tool (full mode only — sub-agents cannot delegate)
|
||||
if (mode === "full" && toolSet.has("delegate")) {
|
||||
lines.push(
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ export interface ProfileContent {
|
|||
soul?: string | undefined;
|
||||
user?: string | undefined;
|
||||
workspace?: string | undefined;
|
||||
memory?: string | undefined;
|
||||
heartbeat?: string | undefined;
|
||||
config?: ProfileConfig | undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { createProcessTool } from "./tools/process.js";
|
|||
import { createGlobTool } from "./tools/glob.js";
|
||||
import { createWebFetchTool, createWebSearchTool } from "./tools/web/index.js";
|
||||
import { createDelegateTool } from "./tools/delegate.js";
|
||||
import { createMemorySearchTool } from "./tools/memory-search.js";
|
||||
import { createCronTool } from "./tools/cron/index.js";
|
||||
import { createDataTool } from "./tools/data/index.js";
|
||||
import { createSendFileTool } from "./tools/send-file.js";
|
||||
|
|
@ -23,8 +22,6 @@ export { resolveModel } from "./providers/index.js";
|
|||
/** Options for creating tools */
|
||||
export interface CreateToolsOptions {
|
||||
cwd: string;
|
||||
/** Profile directory for memory_search tool (optional) */
|
||||
profileDir?: string | undefined;
|
||||
/** Whether this agent is a subagent (passed to delegate tool) */
|
||||
isSubagent?: boolean | undefined;
|
||||
/** Session ID of the agent (passed to delegate tool) */
|
||||
|
|
@ -112,7 +109,7 @@ function wrapTool<TParams extends TSchema, TResult>(
|
|||
export function createAllTools(options: CreateToolsOptions | string): AgentTool<any>[] {
|
||||
// Support legacy string argument for backwards compatibility
|
||||
const opts: CreateToolsOptions = typeof options === "string" ? { cwd: options } : options;
|
||||
const { cwd, profileDir, isSubagent, sessionId } = opts;
|
||||
const { cwd, isSubagent, sessionId } = opts;
|
||||
|
||||
const baseTools = createCodingTools(cwd)
|
||||
.filter((tool) => tool.name !== "bash")
|
||||
|
|
@ -138,12 +135,6 @@ export function createAllTools(options: CreateToolsOptions | string): AgentTool<
|
|||
dataTool as AgentTool<any>,
|
||||
];
|
||||
|
||||
// Add memory_search tool if profileDir is provided
|
||||
if (profileDir) {
|
||||
const memorySearchTool = createMemorySearchTool(profileDir);
|
||||
tools.push(memorySearchTool as AgentTool<any>);
|
||||
}
|
||||
|
||||
// Add send_file tool if channel send callback is provided
|
||||
if (opts.onChannelSendFile) {
|
||||
const sendFileTool = createSendFileTool(opts.onChannelSendFile);
|
||||
|
|
@ -166,10 +157,8 @@ export function createAllTools(options: CreateToolsOptions | string): AgentTool<
|
|||
return tools;
|
||||
}
|
||||
|
||||
/** Extended options for resolveTools that includes profileDir */
|
||||
/** Extended options for resolveTools */
|
||||
export interface ResolveToolsOptions extends AgentOptions {
|
||||
/** Profile directory for memory_search tool (computed from profileId if not provided) */
|
||||
profileDir?: string | undefined;
|
||||
/** Run-log instance (forwarded to delegate tool) */
|
||||
runLog?: import("./run-log.js").RunLog | undefined;
|
||||
}
|
||||
|
|
@ -189,7 +178,6 @@ export function resolveTools(options: ResolveToolsOptions): AgentTool<any>[] {
|
|||
// Create all tools
|
||||
const allTools = createAllTools({
|
||||
cwd,
|
||||
profileDir: options.profileDir,
|
||||
isSubagent: options.isSubagent,
|
||||
sessionId: options.sessionId,
|
||||
provider: options.provider,
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ export const TOOL_GROUPS: Record<string, string[]> = {
|
|||
// Web tools
|
||||
"group:web": ["web_search", "web_fetch"],
|
||||
|
||||
// Memory tools (requires profile)
|
||||
"group:memory": ["memory_search"],
|
||||
|
||||
// Subagent tools
|
||||
"group:subagent": ["delegate"],
|
||||
|
||||
|
|
|
|||
|
|
@ -1,154 +0,0 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mkdirSync, writeFileSync, rmSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { createMemorySearchTool } from "./memory-search.js";
|
||||
|
||||
describe("memory_search tool", () => {
|
||||
let testDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
testDir = join(tmpdir(), `memory-search-test-${Date.now()}`);
|
||||
mkdirSync(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("creates tool with correct name and description", () => {
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
expect(tool.name).toBe("memory_search");
|
||||
expect(tool.label).toBe("Memory Search");
|
||||
expect(tool.description).toContain("memory files");
|
||||
});
|
||||
|
||||
it("returns no matches when no memory files exist", async () => {
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "test" }, undefined);
|
||||
expect(result.details?.matches).toHaveLength(0);
|
||||
expect(result.details?.filesSearched).toBe(0);
|
||||
});
|
||||
|
||||
it("searches memory.md file", async () => {
|
||||
// Create memory.md with test content
|
||||
writeFileSync(
|
||||
join(testDir, "memory.md"),
|
||||
"# Memory\n\nUser prefers TypeScript over JavaScript.\n\nDecision: Use ESLint for linting.\n",
|
||||
);
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "TypeScript" }, undefined);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(1);
|
||||
expect(result.details?.matches[0]?.file).toBe("memory.md");
|
||||
expect(result.details?.matches[0]?.content).toContain("TypeScript");
|
||||
});
|
||||
|
||||
it("searches memory/*.md files", async () => {
|
||||
// Create memory directory with daily logs
|
||||
const memoryDir = join(testDir, "memory");
|
||||
mkdirSync(memoryDir);
|
||||
writeFileSync(
|
||||
join(memoryDir, "2024-01-15.md"),
|
||||
"# 2024-01-15\n\nDiscussed API design with team.\n",
|
||||
);
|
||||
writeFileSync(
|
||||
join(memoryDir, "2024-01-16.md"),
|
||||
"# 2024-01-16\n\nImplemented user authentication.\n",
|
||||
);
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "API" }, undefined);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(1);
|
||||
expect(result.details?.matches[0]?.file).toBe("memory/2024-01-15.md");
|
||||
});
|
||||
|
||||
it("searches both memory.md and memory/*.md", async () => {
|
||||
// Create memory.md
|
||||
writeFileSync(join(testDir, "memory.md"), "Important: Always test code.\n");
|
||||
|
||||
// Create memory directory
|
||||
const memoryDir = join(testDir, "memory");
|
||||
mkdirSync(memoryDir);
|
||||
writeFileSync(join(memoryDir, "2024-01-15.md"), "Remember to test before deploy.\n");
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "test" }, undefined);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(2);
|
||||
expect(result.details?.filesSearched).toBe(2);
|
||||
});
|
||||
|
||||
it("is case-insensitive by default", async () => {
|
||||
writeFileSync(join(testDir, "memory.md"), "User prefers TYPESCRIPT.\n");
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "typescript" }, undefined);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("supports case-sensitive search", async () => {
|
||||
writeFileSync(join(testDir, "memory.md"), "User prefers TYPESCRIPT.\n");
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
|
||||
// Case-sensitive search should not match
|
||||
const result1 = await tool.execute(
|
||||
"test-call",
|
||||
{ query: "typescript", caseSensitive: true },
|
||||
undefined,
|
||||
);
|
||||
expect(result1.details?.matches).toHaveLength(0);
|
||||
|
||||
// Case-sensitive search should match
|
||||
const result2 = await tool.execute(
|
||||
"test-call",
|
||||
{ query: "TYPESCRIPT", caseSensitive: true },
|
||||
undefined,
|
||||
);
|
||||
expect(result2.details?.matches).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("includes context lines in results", async () => {
|
||||
writeFileSync(
|
||||
join(testDir, "memory.md"),
|
||||
"Line 1\nLine 2\nMatch here\nLine 4\nLine 5\n",
|
||||
);
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute("test-call", { query: "Match" }, undefined);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(1);
|
||||
expect(result.details?.matches[0]?.context.before).toContain("Line 2");
|
||||
expect(result.details?.matches[0]?.context.after).toContain("Line 4");
|
||||
});
|
||||
|
||||
it("respects maxResults limit", async () => {
|
||||
// Create file with multiple matches
|
||||
writeFileSync(
|
||||
join(testDir, "memory.md"),
|
||||
"test line 1\ntest line 2\ntest line 3\ntest line 4\ntest line 5\n",
|
||||
);
|
||||
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
const result = await tool.execute(
|
||||
"test-call",
|
||||
{ query: "test", maxResults: 2 },
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(result.details?.matches).toHaveLength(2);
|
||||
expect(result.details?.totalMatches).toBe(5);
|
||||
expect(result.details?.truncated).toBe(true);
|
||||
});
|
||||
|
||||
it("throws error for empty query", async () => {
|
||||
const tool = createMemorySearchTool(testDir);
|
||||
await expect(tool.execute("test-call", { query: "" }, undefined)).rejects.toThrow(
|
||||
"Query must not be empty",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,276 +0,0 @@
|
|||
import { Type } from "@sinclair/typebox";
|
||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
import fg from "fast-glob";
|
||||
|
||||
const MemorySearchSchema = Type.Object({
|
||||
query: Type.String({
|
||||
description: "Search query - keywords or phrases to find in memory files.",
|
||||
}),
|
||||
maxResults: Type.Optional(
|
||||
Type.Number({
|
||||
description: "Maximum number of results to return. Defaults to 10.",
|
||||
minimum: 1,
|
||||
maximum: 50,
|
||||
}),
|
||||
),
|
||||
caseSensitive: Type.Optional(
|
||||
Type.Boolean({
|
||||
description: "Whether the search is case-sensitive. Defaults to false.",
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
type MemorySearchArgs = {
|
||||
query: string;
|
||||
maxResults?: number;
|
||||
caseSensitive?: boolean;
|
||||
};
|
||||
|
||||
export type MemorySearchMatch = {
|
||||
file: string;
|
||||
line: number;
|
||||
content: string;
|
||||
context: {
|
||||
before: string[];
|
||||
after: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type MemorySearchResult = {
|
||||
matches: MemorySearchMatch[];
|
||||
totalMatches: number;
|
||||
filesSearched: number;
|
||||
truncated: boolean;
|
||||
};
|
||||
|
||||
const DEFAULT_MAX_RESULTS = 10;
|
||||
const CONTEXT_LINES = 2;
|
||||
|
||||
/**
|
||||
* Create a memory_search tool for searching memory files.
|
||||
*
|
||||
* @param profileDir - Profile directory containing memory.md and memory/ folder
|
||||
*/
|
||||
export function createMemorySearchTool(
|
||||
profileDir: string,
|
||||
): AgentTool<typeof MemorySearchSchema, MemorySearchResult> {
|
||||
return {
|
||||
name: "memory_search",
|
||||
label: "Memory Search",
|
||||
description:
|
||||
"Search through memory files (memory.md and memory/*.md) for keywords or phrases. " +
|
||||
"Use this before answering questions about prior work, decisions, dates, people, preferences, or todos. " +
|
||||
"Returns matching lines with context.",
|
||||
parameters: MemorySearchSchema,
|
||||
execute: async (_toolCallId, args, _signal) => {
|
||||
const { query, maxResults, caseSensitive } = args as MemorySearchArgs;
|
||||
|
||||
if (!query || query.trim() === "") {
|
||||
throw new Error("Query must not be empty");
|
||||
}
|
||||
|
||||
const limit = Math.min(maxResults || DEFAULT_MAX_RESULTS, 50);
|
||||
const searchQuery = caseSensitive ? query : query.toLowerCase();
|
||||
|
||||
// Find all memory files
|
||||
const memoryFiles = await findMemoryFiles(profileDir);
|
||||
|
||||
if (memoryFiles.length === 0) {
|
||||
return {
|
||||
content: [{ type: "text", text: "No memory files found." }],
|
||||
details: {
|
||||
matches: [],
|
||||
totalMatches: 0,
|
||||
filesSearched: 0,
|
||||
truncated: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Search each file
|
||||
const allMatches: MemorySearchMatch[] = [];
|
||||
|
||||
for (const file of memoryFiles) {
|
||||
const matches = await searchFile(file, searchQuery, caseSensitive ?? false, profileDir);
|
||||
allMatches.push(...matches);
|
||||
}
|
||||
|
||||
// Sort by relevance (files with more matches first, then by line number)
|
||||
allMatches.sort((a, b) => {
|
||||
if (a.file !== b.file) {
|
||||
// Count matches per file
|
||||
const aCount = allMatches.filter((m) => m.file === a.file).length;
|
||||
const bCount = allMatches.filter((m) => m.file === b.file).length;
|
||||
return bCount - aCount;
|
||||
}
|
||||
return a.line - b.line;
|
||||
});
|
||||
|
||||
const totalMatches = allMatches.length;
|
||||
const truncated = allMatches.length > limit;
|
||||
const limitedMatches = allMatches.slice(0, limit);
|
||||
|
||||
// Format output
|
||||
const output = formatSearchResults(limitedMatches, totalMatches, truncated, memoryFiles.length);
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: output }],
|
||||
details: {
|
||||
matches: limitedMatches,
|
||||
totalMatches,
|
||||
filesSearched: memoryFiles.length,
|
||||
truncated,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all memory files in the profile directory.
|
||||
*/
|
||||
async function findMemoryFiles(profileDir: string): Promise<string[]> {
|
||||
const files: string[] = [];
|
||||
|
||||
// Check for memory.md in profile root
|
||||
const memoryMd = path.join(profileDir, "memory.md");
|
||||
try {
|
||||
await fs.access(memoryMd);
|
||||
files.push(memoryMd);
|
||||
} catch {
|
||||
// File doesn't exist
|
||||
}
|
||||
|
||||
// Check for memory/*.md files
|
||||
const memoryDir = path.join(profileDir, "memory");
|
||||
try {
|
||||
await fs.access(memoryDir);
|
||||
const mdFiles = await fg("*.md", {
|
||||
cwd: memoryDir,
|
||||
onlyFiles: true,
|
||||
absolute: true,
|
||||
});
|
||||
files.push(...mdFiles);
|
||||
} catch {
|
||||
// Directory doesn't exist
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a single file for the query.
|
||||
*/
|
||||
async function searchFile(
|
||||
filePath: string,
|
||||
query: string,
|
||||
caseSensitive: boolean,
|
||||
profileDir: string,
|
||||
): Promise<MemorySearchMatch[]> {
|
||||
const matches: MemorySearchMatch[] = [];
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(filePath, "utf-8");
|
||||
const lines = content.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i]!;
|
||||
const searchLine = caseSensitive ? line : line.toLowerCase();
|
||||
|
||||
if (searchLine.includes(query)) {
|
||||
// Get context lines
|
||||
const beforeLines: string[] = [];
|
||||
const afterLines: string[] = [];
|
||||
|
||||
for (let j = Math.max(0, i - CONTEXT_LINES); j < i; j++) {
|
||||
beforeLines.push(lines[j]!);
|
||||
}
|
||||
|
||||
for (let j = i + 1; j <= Math.min(lines.length - 1, i + CONTEXT_LINES); j++) {
|
||||
afterLines.push(lines[j]!);
|
||||
}
|
||||
|
||||
// Get relative path for display
|
||||
const relativePath = path.relative(profileDir, filePath);
|
||||
|
||||
matches.push({
|
||||
file: relativePath,
|
||||
line: i + 1, // 1-indexed
|
||||
content: line,
|
||||
context: {
|
||||
before: beforeLines,
|
||||
after: afterLines,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Skip files that can't be read
|
||||
console.error(`Failed to read ${filePath}:`, err);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format search results for display.
|
||||
*/
|
||||
function formatSearchResults(
|
||||
matches: MemorySearchMatch[],
|
||||
totalMatches: number,
|
||||
truncated: boolean,
|
||||
filesSearched: number,
|
||||
): string {
|
||||
if (matches.length === 0) {
|
||||
return `No matches found in ${filesSearched} memory file(s).`;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(`Found ${totalMatches} match(es) in ${filesSearched} file(s):`);
|
||||
|
||||
if (truncated) {
|
||||
lines.push(`(Showing first ${matches.length} results)`);
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
// Group by file
|
||||
const byFile = new Map<string, MemorySearchMatch[]>();
|
||||
for (const match of matches) {
|
||||
const existing = byFile.get(match.file) || [];
|
||||
existing.push(match);
|
||||
byFile.set(match.file, existing);
|
||||
}
|
||||
|
||||
for (const [file, fileMatches] of byFile) {
|
||||
lines.push(`## ${file}`);
|
||||
lines.push("");
|
||||
|
||||
for (const match of fileMatches) {
|
||||
lines.push(`**Line ${match.line}:**`);
|
||||
|
||||
// Show context before
|
||||
if (match.context.before.length > 0) {
|
||||
for (const ctx of match.context.before) {
|
||||
lines.push(` ${ctx}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Show matching line (highlighted)
|
||||
lines.push(`> ${match.content}`);
|
||||
|
||||
// Show context after
|
||||
if (match.context.after.length > 0) {
|
||||
for (const ctx of match.context.after) {
|
||||
lines.push(` ${ctx}`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ export type AgentLogger = {
|
|||
};
|
||||
|
||||
export type AgentOptions = {
|
||||
/** Agent Profile ID - loads predefined identity, personality, memory and other configurations */
|
||||
/** Agent Profile ID - loads predefined identity, personality, and other configurations */
|
||||
profileId?: string | undefined;
|
||||
/** Profile base directory, defaults to ~/.super-multica/agent-profiles */
|
||||
profileBaseDir?: string | undefined;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
Search,
|
||||
FolderOpen,
|
||||
Globe,
|
||||
Database,
|
||||
GitBranch,
|
||||
BarChart3,
|
||||
ChevronRight,
|
||||
|
|
@ -35,10 +34,6 @@ const TOOL_DISPLAY: Record<string, { label: string; icon: LucideIcon }> = {
|
|||
glob: { label: "Glob", icon: Search },
|
||||
web_search: { label: "WebSearch", icon: Globe },
|
||||
web_fetch: { label: "WebFetch", icon: Globe },
|
||||
memory_get: { label: "MemoryGet", icon: Database },
|
||||
memory_set: { label: "MemorySet", icon: Database },
|
||||
memory_delete: { label: "MemoryDelete", icon: Database },
|
||||
memory_list: { label: "MemoryList", icon: Database },
|
||||
delegate: { label: "Delegate", icon: GitBranch },
|
||||
data: { label: "Data", icon: BarChart3 },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ async function runTask(
|
|||
enableSkills: false,
|
||||
tools: {
|
||||
// Only allow coding tools — no web, no cron, no sessions
|
||||
deny: ["web_fetch", "web_search", "cron", "data", "delegate", "memory_search", "send_file"],
|
||||
deny: ["web_fetch", "web_search", "cron", "data", "delegate", "send_file"],
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue