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-
|
|
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
|
@@ -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
|
@@ -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-
|
|
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;
|