kojee-mcp 0.5.3 → 0.5.6
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 +112 -5
- package/dist/{chunk-YEC7IHIG.js → chunk-2BDAM3TH.js} +92 -523
- package/dist/chunk-2MIISF2W.js +35 -0
- package/dist/chunk-3XDJOHMZ.js +223 -0
- package/dist/{chunk-ZW4SW7LJ.js → chunk-64EOLZNI.js} +14 -5
- package/dist/chunk-6SK6ITFE.js +142 -0
- package/dist/chunk-GI2CKKBL.js +46 -0
- package/dist/chunk-HIZ4NDWN.js +141 -0
- package/dist/chunk-LDZXU3DW.js +170 -0
- package/dist/{resubscribe-SLZNA76S.js → chunk-OT2GILXC.js} +1 -0
- package/dist/{chunk-WBMX4CHB.js → chunk-UEGQGXPY.js} +57 -40
- package/dist/chunk-V5VZPYMZ.js +185 -0
- package/dist/{chunk-C6GZ2L2W.js → chunk-X672ZN7V.js} +5 -2
- package/dist/cli.js +47 -24
- package/dist/{codex-stop-hook-JOTBCS5K.js → codex-stop-hook-SWA53ECG.js} +1 -1
- package/dist/control-token-4BUCTYQB.js +13 -0
- package/dist/{doctor-TSHOMT5X.js → doctor-QCQDFLEH.js} +30 -17
- package/dist/{doctor-codex-BMI5JOO6.js → doctor-codex-NZ53ROQA.js} +12 -5
- package/dist/ensure-join-7AEDJMPE.js +96 -0
- package/dist/gateway-client-93P1E0CZ.d.ts +92 -0
- package/dist/{hook-server-QF5JVUHV.js → hook-server-37E2LUKJ.js} +91 -0
- package/dist/index.d.ts +18 -15
- package/dist/index.js +9 -3
- package/dist/lib.d.ts +427 -0
- package/dist/lib.js +44 -0
- package/dist/reconnect-scheduler-JSXCJKQP.js +26 -0
- package/dist/resubscribe-G5OGDZJD.js +6 -0
- package/dist/send-cli-C2F4WTBN.js +72 -0
- package/dist/{stop-hook-SEPWWETV.js → stop-hook-TRAMQYNE.js} +16 -8
- package/dist/{tail-stream-BYKO4DW6.js → tail-stream-VUZBYKXS.js} +4 -3
- package/dist/{user-prompt-submit-hook-ARPEO6FF.js → user-prompt-submit-hook-ZD2XKN7U.js} +7 -1
- package/dist/webhook-config-O4WMQ532.js +20 -0
- package/dist/{webhook-sink-7OYZBWXA.js → webhook-sink-NWGCUDGY.js} +28 -5
- package/dist/{wizard-7KHD5JT4.js → wizard-OSOAY4GO.js} +64 -27
- package/package.json +11 -2
- package/dist/chunk-F7L25L2J.js +0 -60
- package/dist/webhook-config-5TLLX7RA.js +0 -10
package/README.md
CHANGED
|
@@ -218,8 +218,8 @@ in it is Hermes-specific — and it is **OFF by default**.
|
|
|
218
218
|
|---|---|
|
|
219
219
|
| `KOJEE_WEBHOOK_URL` | Receiver endpoint (http/https). **Unset ⇒ sink OFF** (zero behavior change). |
|
|
220
220
|
| `KOJEE_WEBHOOK_SECRET` | HMAC-SHA256 key for the signature header. URL set but secret unset ⇒ sink **DISABLED with an error** (the proxy NEVER sends unsigned webhooks). |
|
|
221
|
-
| `KOJEE_WEBHOOK_TIMEOUT_MS` | Per-attempt request timeout (default `
|
|
222
|
-
| `KOJEE_WEBHOOK_MAX_RETRIES` | Retries on a retryable failure —
|
|
221
|
+
| `KOJEE_WEBHOOK_TIMEOUT_MS` | Per-attempt request timeout (default `30000`). A timed-out attempt is **never retried** — see the retry policy below. |
|
|
222
|
+
| `KOJEE_WEBHOOK_MAX_RETRIES` | Retries on a **retryable** failure — connection errors (refused / reset / DNS) / 5xx / 408 / 429 (default `2`). Timeouts are **not** in this class. |
|
|
223
223
|
| `KOJEE_WEBHOOK_SIGNATURE_HEADER` | Header name carrying the signature (default `X-Kojee-Signature`). |
|
|
224
224
|
| `KOJEE_WEBHOOK_SIGNATURE_PREFIX` | Literal string prepended to the hex digest (default empty — bare hex). |
|
|
225
225
|
| `KOJEE_WEBHOOK_SIGNATURE_FORMAT` | Optional preset. `github` ⇒ header `X-Hub-Signature-256`, prefix `sha256=` (the GitHub-webhook convention). Explicit `_HEADER`/`_PREFIX` vars override the preset's corresponding value. Unknown values are **warned about once and ignored** — never fatal. |
|
|
@@ -275,10 +275,117 @@ constant beside it):
|
|
|
275
275
|
cursor on restart, so the same event may arrive more than once. There is **no
|
|
276
276
|
exactly-once** promise; the receiver's dedupe is what makes redelivery safe.
|
|
277
277
|
|
|
278
|
+
**Retry policy (0.5.6 — anti-storm).** A delivery attempt that **times out is
|
|
279
|
+
never retried**: the receiver may well have processed the event and just
|
|
280
|
+
answered slowly (the canonical receiver spawns an agent session *before*
|
|
281
|
+
responding), so a re-POST risks duplicate side effects by design. The sink logs
|
|
282
|
+
it as `delivered-unconfirmed (receiver slow)` and moves on. Retries happen
|
|
283
|
+
**only on genuine non-delivery**: connection errors (refused / reset / DNS —
|
|
284
|
+
the request never reached a receiver) and 5xx / 408 / 429 responses (the
|
|
285
|
+
receiver answered that it did *not* process the event), up to
|
|
286
|
+
`KOJEE_WEBHOOK_MAX_RETRIES` (default `2`). For those retried cases delivery is
|
|
287
|
+
still **at-least-once** and every redelivery carries the same
|
|
288
|
+
`X-Kojee-Delivery` id and identical body bytes — **dedupe by event id remains
|
|
289
|
+
the receiver's responsibility** (the `recipe.ts` contract). This fix removes
|
|
290
|
+
the timeout-driven storm, not the at-least-once semantics. If your receiver
|
|
291
|
+
does slow work, the robust pattern is still: verify the signature, dedupe,
|
|
292
|
+
**respond `202` immediately**, then process.
|
|
293
|
+
|
|
278
294
|
The sink is isolated and fire-and-forget: a slow, hanging, or failing webhook can
|
|
279
|
-
never delay or break the Monitor (event-log) or Channel wake paths
|
|
280
|
-
|
|
281
|
-
|
|
295
|
+
never delay or break the Monitor (event-log) or Channel wake paths (those run
|
|
296
|
+
before the webhook push). A slow delivery only delays *later webhook events*
|
|
297
|
+
behind it in the sink's own FIFO, and that backlog is bounded: the queue caps at
|
|
298
|
+
1000 (overflow logs + drops the newest; the resubscribe-replay redelivers after
|
|
299
|
+
a restart). The status log redacts the secret and strips any basic-auth
|
|
300
|
+
credentials embedded in `KOJEE_WEBHOOK_URL`.
|
|
301
|
+
|
|
302
|
+
## Wake Continuity (0.5.4)
|
|
303
|
+
|
|
304
|
+
The event-stream subscription is a **connect-time snapshot of the caller's
|
|
305
|
+
memberships**. A daemon that rotates its session identity across restarts used
|
|
306
|
+
to come up with its tandem seat still bound to the OLD session — subscribed to
|
|
307
|
+
nothing, deaf to webhook wakes, while sends kept working (agent-scoped
|
|
308
|
+
fallback). Two mechanisms close this permanently:
|
|
309
|
+
|
|
310
|
+
**Ensure-join at startup.** After auth and *before* the event stream connects,
|
|
311
|
+
the daemon ensure-joins its live session (`tandem_join` is idempotent — an
|
|
312
|
+
existing seat returns `already_member`). One log line per tandem (`joined
|
|
313
|
+
fresh` vs `already seated`); failures warn and continue (a bad id never kills
|
|
314
|
+
the daemon).
|
|
315
|
+
|
|
316
|
+
| `KOJEE_TANDEMS` | Behavior |
|
|
317
|
+
|---|---|
|
|
318
|
+
| *(unset — the default)* | **Auto**: `tandem_list` (which is **principal-scoped** — it also lists rooms where only *sibling* agents of the principal sit), **filtered to rows where THIS agent holds an active seat** (`my_membership.is_member === true`; rows missing the flag are excluded — fail closed), then ensure-join each with the live session. Auto mode re-seats the agent where it already belongs; it never joins rooms the agent was not in. |
|
|
319
|
+
| `<id>,<id>,…` | Join exactly these tandem ObjectId hexes (24-hex; invalid entries warned + skipped). |
|
|
320
|
+
| `none` | Disable ensure-join entirely. |
|
|
321
|
+
|
|
322
|
+
**Stream reconnect after join.** Any successful `tandem_join` performed by the
|
|
323
|
+
daemon path (the startup ensure-join, or the MCP tool called through the proxy
|
|
324
|
+
by its agent) triggers a graceful event-stream reconnect (close + reconnect via
|
|
325
|
+
the existing backoff machinery, resuming from the per-room cursors), so the
|
|
326
|
+
subscription snapshot always includes just-acquired seats. Multiple joins in a
|
|
327
|
+
burst are debounced into one reconnect. No daemon restart needed, ever.
|
|
328
|
+
|
|
329
|
+
## Local Send Control Surface (0.5.4)
|
|
330
|
+
|
|
331
|
+
One stable, supported way to **send** a Tandem message from *outside* the proxy
|
|
332
|
+
process — for native gateway plugins, scripts, and humans. Two halves, one
|
|
333
|
+
shared core (`src/tandem/send.ts`), one envelope contract.
|
|
334
|
+
|
|
335
|
+
### CLI: `kojee-mcp send`
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
kojee-mcp send <tandem_id> --body "hello" [--reply-to <message_id>] [--kind message|status]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Uses the machine's **paired** credentials — the same `~/.kojee/config.json` +
|
|
342
|
+
`~/.kojee/keypair.json` the proxy reads, the same DPoP/GatewayClient path, the
|
|
343
|
+
same deterministic session seat. Prints **one JSON envelope** to stdout and
|
|
344
|
+
exits `0`/`1`:
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
{"ok":true,"tandem_id":"T-1","message_id":"m_123","cursor":42,"text":"..."}
|
|
348
|
+
{"ok":false,"error":"content_blocked","message":"content blocked by gateway — ..."}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### HTTP: `POST /send` on the running daemon's hook-server
|
|
352
|
+
|
|
353
|
+
Lets a plugin riding a **running** daemon send without spawning Node. Find the
|
|
354
|
+
port and the bearer via the session-discovery file
|
|
355
|
+
(`~/.kojee/sessions/cc-<key>.json` → `port`, `controlTokenPath`):
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
curl -s -X POST "http://127.0.0.1:$PORT/send" \
|
|
359
|
+
-H "Authorization: Bearer $(cat ~/.kojee/control-token)" \
|
|
360
|
+
-H "Content-Type: application/json" \
|
|
361
|
+
-d '{"tandem_id":"T-1","body":"hello","reply_to":"m_0","kind":"message"}'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Returns the **same JSON envelope** as the CLI. HTTP status mirrors the typed
|
|
365
|
+
error: `200` ok · `400 bad_request` · `401 unauthorized` ·
|
|
366
|
+
`403 content_blocked / member_cap / not_member / governance_denied` ·
|
|
367
|
+
`429 rate_limited` · `502` upstream (gateway auth/network/unknown) ·
|
|
368
|
+
`503 send_unavailable`.
|
|
369
|
+
|
|
370
|
+
**Auth.** Every endpoint that returns or writes this principal's data —
|
|
371
|
+
`POST /send`, `GET /poll`, `GET /status` — requires the same bearer: a fresh
|
|
372
|
+
random token issued at every daemon start, stored `0600` at
|
|
373
|
+
`~/.kojee/control-token` (rotates on restart — re-read the file, don't cache).
|
|
374
|
+
Presenting it proves the caller can read this user's files; other local users
|
|
375
|
+
and browser drive-by requests cannot. Only `GET /health` (a liveness ping
|
|
376
|
+
carrying no data) stays open. The in-tree consumers (the Claude Code Stop /
|
|
377
|
+
UserPromptSubmit hooks and `kojee-mcp doctor`) read the token from the path
|
|
378
|
+
advertised in the session-discovery file and send it automatically; the
|
|
379
|
+
Monitor wake path tails the event-log *file* and never touches `/poll`. If
|
|
380
|
+
the daemon could not issue a token at start (exotic FS), the reads degrade
|
|
381
|
+
open and `POST /send` answers `503` — loudly logged.
|
|
382
|
+
|
|
383
|
+
**Typed errors** (`error` field, both surfaces): `member_cap`,
|
|
384
|
+
`content_blocked` (the gateway WAF rejecting message *content* — commonly a
|
|
385
|
+
literal URL in the body; de-fang or split it; this is **not** an auth failure),
|
|
386
|
+
`gateway_auth`, `not_member`, `rate_limited`, `network`, `governance_denied`,
|
|
387
|
+
`approval_required`, `not_paired`, `not_enrolled`, `bad_request`,
|
|
388
|
+
`unauthorized`, `send_unavailable`, `send_failed` (raw text preserved).
|
|
282
389
|
|
|
283
390
|
## Development
|
|
284
391
|
|