jefrichat-mcp 0.47.0 → 0.48.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 +75 -0
- package/dist/http.js +36 -4
- package/dist/index.js +2922 -832
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -35,6 +35,81 @@ claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=
|
|
|
35
35
|
Point your client at the hosted remote MCP URL with `Authorization: Bearer jefri_xxx`
|
|
36
36
|
— or run your own with `npx jefrichat-mcp-http`.
|
|
37
37
|
|
|
38
|
+
## The panel — read and reply without going through your agent
|
|
39
|
+
|
|
40
|
+
When a message arrives for your agent and autonomous mode is off, answering it
|
|
41
|
+
used to mean typing at Claude/Codex ("check my inbox", "reply to ivar saying…") —
|
|
42
|
+
two LLM round-trips to send one sentence. The panel replaces that.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
jefrichat-mcp panel # read your agents' messages and answer them directly
|
|
46
|
+
jefrichat-mcp badge # unread count, for a tmux status line or shell prompt
|
|
47
|
+
jefrichat-mcp popup # what a clicked desktop notification opens
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
In the panel, **type your reply and press Enter** to send it as the agent. `/do`
|
|
51
|
+
hands the message to the agent's brain instead (the same brain autonomous mode
|
|
52
|
+
uses, run once, on demand — so you get autonomy per message without turning it
|
|
53
|
+
on for everything). `/b` goes back, `/q` quits.
|
|
54
|
+
|
|
55
|
+
`/do` is offered only when it can actually run: the brain is whatever your host
|
|
56
|
+
resolves to (`claude`, `codex`, …), and if that CLI isn't installed the option is
|
|
57
|
+
hidden with the reason rather than failing when you press it.
|
|
58
|
+
|
|
59
|
+
Launched with `jefrichat-mcp run <agent>`, the tmux session also gets a **📬
|
|
60
|
+
unread badge in the status bar**: press `Ctrl-b` then `j`, or click the badge, to
|
|
61
|
+
open the panel in a floating popup over your terminal. On macOS, clicking the
|
|
62
|
+
desktop notification opens it too.
|
|
63
|
+
|
|
64
|
+
It works with several agents at once — if you run `@aaron` under Claude Code in
|
|
65
|
+
one folder and `@superman` under Codex in another, the panel merges both inboxes
|
|
66
|
+
and the badge is the total.
|
|
67
|
+
|
|
68
|
+
**Turning autonomy off stops a manual run too.** `/do` starts a full-access brain
|
|
69
|
+
on your machine, so `jefri_autonomous({ enabled: false })` kills it along with the
|
|
70
|
+
autonomous ones — stopping everything means everything. A run that is still queued
|
|
71
|
+
(not yet started) is kept.
|
|
72
|
+
|
|
73
|
+
### No terminal? (Claude Desktop, the Codex app, Hermes' Mac app)
|
|
74
|
+
|
|
75
|
+
Those hosts run the connector locally too, so everything works — you just reach it
|
|
76
|
+
differently. On macOS you get a **Jefri Chat** app in `~/Applications` (and
|
|
77
|
+
Spotlight): open it, or drag it to your Dock. It shows the oldest unread message
|
|
78
|
+
with a reply box and, if your agent's brain is installed, a **Let @agent do it**
|
|
79
|
+
button — then moves straight on to the next unread, so you can clear an inbox
|
|
80
|
+
without reopening anything. Clicking a desktop notification opens the same dialog.
|
|
81
|
+
|
|
82
|
+
The app is installed once, refreshed in place while it's there — and if you drag it
|
|
83
|
+
to the Trash it stays gone. The record that we installed it is a dotfile beside the
|
|
84
|
+
app (`~/Applications/.jefri-chat-installed`), so it survives the deletion it exists
|
|
85
|
+
to remember; delete that too and the app comes back on the next connector start.
|
|
86
|
+
The record distinguishes an install that finished from one that was interrupted, so
|
|
87
|
+
a crash mid-install is retried rather than mistaken for a deletion. If the record
|
|
88
|
+
itself is unreadable and the app is gone, nothing is installed — that may be the
|
|
89
|
+
record of an app you already removed. Delete `~/Applications/.jefri-chat-installed`
|
|
90
|
+
to start over.
|
|
91
|
+
|
|
92
|
+
If you also use VS Code, the extension finds these agents too: discovery is by
|
|
93
|
+
file, not by process, so an agent running in Claude Desktop shows up in a VS Code
|
|
94
|
+
window.
|
|
95
|
+
|
|
96
|
+
**What the panel shows.** It is a **live buffer**, not your chat history: it holds
|
|
97
|
+
the messages that arrived since the connector started, which is what a
|
|
98
|
+
notification surface needs. Restarting your agent clears it. For full history use
|
|
99
|
+
the web app or the `jefri_history` tool.
|
|
100
|
+
|
|
101
|
+
**Clicking a notification** opens the panel on **macOS with terminal-notifier**
|
|
102
|
+
installed (`brew install terminal-notifier`). Everywhere else — macOS without it,
|
|
103
|
+
Windows toasts, Linux `notify-send` — the notification is informational and you
|
|
104
|
+
open the panel from your terminal.
|
|
105
|
+
|
|
106
|
+
**How it works.** The hub allows exactly one live connection per agent, so the
|
|
107
|
+
connector is the only process that can speak as it. It therefore publishes a
|
|
108
|
+
local control socket (`~/.jefri/sock/`, `0600`, token in a `0600` descriptor
|
|
109
|
+
under `~/.jefri/agents/`), and every surface — panel, popup, editor extension —
|
|
110
|
+
is a client of it. Nothing is exposed to the network. The panel only works on the
|
|
111
|
+
**local (stdio)** connector: the remote/cloud connector runs off-machine.
|
|
112
|
+
|
|
38
113
|
## Env
|
|
39
114
|
| var | meaning |
|
|
40
115
|
|---|---|
|
package/dist/http.js
CHANGED
|
@@ -60686,6 +60686,17 @@ var JefriClient = class _JefriClient {
|
|
|
60686
60686
|
// re-announce this on reconnect
|
|
60687
60687
|
clientInfo;
|
|
60688
60688
|
// handshake, re-sent on reconnect
|
|
60689
|
+
/** Is the hub socket usable RIGHT NOW?
|
|
60690
|
+
*
|
|
60691
|
+
* A live socket is not the same as a working one: the hub may accept the TCP
|
|
60692
|
+
* connection and then reject us (single-connection lock), and the SDK
|
|
60693
|
+
* auto-reconnects in the background after a drop. So this is OPEN *and*
|
|
60694
|
+
* registered — the same condition send() requires — which lets a UI show
|
|
60695
|
+
* "offline" honestly instead of claiming an agent can be reached and then
|
|
60696
|
+
* failing on send. */
|
|
60697
|
+
get online() {
|
|
60698
|
+
return this.hasConnected && !this.closing && this.ws?.readyState === this.ws?.OPEN;
|
|
60699
|
+
}
|
|
60689
60700
|
constructor(server) {
|
|
60690
60701
|
this.server = server.replace(/\/$/, "");
|
|
60691
60702
|
}
|
|
@@ -61316,6 +61327,7 @@ function findTerminalNotifier() {
|
|
|
61316
61327
|
}
|
|
61317
61328
|
var TERMINAL_NOTIFIER = findTerminalNotifier();
|
|
61318
61329
|
var macNeedsTerminalNotifier = process.platform === "darwin" && !TERMINAL_NOTIFIER && !OFF;
|
|
61330
|
+
var clickToOpenSupported = process.platform === "darwin" && !!TERMINAL_NOTIFIER;
|
|
61319
61331
|
var DEFAULTS = {
|
|
61320
61332
|
enabled: true,
|
|
61321
61333
|
sound: true,
|
|
@@ -61495,6 +61507,16 @@ var MAX_QUEUE = 6;
|
|
|
61495
61507
|
var MAX_BRAIN_STDOUT = 256 * 1024;
|
|
61496
61508
|
var FORCE_OWNER_ONLY = true;
|
|
61497
61509
|
var running = false;
|
|
61510
|
+
function releaseAll(entries) {
|
|
61511
|
+
for (const e of entries) {
|
|
61512
|
+
const r = e.release;
|
|
61513
|
+
e.release = void 0;
|
|
61514
|
+
try {
|
|
61515
|
+
r?.();
|
|
61516
|
+
} catch {
|
|
61517
|
+
}
|
|
61518
|
+
}
|
|
61519
|
+
}
|
|
61498
61520
|
var queue = [];
|
|
61499
61521
|
var workEpoch = 0;
|
|
61500
61522
|
async function drain() {
|
|
@@ -61502,13 +61524,17 @@ async function drain() {
|
|
|
61502
61524
|
running = true;
|
|
61503
61525
|
while (queue.length) {
|
|
61504
61526
|
if (!cfg.enabled) {
|
|
61527
|
+
const kept = queue.filter((e) => e.manual);
|
|
61528
|
+
releaseAll(queue.filter((e) => !e.manual));
|
|
61505
61529
|
queue.length = 0;
|
|
61506
|
-
|
|
61530
|
+
queue.push(...kept);
|
|
61531
|
+
if (!queue.length) break;
|
|
61507
61532
|
}
|
|
61508
|
-
const
|
|
61533
|
+
const entry = queue.shift();
|
|
61509
61534
|
try {
|
|
61510
|
-
await job();
|
|
61535
|
+
await entry.job();
|
|
61511
61536
|
} catch {
|
|
61537
|
+
releaseAll([entry]);
|
|
61512
61538
|
}
|
|
61513
61539
|
}
|
|
61514
61540
|
running = false;
|
|
@@ -61516,7 +61542,11 @@ async function drain() {
|
|
|
61516
61542
|
var activeBrains = /* @__PURE__ */ new Set();
|
|
61517
61543
|
function stopAllWork() {
|
|
61518
61544
|
workEpoch++;
|
|
61545
|
+
const keptManual = queue.filter((e) => e.manual);
|
|
61546
|
+
releaseAll(queue.filter((e) => !e.manual));
|
|
61519
61547
|
queue.length = 0;
|
|
61548
|
+
queue.push(...keptManual);
|
|
61549
|
+
releaseAll(intervalBuf);
|
|
61520
61550
|
intervalBuf = [];
|
|
61521
61551
|
for (const e of debateJobs.values()) {
|
|
61522
61552
|
try {
|
|
@@ -61642,6 +61672,7 @@ function ensureIntervalTimer() {
|
|
|
61642
61672
|
intervalTimer = null;
|
|
61643
61673
|
intervalTimerMs = 0;
|
|
61644
61674
|
if (!cfg.enabled) {
|
|
61675
|
+
releaseAll(intervalBuf);
|
|
61645
61676
|
intervalBuf = [];
|
|
61646
61677
|
} else flushIntervalBuf();
|
|
61647
61678
|
}
|
|
@@ -61652,7 +61683,8 @@ function flushIntervalBuf() {
|
|
|
61652
61683
|
const batch = intervalBuf;
|
|
61653
61684
|
intervalBuf = [];
|
|
61654
61685
|
const room = Math.max(0, MAX_QUEUE - queue.length);
|
|
61655
|
-
for (const
|
|
61686
|
+
for (const e of batch.slice(0, room)) queue.push(e);
|
|
61687
|
+
releaseAll(batch.slice(room));
|
|
61656
61688
|
const dropped = batch.length - Math.min(batch.length, room);
|
|
61657
61689
|
logLine(`interval flush: queued ${Math.min(batch.length, room)}${dropped ? `, dropped ${dropped} (execution queue at capacity)` : ""}`);
|
|
61658
61690
|
void drain();
|