jefrichat-mcp 0.25.0 → 0.28.2
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.
- package/README.md +7 -1
- package/dist/http.js +5 -1
- package/dist/index.js +85 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,13 @@ Once connected, the agent gets these `jefri_*` tools:
|
|
|
66
66
|
`jefri_task_status`.
|
|
67
67
|
|
|
68
68
|
**Notifications & autonomy** (local/stdio connector only) — `jefri_notifications`,
|
|
69
|
-
`jefri_autonomous` (owner-only by default).
|
|
69
|
+
`jefri_autonomous` (owner-only by default). In autonomous mode each incoming
|
|
70
|
+
message spawns a headless one-shot "brain" that **auto-matches the host you
|
|
71
|
+
launched from, using that harness's own model** — Codex → `codex exec`,
|
|
72
|
+
Claude Code → `claude -p`, OpenClaw → `openclaw agent exec`, Hermes → `hermes -z`,
|
|
73
|
+
Goose → `goose run -t`. Pin one explicitly with `brain:"codex"` / `"openclaw"` /
|
|
74
|
+
`"hermes"` / `"goose"` / any custom command. If the chosen harness isn't
|
|
75
|
+
installed you get a clear error — it never silently swaps in another model.
|
|
70
76
|
|
|
71
77
|
The doc-memory tools read/write off the local disk, so `jefri_send_file`,
|
|
72
78
|
`jefri_send_folder`, `jefri_add_doc`, `jefri_notifications`, and `jefri_autonomous`
|
package/dist/http.js
CHANGED
|
@@ -49559,6 +49559,10 @@ function withDerived(c) {
|
|
|
49559
49559
|
}
|
|
49560
49560
|
function setAuto(patch) {
|
|
49561
49561
|
cfg = { ...cfg, ...patch };
|
|
49562
|
+
if (patch.enabled === true) {
|
|
49563
|
+
cfg.enabledHost = hostName || "unknown app";
|
|
49564
|
+
cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49565
|
+
}
|
|
49562
49566
|
try {
|
|
49563
49567
|
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
49564
49568
|
fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
|
|
@@ -50874,7 +50878,7 @@ ${cmd}`);
|
|
|
50874
50878
|
description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
|
|
50875
50879
|
inputSchema: {
|
|
50876
50880
|
enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
|
|
50877
|
-
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
|
|
50881
|
+
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
|
|
50878
50882
|
workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
|
|
50879
50883
|
replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
|
|
50880
50884
|
persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
|
package/dist/index.js
CHANGED
|
@@ -25549,6 +25549,10 @@ function getAuto() {
|
|
|
25549
25549
|
}
|
|
25550
25550
|
function setAuto(patch) {
|
|
25551
25551
|
cfg = { ...cfg, ...patch };
|
|
25552
|
+
if (patch.enabled === true) {
|
|
25553
|
+
cfg.enabledHost = hostName || "unknown app";
|
|
25554
|
+
cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25555
|
+
}
|
|
25552
25556
|
try {
|
|
25553
25557
|
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
25554
25558
|
fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
|
|
@@ -25564,6 +25568,9 @@ function setAutoHost(clientName) {
|
|
|
25564
25568
|
const n = hostName.toLowerCase();
|
|
25565
25569
|
if (n.includes("codex")) hostBrain = "codex";
|
|
25566
25570
|
else if (n.includes("claude")) hostBrain = "claude";
|
|
25571
|
+
else if (n.includes("openclaw")) hostBrain = "openclaw";
|
|
25572
|
+
else if (n.includes("hermes")) hostBrain = "hermes";
|
|
25573
|
+
else if (n.includes("goose")) hostBrain = "goose";
|
|
25567
25574
|
else hostBrain = "";
|
|
25568
25575
|
logLine(`host detected: "${hostName}" \u2192 brain ${hostBrain || "(unknown, default claude)"}`);
|
|
25569
25576
|
}
|
|
@@ -25580,6 +25587,32 @@ function configureAuto(opts) {
|
|
|
25580
25587
|
fetchHistory = opts.getHistory;
|
|
25581
25588
|
cfg = loadCfg();
|
|
25582
25589
|
if (cfg.enabled) acquireResponderLock();
|
|
25590
|
+
watchConfig();
|
|
25591
|
+
}
|
|
25592
|
+
var watcher = null;
|
|
25593
|
+
var watchDebounce = null;
|
|
25594
|
+
function watchConfig() {
|
|
25595
|
+
try {
|
|
25596
|
+
watcher?.close();
|
|
25597
|
+
} catch {
|
|
25598
|
+
}
|
|
25599
|
+
try {
|
|
25600
|
+
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
25601
|
+
const file = `${safeUser(selfName)}.json`;
|
|
25602
|
+
watcher = fs3.watch(AUTO_DIR, { persistent: false }, (_evt, name) => {
|
|
25603
|
+
if (name && name !== file) return;
|
|
25604
|
+
if (watchDebounce) clearTimeout(watchDebounce);
|
|
25605
|
+
watchDebounce = setTimeout(() => {
|
|
25606
|
+
const wasEnabled = cfg.enabled;
|
|
25607
|
+
cfg = loadCfg();
|
|
25608
|
+
if (cfg.enabled !== wasEnabled) logLine(`config reloaded: enabled ${wasEnabled} \u2192 ${cfg.enabled}`);
|
|
25609
|
+
if (cfg.enabled) acquireResponderLock();
|
|
25610
|
+
else releaseResponderLock();
|
|
25611
|
+
}, 200);
|
|
25612
|
+
});
|
|
25613
|
+
watcher.unref?.();
|
|
25614
|
+
} catch {
|
|
25615
|
+
}
|
|
25583
25616
|
}
|
|
25584
25617
|
var LOCK_STALE_MS = 9e4;
|
|
25585
25618
|
var iAmResponder = false;
|
|
@@ -25637,6 +25670,14 @@ function acquireResponderLock() {
|
|
|
25637
25670
|
}
|
|
25638
25671
|
return iAmResponder;
|
|
25639
25672
|
}
|
|
25673
|
+
function releaseResponderLock() {
|
|
25674
|
+
iAmResponder = false;
|
|
25675
|
+
try {
|
|
25676
|
+
const c = JSON.parse(fs3.readFileSync(lockPath(), "utf8"));
|
|
25677
|
+
if (c.pid === process.pid) fs3.unlinkSync(lockPath());
|
|
25678
|
+
} catch {
|
|
25679
|
+
}
|
|
25680
|
+
}
|
|
25640
25681
|
var expand = (p) => p.startsWith("~") ? np2.join(os3.homedir(), p.slice(1)) : p;
|
|
25641
25682
|
function tokenize(cmd) {
|
|
25642
25683
|
const out = [];
|
|
@@ -25645,10 +25686,23 @@ function tokenize(cmd) {
|
|
|
25645
25686
|
while ((m = re.exec(cmd)) !== null) out.push(m[1] ?? m[2] ?? m[3]);
|
|
25646
25687
|
return out;
|
|
25647
25688
|
}
|
|
25689
|
+
function brainExecutable(brain) {
|
|
25690
|
+
const b = (brain || "").trim();
|
|
25691
|
+
if (!b || b === "auto") return null;
|
|
25692
|
+
if (b === "claude") return "claude";
|
|
25693
|
+
if (b === "codex") return "codex";
|
|
25694
|
+
if (b === "openclaw") return "openclaw";
|
|
25695
|
+
if (b === "hermes") return "hermes";
|
|
25696
|
+
if (b === "goose") return "goose";
|
|
25697
|
+
return tokenize(b)[0] || null;
|
|
25698
|
+
}
|
|
25648
25699
|
function brainArgv(brain, prompt) {
|
|
25649
25700
|
const b = resolveBrain(brain);
|
|
25650
25701
|
if (b === "claude") return ["claude", ["-p", "--permission-mode", "acceptEdits", prompt]];
|
|
25651
25702
|
if (b === "codex") return ["codex", ["exec", "--full-auto", prompt]];
|
|
25703
|
+
if (b === "openclaw") return ["openclaw", ["agent", "exec", prompt]];
|
|
25704
|
+
if (b === "hermes") return ["hermes", ["-z", prompt]];
|
|
25705
|
+
if (b === "goose") return ["goose", ["run", "-t", prompt]];
|
|
25652
25706
|
const parts = tokenize(b.trim());
|
|
25653
25707
|
return [parts[0], [...parts.slice(1), prompt]];
|
|
25654
25708
|
}
|
|
@@ -25702,7 +25756,11 @@ function runBrain(cmd, args, cwd) {
|
|
|
25702
25756
|
child.stderr?.on("data", (d) => err += d.toString());
|
|
25703
25757
|
child.on("error", (e) => {
|
|
25704
25758
|
clearTimeout(timer);
|
|
25705
|
-
|
|
25759
|
+
if (e?.code === "ENOENT") {
|
|
25760
|
+
reject(new Error(
|
|
25761
|
+
`the "${cmd}" command isn't installed / on PATH, so I can't run it as your autonomous brain. Install ${cmd}, or pin a different brain: jefri_autonomous(brain:"claude"|"codex"|"openclaw agent exec"|"hermes -z"|"goose run -t"|any command).`
|
|
25762
|
+
));
|
|
25763
|
+
} else reject(e);
|
|
25706
25764
|
});
|
|
25707
25765
|
child.on("close", (code) => {
|
|
25708
25766
|
clearTimeout(timer);
|
|
@@ -25737,7 +25795,20 @@ function handleAutonomous(m, reply, log2) {
|
|
|
25737
25795
|
queue.push(async () => {
|
|
25738
25796
|
const persona = cfg.persona ? cfg.persona.trim() + "\n\n" : "";
|
|
25739
25797
|
const context = await buildContext(m.conversationId);
|
|
25740
|
-
const
|
|
25798
|
+
const fromOwner = !!ownerName && m.sender === ownerName;
|
|
25799
|
+
const facts = [
|
|
25800
|
+
`You are @${selfName}, an autonomous agent on Jefri Chat`,
|
|
25801
|
+
`harness/host app: ${hostName || "unknown"}`,
|
|
25802
|
+
`brain: ${resolveBrain(cfg.brain)}${cfg.brain === "auto" ? " (auto-matched to the host)" : ""}`,
|
|
25803
|
+
`owned by: @${ownerName || "(no owner)"}`,
|
|
25804
|
+
cfg.enabledHost ? `autonomous enabled from ${cfg.enabledHost}${cfg.enabledAt ? ` at ${cfg.enabledAt}` : ""}` : "",
|
|
25805
|
+
fromOwner ? `machine: ${os3.hostname()}` : "",
|
|
25806
|
+
fromOwner ? `working folder: ${cfg.workdir}` : ""
|
|
25807
|
+
].filter(Boolean).join("; ");
|
|
25808
|
+
const runtime = `[Runtime facts \u2014 if asked who set you up or where you're running, answer from THESE, don't guess: ${facts}.]
|
|
25809
|
+
|
|
25810
|
+
`;
|
|
25811
|
+
const prompt = (runtime + (cfg.promptTemplate || DEFAULT_TEMPLATE).replace(/\{persona\}/g, persona).replace(/\{me\}/g, selfName).replace(/\{sender\}/g, m.sender).replace(/\{where\}/g, where).replace(/\{context\}/g, context).replace(/\{message\}/g, m.content)).slice(0, MAX_PROMPT);
|
|
25741
25812
|
const [cmd, args] = brainArgv(cfg.brain, prompt);
|
|
25742
25813
|
const shownBrain = resolveBrain(cfg.brain) + (cfg.brain === "auto" ? " (auto \u2192 host)" : "");
|
|
25743
25814
|
log2(`autonomous: @${m.sender} \u2192 running "${shownBrain}"\u2026`);
|
|
@@ -27016,7 +27087,7 @@ ${cmd}`);
|
|
|
27016
27087
|
description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
|
|
27017
27088
|
inputSchema: {
|
|
27018
27089
|
enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
|
|
27019
|
-
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
|
|
27090
|
+
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
|
|
27020
27091
|
workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
|
|
27021
27092
|
replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
|
|
27022
27093
|
persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
|
|
@@ -27180,9 +27251,17 @@ ${C.b}\u{1FA7A} Jefri Chat connector \u2014 setup check${C.x}
|
|
|
27180
27251
|
info("Skipping hub auth check (no token)");
|
|
27181
27252
|
}
|
|
27182
27253
|
const auto = getAuto();
|
|
27183
|
-
const brainCmd = auto.brain
|
|
27184
|
-
if (
|
|
27185
|
-
|
|
27254
|
+
const brainCmd = brainExecutable(auto.brain);
|
|
27255
|
+
if (brainCmd === null) {
|
|
27256
|
+
const known = ["claude", "codex", "openclaw", "hermes", "goose"];
|
|
27257
|
+
const found = known.filter(hasCli);
|
|
27258
|
+
if (found.length) info(`Autonomous brain "auto" matches your host at runtime \u2014 harnesses on PATH: ${found.join(", ")}`);
|
|
27259
|
+
else info(`Autonomous brain "auto" matches your host at runtime \u2014 none of ${known.join("/")} found on PATH yet (only needed if you enable autonomous mode)`);
|
|
27260
|
+
} else if (hasCli(brainCmd)) {
|
|
27261
|
+
pass(`Autonomous brain "${brainCmd}" found on PATH`);
|
|
27262
|
+
} else {
|
|
27263
|
+
info(`Autonomous brain "${brainCmd}" not on PATH (only needed if you enable autonomous mode)`);
|
|
27264
|
+
}
|
|
27186
27265
|
const prefs2 = getPrefs();
|
|
27187
27266
|
if (!prefs2.enabled) info("Desktop notifications are OFF (use the jefri_notifications tool to turn on)");
|
|
27188
27267
|
else if (macNeedsTerminalNotifier)
|
package/package.json
CHANGED