Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 43 additions & 19 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,53 @@ import { traceTools } from "./tracing.js";
import { restoreSession } from "./session.js";
import { checkPermission } from "./permissions.js";

const staticTools: AgentTool[] = [
// GPU
wakeGpu, shutdownGpu, gpuStatus,
// File system
/**
* Core tools — always registered. Small, general-purpose surface the model
* will want on virtually every task.
*/
const coreTools: AgentTool[] = [
readFileTool, writeFileTool, listFilesTool,
// Remote
sshToNas, delegateToNix,
// Browser
browserControl,
// LinkedIn
linkedinSearch, linkedinResults,
// iOS
iosListDevices, iosBuild, iosInstall, iosBuildAndDeploy,
// Launchpad
launchpadRunScraper, launchpadDeploy, launchpadScrape,
// browser-use agentic tasks
browserTask,
// Shell
runShell,
// Claude Code delegation
delegateToClaudeSubagent,
sshToNas,
delegateToNix,
wakeGpu, shutdownGpu, gpuStatus,
];

/**
* Specialized tool groups — opt in via env flags. Each group adds ~hundreds
* of tokens of schema to every single request; only register what's needed.
*
* MAX_TOOLS_BROWSER=true browser_control + browser_task
* MAX_TOOLS_LINKEDIN=true linkedin_search + linkedin_results
* MAX_TOOLS_IOS=true ios_list_devices + ios_build + ios_install + ios_build_and_deploy
* MAX_TOOLS_LAUNCHPAD=true launchpad_run_scraper + launchpad_deploy + launchpad_scrape
* MAX_TOOLS_SUBAGENT=true delegate_to_claude_subagent
* MAX_TOOLS_ALL=true everything (back-compat with pre-split behaviour)
*/
const toolGroups: Record<string, AgentTool[]> = {
BROWSER: [browserControl, browserTask],
LINKEDIN: [linkedinSearch, linkedinResults],
IOS: [iosListDevices, iosBuild, iosInstall, iosBuildAndDeploy],
LAUNCHPAD: [launchpadRunScraper, launchpadDeploy, launchpadScrape],
SUBAGENT: [delegateToClaudeSubagent],
};

function buildStaticTools(): AgentTool[] {
const all = process.env.MAX_TOOLS_ALL === "true";
const enabled: AgentTool[] = [...coreTools];
const activeGroups: string[] = ["core"];
for (const [group, tools] of Object.entries(toolGroups)) {
if (all || process.env[`MAX_TOOLS_${group}`] === "true") {
enabled.push(...tools);
activeGroups.push(group.toLowerCase());
}
}
log("info", `Tool groups active: ${activeGroups.join(", ")} (${enabled.length} tools)`);
return enabled;
}

const staticTools: AgentTool[] = buildStaticTools();

function inferProvider(modelId: string): "anthropic" | "google" {
return modelId.startsWith("claude") ? "anthropic" : "google";
}
Expand Down
10 changes: 9 additions & 1 deletion src/memory.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { readFile, writeFile, mkdir } from "fs/promises";
import { existsSync } from "fs";
import path from "path";
import { log } from "./logger.js";

const MAX_HOME = path.join(process.env.HOME!, "max");
const MEMORY_DIR = path.join(MAX_HOME, "memory");

const MAX_MEMORY_FILE_CHARS = Number(process.env.MAX_MEMORY_FILE_CHARS) || 20_000;

function formatDate(d: Date): string {
return d.toISOString().slice(0, 10);
}
Expand All @@ -17,7 +20,12 @@ function subDays(d: Date, n: number): Date {

async function readFileSafe(p: string): Promise<string> {
try {
return await readFile(p, "utf-8");
const content = await readFile(p, "utf-8");
if (content.length > MAX_MEMORY_FILE_CHARS) {
log("warn", `Memory file ${p} is ${content.length} chars, exceeds cap of ${MAX_MEMORY_FILE_CHARS}; truncating head`);
return content.slice(0, MAX_MEMORY_FILE_CHARS) + `\n\n[truncated ${content.length - MAX_MEMORY_FILE_CHARS} chars — cap is MAX_MEMORY_FILE_CHARS=${MAX_MEMORY_FILE_CHARS}]`;
}
return content;
} catch {
return "";
}
Expand Down
Loading