🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

sentoagent

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sentoagent - npm Package Compare versions

Comparing version
1.1.59
to
1.1.61
+1
-1
package.json
{
"name": "sentoagent",
"version": "1.1.59",
"version": "1.1.61",
"description": "Agents sent to fight your battles. Self-improving AI agents powered by Claude Code.",

@@ -5,0 +5,0 @@ "author": "Gabriel Gil",

@@ -151,5 +151,48 @@ import fs from "fs";

// ─── MCP bun-path self-heal ───
// Newer Claude Code sanitizes PATH when spawning MCP servers, so any plugin
// whose .mcp.json uses bare "command": "bun" fails silently with
// "No such file or directory" and the channel bot appears offline. Patch
// every installed plugin's .mcp.json to the absolute bun path (idempotent).
function patchMcpBunPaths() {
const home = os.homedir();
const root = path.join(home, ".claude/plugins/marketplaces");
const bunAbs = path.join(home, ".bun/bin/bun");
if (!fs.existsSync(root)) return 0;
if (!fs.existsSync(bunAbs)) { log.warn(`bun not found at ${bunAbs}, skipping .mcp.json patch`); return 0; }
let touched = 0;
for (const mp of fs.readdirSync(root)) {
const ext = path.join(root, mp, "external_plugins");
if (!fs.existsSync(ext)) continue;
for (const pl of fs.readdirSync(ext)) {
const mcpPath = path.join(ext, pl, ".mcp.json");
if (!fs.existsSync(mcpPath)) continue;
try {
const data = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
let changed = false;
for (const name of Object.keys(data.mcpServers || {})) {
const srv = data.mcpServers[name];
if (srv && srv.command === "bun") { srv.command = bunAbs; changed = true; }
}
if (changed) {
fs.writeFileSync(mcpPath, JSON.stringify(data, null, 2));
log.success(`MCP bun-path: patched ${mp}/${pl}`);
touched++;
}
} catch (e) { log.warn(`MCP bun-path: skip ${mcpPath} (${e.message})`); }
}
}
return touched;
}
// ─── Main export ───
export async function patchChannels(config) {
// Defensive: ensure every plugin's .mcp.json has an absolute bun path.
// Independent of channel type — fixes ALL MCP-based plugins.
log.step("Ensuring absolute bun paths in plugin .mcp.json files...");
const n = patchMcpBunPaths();
if (n === 0) log.success("All plugin .mcp.json files already use absolute bun paths");
// Discord patches

@@ -156,0 +199,0 @@ if (config.channelType === "discord" || config.patchAll) {

@@ -389,2 +389,42 @@ export function renderGuardian(config) {

// ─── MCP bun-path self-heal ───────────────────────────────────────────────
// Newer Claude Code sanitizes PATH when spawning MCP servers, so any plugin
// whose .mcp.json uses bare "command": "bun" fails silently with
// "No such file or directory" — the MCP never starts and the channel bot
// appears offline. Patch every plugin's .mcp.json to use the absolute path
// to bun (idempotent). Runs alongside checkAndReapplyPatches so it self-
// heals on every restart, regardless of what Claude Code does upstream.
function patchMcpBunPaths() {
const root = HOME + '/.claude/plugins/marketplaces';
if (!fs.existsSync(root)) return;
const bunAbs = HOME + '/.bun/bin/bun';
if (!fs.existsSync(bunAbs)) { log('MCP bun-path: bun not found at ' + bunAbs + ', skipping'); return; }
let touched = 0;
try {
for (const mp of fs.readdirSync(root)) {
const ext = root + '/' + mp + '/external_plugins';
if (!fs.existsSync(ext)) continue;
for (const pl of fs.readdirSync(ext)) {
const mcp = ext + '/' + pl + '/.mcp.json';
if (!fs.existsSync(mcp)) continue;
try {
const raw = fs.readFileSync(mcp, 'utf-8');
const data = JSON.parse(raw);
let changed = false;
for (const name of Object.keys(data.mcpServers || {})) {
const srv = data.mcpServers[name];
if (srv && srv.command === 'bun') { srv.command = bunAbs; changed = true; }
}
if (changed) {
fs.writeFileSync(mcp, JSON.stringify(data, null, 2));
log('MCP bun-path: patched ' + mp + '/' + pl);
touched++;
}
} catch (e) { log('MCP bun-path: skip ' + mcp + ' (' + e.message + ')'); }
}
}
} catch (e) { log('MCP bun-path scan error: ' + e.message); }
if (touched) log('MCP bun-path: ' + touched + ' file(s) updated');
}
// ─── Restart ───

@@ -410,3 +450,3 @@ function restart() {

catch (e) { log('Failed: ' + e.message); }
setTimeout(() => { checkAndReapplyPatches(); }, 10000);
setTimeout(() => { checkAndReapplyPatches(); patchMcpBunPaths(); }, 10000);
}, 3000);

@@ -514,2 +554,30 @@ }, 3000);

// AUTOCOMPACT v1: prevent agents from going unusable at high context. When
// tmux shows >= 80% context used, snapshot the recent buffer to memory/
// (so the agent has a paper trail of what was happening), then fire /compact.
// Throttled to once per 15 min so slow-clearing compact does not re-trigger.
// Wrapped so any bug here cannot break the rest of the guardian loop.
try {
const ctxMatch = o.match(/(\\d+)% context used/);
if (ctxMatch) {
const ctxPct = parseInt(ctxMatch[1], 10);
const now = Date.now();
const lastCompact = s.lastCompact || 0;
if (ctxPct >= 80 && (now - lastCompact) > 15 * 60 * 1000) {
log('AUTOCOMPACT: context at ' + ctxPct + '% — capturing snapshot');
try {
const snap = execFileSync('tmux', ['capture-pane', '-t', SESSION, '-p', '-S', '-200'], { encoding: 'utf8', timeout: 5000 });
const tsName = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const fname = process.env.HOME + '/workspace/memory/autocompact-' + tsName + '.log';
fs.writeFileSync(fname, '# Pre-compact snapshot\\n# Session: ' + SESSION + '\\n# Context: ' + ctxPct + '%\\n# Time: ' + new Date().toISOString() + '\\n# Reason: auto-fired by guardian at >= 80% context\\n\\n' + snap);
log('AUTOCOMPACT: snapshot saved to ' + fname);
} catch (snapE) { log('AUTOCOMPACT snapshot error (non-fatal): ' + (snapE && snapE.message)); }
log('AUTOCOMPACT: firing /compact');
execFileSync('tmux', ['send-keys', '-t', SESSION, '/compact', 'Enter'], { timeout: 5000 });
s.lastCompact = now;
sv(s);
}
}
} catch (e) { log('AUTOCOMPACT error (non-fatal): ' + (e && e.message)); }
// Check for permission prompts — AUTO-ACCEPT immediately

@@ -516,0 +584,0 @@ // Agents run with --dangerously-skip-permissions (user consented to full autonomy)

@@ -12,5 +12,11 @@ export function renderWatchdog(config) {

# ─── Guardian health check ───
# If Guardian is not running, relaunch it (may have exited after auto-update)
if ! pgrep -u "$(whoami)" -f "guardian.mjs" > /dev/null 2>&1; then
# ─── Guardian health check (DEDUP v1) ───
# Count guardians. Zero = relaunch. One = healthy. >1 = kill extras (keep oldest PID).
GUARDIAN_PIDS=\$(pgrep -u "$(whoami)" -f "guardian.mjs" 2>/dev/null)
GUARDIAN_COUNT=\$(echo -n "\$GUARDIAN_PIDS" | grep -c . || true)
if [ "\$GUARDIAN_COUNT" -gt 1 ]; then
EXTRAS=\$(echo "\$GUARDIAN_PIDS" | sort -n | tail -n +2)
echo "$(date): \$GUARDIAN_COUNT guardians running. Killing extras: \$EXTRAS" >> "\$LOG"
echo "\$EXTRAS" | xargs -r kill -9 2>/dev/null
elif [ "\$GUARDIAN_COUNT" -eq 0 ]; then
echo "$(date): Guardian not running. Relaunching..." >> "\$LOG"

@@ -17,0 +23,0 @@ cd "\$HOME/workspace" && nohup node guardian.mjs >> memory/guardian.log 2>&1 &