kojee-mcp 0.7.5-staging.21 → 0.7.5-staging.22

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.
@@ -390,7 +390,7 @@ async function startProxy(config) {
390
390
  console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
391
391
  let server;
392
392
  const isHeadlessDaemon = !!process.env["KOJEE_HEADLESS"] || !!process.env["KOJEE_WEBHOOK_EXPECTED"];
393
- const { selectDelivery } = await import("./registry-MCJAVXJW.js");
393
+ const { selectDelivery } = await import("./registry-RR665OLS.js");
394
394
  const delivery = selectDelivery(adapter.runtime, {
395
395
  supportsChannels: adapter.supportsChannels,
396
396
  headless: isHeadlessDaemon,
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-EIAUW6KO.js";
5
5
  import {
6
6
  startProxy
7
- } from "./chunk-676WQSWZ.js";
7
+ } from "./chunk-5LUNLL56.js";
8
8
  import "./chunk-CCVRXJJ2.js";
9
9
  import {
10
10
  pairedConfigPath
@@ -57,6 +57,20 @@ async function handleRequest(req, res, opts) {
57
57
  if (req.method === "POST" && url.pathname === "/send") {
58
58
  return handleSend(req, res, opts);
59
59
  }
60
+ if (req.method === "POST" && url.pathname === "/presence") {
61
+ if (opts.controlToken !== void 0 && !bearerMatches(req.headers.authorization, opts.controlToken)) {
62
+ return json(res, 401, {
63
+ error: "unauthorized",
64
+ detail: "POST /presence is gated by the control token \u2014 send it as `Authorization: Bearer <token>`"
65
+ });
66
+ }
67
+ const state = url.searchParams.get("state") ?? "";
68
+ if (state !== "working" && state !== "idle") {
69
+ return json(res, 400, { error: "bad_state", detail: "state must be 'working' or 'idle'" });
70
+ }
71
+ firePresenceState(opts.liveliness, state);
72
+ return json(res, 200, { ok: true });
73
+ }
60
74
  return json(res, 404, { error: "not_found", detail: `${req.method} ${url.pathname}` });
61
75
  }
62
76
  var MAX_SEND_BODY_BYTES = 256 * 1024;
@@ -143,6 +157,17 @@ function fireLiveliness(liveliness, type) {
143
157
  } catch {
144
158
  }
145
159
  }
160
+ function firePresenceState(liveliness, state) {
161
+ if (!liveliness) return;
162
+ try {
163
+ if (state === "working") {
164
+ liveliness.onTurnStart();
165
+ } else {
166
+ liveliness.onTurnEnd();
167
+ }
168
+ } catch {
169
+ }
170
+ }
146
171
  function respondWithEvents(res, opts) {
147
172
  const entries = opts.queue.takeForHook();
148
173
  const events = entries.map((entry) => opts.adapter.formatTandemEvent(entry.event));
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  listTandemIds,
3
3
  startProxy
4
- } from "./chunk-676WQSWZ.js";
4
+ } from "./chunk-5LUNLL56.js";
5
5
  import "./chunk-CCVRXJJ2.js";
6
6
  import "./chunk-AGZ7ZGLY.js";
7
7
  import "./chunk-Z5LPNJQ6.js";
@@ -70,25 +70,45 @@ function formatChannelNotification(event) {
70
70
  // src/delivery/liveliness.ts
71
71
  function createPresenceProvider({
72
72
  post,
73
- debounceMs = 2e3
73
+ debounceMs = 2e3,
74
+ maxWorkingMs = 0
75
+ // OFF until a mid-turn `working` heartbeat re-arms it (see option doc)
74
76
  }) {
75
77
  let idleTimer = null;
78
+ let workingTimer = null;
76
79
  const clearIdleTimer = () => {
77
80
  if (idleTimer !== null) {
78
81
  clearTimeout(idleTimer);
79
82
  idleTimer = null;
80
83
  }
81
84
  };
85
+ const clearWorkingTimer = () => {
86
+ if (workingTimer !== null) {
87
+ clearTimeout(workingTimer);
88
+ workingTimer = null;
89
+ }
90
+ };
82
91
  const emit = (state) => {
83
92
  void post(state).catch(() => {
84
93
  });
85
94
  };
95
+ const armWorkingWatchdog = () => {
96
+ clearWorkingTimer();
97
+ if (maxWorkingMs > 0) {
98
+ workingTimer = setTimeout(() => {
99
+ workingTimer = null;
100
+ emit("idle");
101
+ }, maxWorkingMs);
102
+ }
103
+ };
86
104
  return {
87
105
  onTurnStart() {
88
106
  clearIdleTimer();
107
+ armWorkingWatchdog();
89
108
  emit("working");
90
109
  },
91
110
  onTurnEnd() {
111
+ clearWorkingTimer();
92
112
  clearIdleTimer();
93
113
  idleTimer = setTimeout(() => {
94
114
  idleTimer = null;
@@ -97,6 +117,7 @@ function createPresenceProvider({
97
117
  },
98
118
  stop() {
99
119
  clearIdleTimer();
120
+ clearWorkingTimer();
100
121
  }
101
122
  };
102
123
  }
@@ -115,7 +136,7 @@ function createClaudeCodeDelivery() {
115
136
  name: "claude-code",
116
137
  async start(ctx) {
117
138
  const { EventQueue } = await import("./event-queue-5YVJFR3E.js");
118
- const { startHookServer } = await import("./hook-server-CWBH6PFL.js");
139
+ const { startHookServer } = await import("./hook-server-EC7JEJXK.js");
119
140
  const {
120
141
  writeDiscoveryByKey,
121
142
  cleanupDiscoveryByKey,
@@ -184,7 +205,21 @@ function createClaudeCodeDelivery() {
184
205
  );
185
206
  }
186
207
  liveliness = createPresenceProvider({
187
- post: (state) => ctx.gateway.sendRest("POST", "/api/v2/presence", { state })
208
+ post: (state) => ctx.gateway.sendRest("POST", "/api/v2/presence", { state }),
209
+ // Max-working backstop (env-tunable). Only catches a lost turn-end
210
+ // signal (aborted turn with no Stop hook, crash, dropped `/presence
211
+ // idle`); the explicit signals are the fast path. DEFAULT OFF in the
212
+ // provider (a fixed window without a mid-turn re-arm false-idles long
213
+ // turns) — enabled once the supervisor emits `working` heartbeats. `0`
214
+ // disables. Unset/empty env → provider default; only a valid non-empty
215
+ // number overrides (so `KOJEE_PRESENCE_MAX_WORKING_MS=""` doesn't
216
+ // silently coerce to 0 via Number("")).
217
+ ...(() => {
218
+ const s = process.env.KOJEE_PRESENCE_MAX_WORKING_MS;
219
+ if (s === void 0 || s.trim() === "") return {};
220
+ const raw = Number(s);
221
+ return Number.isFinite(raw) && raw >= 0 && raw <= 2147483647 ? { maxWorkingMs: raw } : {};
222
+ })()
188
223
  });
189
224
  queue = new EventQueue();
190
225
  const localQueue = queue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kojee-mcp",
3
- "version": "0.7.5-staging.21",
3
+ "version": "0.7.5-staging.22",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {