qiksy-mcp 1.63.0 → 1.64.0
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/package.json +1 -1
- package/server.mjs +48 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qiksy-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.64.0",
|
|
4
4
|
"description": "Browser MCP server for the Chrome tab you already have open — your session, your logins. Gives Claude Code, Cursor, Codex and VS Code the live page: findings, forms, failed requests with server bodies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
package/server.mjs
CHANGED
|
@@ -1292,6 +1292,11 @@ function onConnection(ws, req) {
|
|
|
1292
1292
|
// answer back under the follower's own id. Ours is a fresh randomUUID, so the two
|
|
1293
1293
|
// id spaces cannot collide.
|
|
1294
1294
|
if (msg.t === 'req' && isFollower) {
|
|
1295
|
+
/* A REAL full stop from a sibling window or from `npx qiksy-mcp done` — the Stop hook's
|
|
1296
|
+
route into this process. Our OWN estimate travels the same way when a follower guesses,
|
|
1297
|
+
which is why the flag it carries is checked here: a guess must not teach the hub that
|
|
1298
|
+
this setup reports its endings. */
|
|
1299
|
+
if (msg.tool === 'qa_done' && !msg.args?.guessed) noteRealDone();
|
|
1295
1300
|
callExtension(msg.tool, msg.args, msg.timeoutMs)
|
|
1296
1301
|
.then((result) => safeSend(ws, { t: 'res', id: msg.id, ok: true, result }))
|
|
1297
1302
|
.catch((err) => safeSend(ws, { t: 'res', id: msg.id, ok: false, error: err?.message || String(err) }));
|
|
@@ -1315,51 +1320,69 @@ function onConnection(ws, req) {
|
|
|
1315
1320
|
let uiLanguage = '';
|
|
1316
1321
|
|
|
1317
1322
|
/**
|
|
1318
|
-
* THE END OF A RUN,
|
|
1323
|
+
* GUESSING THE END OF A RUN — OFF BY DEFAULT SINCE 1.64.0, AND THE REASON IS WORTH KEEPING.
|
|
1319
1324
|
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1323
|
-
*
|
|
1324
|
-
* the state this exists to prevent (owner, 21.09.2026).
|
|
1325
|
+
* It was added on 21.09.2026 to cover clients that never call `qa_done`: the person updates,
|
|
1326
|
+
* everything is on, and they hear nothing. The guess is silence — no bridge call for a while means
|
|
1327
|
+
* the run is over — and it was defended as safe to be wrong, because a later call cancels the
|
|
1328
|
+
* pulse and the extension refuses to sound twice in half a minute.
|
|
1325
1329
|
*
|
|
1326
|
-
*
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1330
|
+
* THAT DEFENCE ONLY COVERED THE PULSE. A sound cannot be cancelled after it has been heard, and
|
|
1331
|
+
* the silence it keys on is not silence at all: an agent that walked away from the browser to read
|
|
1332
|
+
* files, edit code or think makes no bridge call for MINUTES while working flat out. So the signal
|
|
1333
|
+
* arrived mid-run, and again on the next lull, and again — which is exactly what the owner
|
|
1334
|
+
* reported hearing (28.09.2026: «звук отрабатывает, а агент продолжает маслать… QADone несколько
|
|
1335
|
+
* раз отрабатывает, то есть он не по окончанию»). His ruling covers the mark as well as the
|
|
1336
|
+
* sound: both announce the END of a task, so neither may fire on a guess.
|
|
1329
1337
|
*
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
+
* A signal that is right most of the time is worse than no signal, because it is learned as noise
|
|
1339
|
+
* and then switched off — taking the true ones with it.
|
|
1340
|
+
*
|
|
1341
|
+
* WHAT REPLACES IT IS NOT NOTHING. Two routes report a real full stop, and `connect` writes the
|
|
1342
|
+
* second one into the client's settings, so an ordinary install has it:
|
|
1343
|
+
* · the agent calling `qa_done` itself, which every client is told about at `initialize`;
|
|
1344
|
+
* · a Stop hook running `npx qiksy-mcp done`, which fires whether or not the model remembered.
|
|
1345
|
+
* The guess survives as an opt-in for a client that has neither: `QIKSY_MCP_DONE_AFTER=<seconds>`.
|
|
1338
1346
|
*/
|
|
1339
1347
|
const DONE_AFTER_MS = (() => {
|
|
1340
1348
|
const raw = process.env.QIKSY_MCP_DONE_AFTER;
|
|
1341
|
-
if (raw === '0' || /^(off|no|false)$/i.test(raw || '')) return 0;
|
|
1342
1349
|
const n = Number(raw);
|
|
1343
1350
|
/* Below twenty seconds this would fire inside an ordinary pause for thought, so a smaller
|
|
1344
|
-
number is
|
|
1345
|
-
return Number.isFinite(n) && n >= 20 ? n * 1000 :
|
|
1351
|
+
number is refused rather than honoured. Anything else, including nothing at all, is off. */
|
|
1352
|
+
return Number.isFinite(n) && n >= 20 ? n * 1000 : 0;
|
|
1346
1353
|
})();
|
|
1347
1354
|
let doneTimer = null;
|
|
1348
1355
|
let workSinceDone = false;
|
|
1356
|
+
/**
|
|
1357
|
+
* A SETUP THAT REPORTS ITS OWN FULL STOP NEEDS NO GUESS, and the guess is the part that can be
|
|
1358
|
+
* wrong. One real `qa_done` proves this client has a working route, so the estimate stands down
|
|
1359
|
+
* for the rest of the process — nobody has to notice that they opted into both.
|
|
1360
|
+
*/
|
|
1361
|
+
let realDoneSeen = false;
|
|
1362
|
+
|
|
1363
|
+
/** Called where a REAL full stop enters this process, and nowhere else — never from `fireDone`. */
|
|
1364
|
+
function noteRealDone() {
|
|
1365
|
+
if (realDoneSeen) return;
|
|
1366
|
+
realDoneSeen = true;
|
|
1367
|
+
if (doneTimer) clearTimeout(doneTimer);
|
|
1368
|
+
doneTimer = null;
|
|
1369
|
+
workSinceDone = false;
|
|
1370
|
+
if (DONE_AFTER_MS) log('▸ this setup announces the end of a run itself — the quiet-timer guess is off from here on');
|
|
1371
|
+
}
|
|
1349
1372
|
|
|
1350
1373
|
function fireDone() {
|
|
1351
1374
|
doneTimer = null;
|
|
1352
|
-
if (!workSinceDone) return;
|
|
1375
|
+
if (!workSinceDone || realDoneSeen) return;
|
|
1353
1376
|
workSinceDone = false;
|
|
1354
1377
|
log(`▸ quiet for ${Math.round(DONE_AFTER_MS / 1000)}s after a run — saying it has finished`);
|
|
1355
1378
|
/* Failure here is nothing: the browser may have gone, the tab may have closed. The pulse is a
|
|
1356
1379
|
courtesy, and a courtesy that throws is worse than one that does not happen. */
|
|
1357
|
-
callExtension('qa_done', { verdict: 'ok' }, 8_000).catch(() => {});
|
|
1380
|
+
callExtension('qa_done', { verdict: 'ok', guessed: true }, 8_000).catch(() => {});
|
|
1358
1381
|
}
|
|
1359
1382
|
|
|
1360
1383
|
/** Called when a call FINISHES, not when it starts: a slow verb must not look like silence. */
|
|
1361
1384
|
function armDone(tool) {
|
|
1362
|
-
if (!DONE_AFTER_MS) return;
|
|
1385
|
+
if (!DONE_AFTER_MS || realDoneSeen) return;
|
|
1363
1386
|
if (doneTimer) clearTimeout(doneTimer);
|
|
1364
1387
|
doneTimer = null;
|
|
1365
1388
|
if (tool === 'qa_done') {
|
|
@@ -7147,6 +7170,8 @@ server.registerTool(
|
|
|
7147
7170
|
},
|
|
7148
7171
|
async ({ tabId, verdict }) => {
|
|
7149
7172
|
try {
|
|
7173
|
+
/* The agent said it itself — the one route that needs no guessing behind it. */
|
|
7174
|
+
noteRealDone();
|
|
7150
7175
|
return asText(await callExtension('qa_done', { tabId, verdict }, 15_000));
|
|
7151
7176
|
} catch (e) {
|
|
7152
7177
|
/* The verb lives in the EXTENSION, which updates on the Web Store's clock while this server
|