@essentialai/cogent-bridge 3.5.6 → 3.5.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.5.8 — 2026-07-03
4
+
5
+ ### Added
6
+ - **`watch` subcommand — a proactive poller/notifier.** `npx @essentialai/cogent-bridge watch --peer <id>`
7
+ runs a standalone, read-only process that polls the relay's existing `GET /api/sessions/:sessionId/poll`
8
+ and fires an OS notification (`osascript` on macOS / `notify-send` on Linux, with a stdout fallback) when
9
+ a message is directed at a peer that can't auto-respond — e.g. a **Claude Desktop** agent, which the
10
+ `claude --resume` auto-response mechanism can't wake. Reuses the per-cwd credential store; flags:
11
+ `--peer` (required), `--label`, `--interval` (default 15s), `--cwd`. New `src/watch/` (filter, poll-client,
12
+ notifier, orchestrator) + one additive `watch` branch in `cli.ts` — ZERO core-runtime edits, no breaking
13
+ change. (Approach C, an in-process WS hook, remains a backlog iteration.)
14
+
15
+ ### Internal
16
+ - 20 new unit tests (`src/watch/*.test.ts`); full unit gate green.
17
+
18
+ ## 3.5.7 — 2026-07-01
19
+
20
+ ### Fixed
21
+ - **Plugin now pins the exact bridge version, so `plugin update` actually updates the bridge.**
22
+ The plugin's `.mcp.json` spawned `npx -y @essentialai/cogent-bridge` with NO version — a bare
23
+ `npx <pkg>` reuses whatever build is already in the `~/.npm/_npx` cache and does not re-fetch,
24
+ so updating the plugin (skill) left the MCP-server BINARY stale (agents kept registering an old
25
+ `clientVersion` even after `claude plugin update` + restart). Both plugin manifests now pin
26
+ `@essentialai/cogent-bridge@3.5.7`; because each release bumps this pin, `claude plugin update
27
+ cogent` (then restart) pulls the plugin whose config names the exact matching bridge — plugin
28
+ and bridge move together, deterministically, with no manual npx-cache clearing. See
29
+ `plugin/.mcp.json`, `codex-plugin/.mcp.json`. (Release convention: bump the mcp.json pin every
30
+ release — added to the release skill.)
31
+
3
32
  ## 3.5.6 — 2026-07-01
4
33
 
5
34
  ### Added
package/README.md CHANGED
@@ -349,3 +349,16 @@ src/
349
349
  ## License
350
350
 
351
351
  Apache-2.0
352
+
353
+ ### `watch` — proactive notifier for a peer
354
+
355
+ Watches a channel and fires an OS notification when a message is directed at a peer
356
+ that can't auto-respond (e.g. a Claude Desktop agent). Read-only; it does not reply.
357
+
358
+ # run from the SAME directory you registered the channel in (so it finds the saved creds)
359
+ npx @essentialai/cogent-bridge watch --peer dnarc-architect --label DARC --interval 15
360
+
361
+ Flags: `--peer <peerId>` (required), `--label <displayLabel>` (optional, for `[→ Label]` tags),
362
+ `--interval <seconds>` (default 15), `--cwd <dir>` (informational). Credentials are read from the
363
+ per-cwd store the bridge writes on join (`~/.cogent/credentials/…`), or `COGENT_CREDENTIALS_FILE`.
364
+ Notifications use `osascript` (macOS) / `notify-send` (Linux), falling back to a stdout line.
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAU5C"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5C"}
package/dist/cli.js CHANGED
@@ -5,6 +5,10 @@ export async function runCli() {
5
5
  const { runWizard } = await import("./wizard/index.js");
6
6
  await runWizard();
7
7
  }
8
+ else if (command === "watch") {
9
+ const { runWatch } = await import("./watch/index.js");
10
+ await runWatch(process.argv.slice(3));
11
+ }
8
12
  else {
9
13
  // Default: start MCP server (existing behavior)
10
14
  await import("./index.js");
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC9B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;SAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC9B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { MessageRecord } from "../types.js";
2
+ /** The peer this watcher notifies for. `label` is the peer's optional display name. */
3
+ export interface WatchTarget {
4
+ peerId: string;
5
+ label?: string;
6
+ }
7
+ /**
8
+ * True when `msg` is a directed message the watched peer should be notified about.
9
+ * Excludes the peer's own messages and auto-relay echoes. Matches either a
10
+ * strict-directed toPeerId or an obey-tag `[→ peerId]` / `[→ label]` in the body.
11
+ */
12
+ export declare function isDirectedTo(msg: MessageRecord, target: WatchTarget): boolean;
13
+ /**
14
+ * From a poll batch, return the directed messages not yet notified, adding each
15
+ * returned id to `seen`. Only directed ids are tracked, so `seen` stays bounded.
16
+ */
17
+ export declare function selectFreshDirected(messages: MessageRecord[], target: WatchTarget, seen: Set<string>): MessageRecord[];
18
+ //# sourceMappingURL=filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/watch/filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAO7E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EAAE,EACzB,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAChB,aAAa,EAAE,CASjB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * True when `msg` is a directed message the watched peer should be notified about.
3
+ * Excludes the peer's own messages and auto-relay echoes. Matches either a
4
+ * strict-directed toPeerId or an obey-tag `[→ peerId]` / `[→ label]` in the body.
5
+ */
6
+ export function isDirectedTo(msg, target) {
7
+ if (msg.fromPeerId === target.peerId)
8
+ return false;
9
+ if (msg.isRelayEcho)
10
+ return false;
11
+ if (msg.toPeerId === target.peerId)
12
+ return true;
13
+ const tags = [`[→ ${target.peerId}]`];
14
+ if (target.label)
15
+ tags.push(`[→ ${target.label}]`);
16
+ return tags.some((t) => msg.message.includes(t));
17
+ }
18
+ /**
19
+ * From a poll batch, return the directed messages not yet notified, adding each
20
+ * returned id to `seen`. Only directed ids are tracked, so `seen` stays bounded.
21
+ */
22
+ export function selectFreshDirected(messages, target, seen) {
23
+ const fresh = [];
24
+ for (const m of messages) {
25
+ if (!isDirectedTo(m, target))
26
+ continue;
27
+ if (seen.has(m.id))
28
+ continue;
29
+ seen.add(m.id);
30
+ fresh.push(m);
31
+ }
32
+ return fresh;
33
+ }
34
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/watch/filter.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAkB,EAAE,MAAmB;IAClE,IAAI,GAAG,CAAC,UAAU,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,GAAG,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;IACnD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAyB,EACzB,MAAmB,EACnB,IAAiB;IAEjB,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC;YAAE,SAAS;QACvC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { type PersistedCredentials } from "../cloud/credential-store.js";
2
+ import { pollOnce } from "./poll-client.js";
3
+ import { notify as notifyOs } from "./notifier.js";
4
+ import { type WatchTarget } from "./filter.js";
5
+ export interface WatchConfig {
6
+ peerId: string;
7
+ label?: string;
8
+ intervalSec: number;
9
+ cwd?: string;
10
+ }
11
+ export interface WatchState {
12
+ lastMessageId?: string;
13
+ seen: Set<string>;
14
+ }
15
+ export interface WatchContext {
16
+ creds: PersistedCredentials;
17
+ target: WatchTarget;
18
+ poll: typeof pollOnce;
19
+ notify: typeof notifyOs;
20
+ }
21
+ /** Parse `watch` flags. Throws if --peer is missing. */
22
+ export declare function parseWatchArgs(argv: string[]): WatchConfig;
23
+ /** One poll → notify cycle. Advances state; never re-notifies a seen message. */
24
+ export declare function runTick(state: WatchState, ctx: WatchContext): Promise<void>;
25
+ /**
26
+ * Entry point for `cogent-bridge watch`. Loads creds, primes state (seeds `seen`
27
+ * from the current backlog WITHOUT notifying), then loops with exponential backoff
28
+ * on error. Runs until SIGINT/SIGTERM.
29
+ */
30
+ export declare function runWatch(argv: string[]): Promise<void>;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watch/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,oBAAoB,CAAC;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,OAAO,QAAQ,CAAC;IACtB,MAAM,EAAE,OAAO,QAAQ,CAAC;CACzB;AAED,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CAY1D;AAMD,iFAAiF;AACjF,wBAAsB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAejF;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C5D"}
@@ -0,0 +1,91 @@
1
+ import { loadCredentials } from "../cloud/credential-store.js";
2
+ import { pollOnce } from "./poll-client.js";
3
+ import { notify as notifyOs } from "./notifier.js";
4
+ import { selectFreshDirected } from "./filter.js";
5
+ /** Parse `watch` flags. Throws if --peer is missing. */
6
+ export function parseWatchArgs(argv) {
7
+ const get = (flag) => {
8
+ const i = argv.indexOf(flag);
9
+ return i >= 0 && i + 1 < argv.length ? argv[i + 1] : undefined;
10
+ };
11
+ const peerId = get("--peer");
12
+ if (!peerId)
13
+ throw new Error("watch: --peer <peerId> is required");
14
+ const label = get("--label");
15
+ const cwd = get("--cwd");
16
+ const intervalRaw = Number(get("--interval"));
17
+ const intervalSec = Number.isFinite(intervalRaw) && intervalRaw > 0 ? intervalRaw : 15;
18
+ return { peerId, ...(label ? { label } : {}), ...(cwd ? { cwd } : {}), intervalSec };
19
+ }
20
+ function snippet(s) {
21
+ return s.replace(/\s+/g, " ").trim().slice(0, 140);
22
+ }
23
+ /** One poll → notify cycle. Advances state; never re-notifies a seen message. */
24
+ export async function runTick(state, ctx) {
25
+ const { messages } = await ctx.poll({
26
+ endpoint: ctx.creds.endpoint,
27
+ sessionId: ctx.creds.sessionId,
28
+ peerId: ctx.target.peerId,
29
+ token: ctx.creds.token,
30
+ lastMessageId: state.lastMessageId,
31
+ });
32
+ const fresh = selectFreshDirected(messages, ctx.target, state.seen);
33
+ for (const m of fresh) {
34
+ await ctx.notify(`Cogent: ${m.fromPeerId} → ${ctx.target.peerId}`, snippet(m.message));
35
+ }
36
+ if (messages.length > 0) {
37
+ state.lastMessageId = messages[messages.length - 1].id;
38
+ }
39
+ }
40
+ /**
41
+ * Entry point for `cogent-bridge watch`. Loads creds, primes state (seeds `seen`
42
+ * from the current backlog WITHOUT notifying), then loops with exponential backoff
43
+ * on error. Runs until SIGINT/SIGTERM.
44
+ */
45
+ export async function runWatch(argv) {
46
+ const cfg = parseWatchArgs(argv);
47
+ // Credentials resolve per-cwd via process.cwd() or COGENT_CREDENTIALS_FILE. `--cwd` is
48
+ // informational in v1 — run the watcher from the directory you registered in (documented in README).
49
+ const creds = await loadCredentials();
50
+ if (!creds) {
51
+ process.stderr.write("watch: no saved credentials for this directory. Join the channel first " +
52
+ "(/cogent:register), or run watch from the same cwd, or set COGENT_CREDENTIALS_FILE.\n");
53
+ process.exit(1);
54
+ return;
55
+ }
56
+ const target = { peerId: cfg.peerId, ...(cfg.label ? { label: cfg.label } : {}) };
57
+ const ctx = { creds, target, poll: pollOnce, notify: notifyOs };
58
+ const state = { seen: new Set() };
59
+ // Prime: seed lastMessageId + seen from the backlog without notifying.
60
+ try {
61
+ const primed = await pollOnce({ endpoint: creds.endpoint, sessionId: creds.sessionId, peerId: cfg.peerId, token: creds.token });
62
+ selectFreshDirected(primed.messages, target, state.seen);
63
+ if (primed.messages.length > 0)
64
+ state.lastMessageId = primed.messages[primed.messages.length - 1].id;
65
+ }
66
+ catch (err) {
67
+ process.stderr.write(`watch: initial poll failed (${err.message}); will retry.\n`);
68
+ }
69
+ process.stdout.write(`watch: notifying for [→ ${cfg.peerId}] on ${creds.label ?? creds.sessionId} every ${cfg.intervalSec}s. Ctrl-C to stop.\n`);
70
+ let stopped = false;
71
+ const stop = () => { stopped = true; };
72
+ process.on("SIGINT", stop);
73
+ process.on("SIGTERM", stop);
74
+ let backoff = 0;
75
+ while (!stopped) {
76
+ const waitMs = (backoff > 0 ? Math.min(cfg.intervalSec * 2 ** backoff, 120) : cfg.intervalSec) * 1000;
77
+ await new Promise((r) => setTimeout(r, waitMs));
78
+ if (stopped)
79
+ break;
80
+ try {
81
+ await runTick(state, ctx);
82
+ backoff = 0;
83
+ }
84
+ catch (err) {
85
+ backoff = Math.min(backoff + 1, 6);
86
+ process.stderr.write(`watch: poll error (${err.message}); backing off.\n`);
87
+ }
88
+ }
89
+ process.stdout.write("watch: stopped.\n");
90
+ }
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watch/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export type CommandRunner = (cmd: string, args: string[]) => Promise<void>;
2
+ /**
3
+ * Build the platform-appropriate notifier command, or null on an unsupported OS
4
+ * (the caller then falls back to stdout). macOS uses `osascript`; Linux uses
5
+ * `notify-send`. Args are passed as an argv array (no shell), so only the
6
+ * AppleScript string literal needs sanitizing.
7
+ */
8
+ export declare function notifierCommand(platform: NodeJS.Platform, title: string, body: string): {
9
+ cmd: string;
10
+ args: string[];
11
+ } | null;
12
+ export interface NotifyOptions {
13
+ platform?: NodeJS.Platform;
14
+ runner?: CommandRunner;
15
+ }
16
+ /**
17
+ * Fire an OS notification. Never rejects: an unsupported platform or a failing
18
+ * command falls back to a stdout line (with a terminal bell), so the watch loop
19
+ * is never broken by a notifier problem.
20
+ */
21
+ export declare function notify(title: string, body: string, opts?: NotifyOptions): Promise<void>;
22
+ //# sourceMappingURL=notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/watch/notifier.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAW3E;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CASxC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAajG"}
@@ -0,0 +1,47 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const pexecFile = promisify(execFile);
4
+ const defaultRunner = async (cmd, args) => {
5
+ await pexecFile(cmd, args);
6
+ };
7
+ /** Strip characters that would break an AppleScript double-quoted string literal. */
8
+ function sanitize(s) {
9
+ return s.replace(/["\\]/g, "'").replace(/[\r\n]+/g, " ").slice(0, 200);
10
+ }
11
+ /**
12
+ * Build the platform-appropriate notifier command, or null on an unsupported OS
13
+ * (the caller then falls back to stdout). macOS uses `osascript`; Linux uses
14
+ * `notify-send`. Args are passed as an argv array (no shell), so only the
15
+ * AppleScript string literal needs sanitizing.
16
+ */
17
+ export function notifierCommand(platform, title, body) {
18
+ if (platform === "darwin") {
19
+ const script = `display notification "${sanitize(body)}" with title "${sanitize(title)}"`;
20
+ return { cmd: "osascript", args: ["-e", script] };
21
+ }
22
+ if (platform === "linux") {
23
+ return { cmd: "notify-send", args: [title, body] };
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Fire an OS notification. Never rejects: an unsupported platform or a failing
29
+ * command falls back to a stdout line (with a terminal bell), so the watch loop
30
+ * is never broken by a notifier problem.
31
+ */
32
+ export async function notify(title, body, opts = {}) {
33
+ const platform = opts.platform ?? process.platform;
34
+ const runner = opts.runner ?? defaultRunner;
35
+ const cmd = notifierCommand(platform, title, body);
36
+ if (!cmd) {
37
+ process.stdout.write(`\x07🔔 ${title}: ${body}\n`);
38
+ return;
39
+ }
40
+ try {
41
+ await runner(cmd.cmd, cmd.args);
42
+ }
43
+ catch {
44
+ process.stdout.write(`\x07🔔 ${title}: ${body}\n`);
45
+ }
46
+ }
47
+ //# sourceMappingURL=notifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.js","sourceRoot":"","sources":["../../src/watch/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAItC,MAAM,aAAa,GAAkB,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvD,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,qFAAqF;AACrF,SAAS,QAAQ,CAAC,CAAS;IACzB,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAyB,EACzB,KAAa,EACb,IAAY;IAEZ,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,yBAAyB,QAAQ,CAAC,IAAI,CAAC,iBAAiB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;QAC1F,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAOD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAa,EAAE,IAAY,EAAE,OAAsB,EAAE;IAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC;IAC5C,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { MessageRecord } from "../types.js";
2
+ export interface PollResult {
3
+ messages: MessageRecord[];
4
+ events: unknown[];
5
+ }
6
+ export interface PollOptions {
7
+ endpoint: string;
8
+ sessionId: string;
9
+ peerId: string;
10
+ token: string;
11
+ lastMessageId?: string;
12
+ fetchImpl?: typeof fetch;
13
+ }
14
+ /**
15
+ * One call to the relay's poll endpoint. Returns the peer-relevant messages since
16
+ * `lastMessageId` (all of them on the first, priming poll). Throws on a non-2xx
17
+ * response so the loop can back off; the caller owns retry/backoff.
18
+ */
19
+ export declare function pollOnce(opts: PollOptions): Promise<PollResult>;
20
+ //# sourceMappingURL=poll-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-client.d.ts","sourceRoot":"","sources":["../../src/watch/poll-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBrE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * One call to the relay's poll endpoint. Returns the peer-relevant messages since
3
+ * `lastMessageId` (all of them on the first, priming poll). Throws on a non-2xx
4
+ * response so the loop can back off; the caller owns retry/backoff.
5
+ */
6
+ export async function pollOnce(opts) {
7
+ const f = opts.fetchImpl ?? fetch;
8
+ const url = new URL(`${opts.endpoint.replace(/\/$/, "")}/api/sessions/${encodeURIComponent(opts.sessionId)}/poll`);
9
+ url.searchParams.set("peerId", opts.peerId);
10
+ if (opts.lastMessageId)
11
+ url.searchParams.set("lastMessageId", opts.lastMessageId);
12
+ const res = await f(url.toString(), {
13
+ method: "GET",
14
+ headers: { authorization: `Bearer ${opts.token}` },
15
+ // Bounded deadline so a stalled relay never pins the loop.
16
+ signal: AbortSignal.timeout(10_000),
17
+ });
18
+ if (!res.ok)
19
+ throw new Error(`poll failed: HTTP ${res.status}`);
20
+ const body = (await res.json());
21
+ return { messages: body.messages ?? [], events: body.events ?? [] };
22
+ }
23
+ //# sourceMappingURL=poll-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-client.js","sourceRoot":"","sources":["../../src/watch/poll-client.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAiB;IAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CACjB,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAC9F,CAAC;IACF,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAI,CAAC,aAAa;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAElF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QAClC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,EAAE;QAClD,2DAA2D;QAC3D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuD,CAAC;IACtF,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;AACtE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essentialai/cogent-bridge",
3
- "version": "3.5.6",
3
+ "version": "3.5.8",
4
4
  "description": "MCP server for inter-Claude-Code session communication bridge",
5
5
  "type": "module",
6
6
  "workspaces": [
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.eaisdevelopment/cogent",
4
4
  "title": "Cogent Bridge from Essential AI Solutions (essentialai.uk)",
5
5
  "description": "MCP bridge for inter-session communication between Claude Code instances",
6
- "version": "3.5.6",
6
+ "version": "3.5.8",
7
7
  "repository": {
8
8
  "url": "https://github.com/eaisdevelopment/cogent",
9
9
  "source": "github"
@@ -12,7 +12,7 @@
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@essentialai/cogent-bridge",
15
- "version": "3.5.6",
15
+ "version": "3.5.8",
16
16
  "runtimeHint": "npx",
17
17
  "transport": {
18
18
  "type": "stdio"