gm-plugkit 2.0.1635 → 2.0.1637

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/SKILL.md CHANGED
@@ -34,7 +34,7 @@ Every turn: dispatch `instruction`, read it, follow the imperative, dispatch the
34
34
  cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
35
35
  ```
36
36
 
37
- `.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: eager-upgrade with `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool` (the spool call blocks until serving), then read `.status.json` to confirm `version`. `deviations_30m` non-zero indicates active drift to investigate before continuing.
37
+ `.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: the watcher auto-updates itself when idle (cache-busted self-respawn to latest), so it usually clears on its own within a few minutes -- keep working. To land it immediately, just re-run the idempotent `bun x gm-plugkit@latest spool` (blocks until serving); only add `--kill-stale-watchers` first if it stays stuck across several turns. `PLUGKIT_NO_AUTO_UPDATE=1` pins the version. `deviations_30m` non-zero indicates active drift to investigate before continuing.
38
38
 
39
39
  Compare `.status.json` `ts` to the printed epoch: gap > 15000 = dead, boot it. Exception: a future `busy_until` means a long verb (browser/chromium spawn blocks the heartbeat ~15-18s) -- wait, do not boot a second watcher.
40
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1635",
3
+ "version": "2.0.1637",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -308,7 +308,7 @@ function injectUpdateWarning(parsed) {
308
308
  if (!upd || !upd.installed || !upd.latest || upd.installed === upd.latest) return;
309
309
  const target = (parsed.data && typeof parsed.data === 'object') ? parsed.data : parsed;
310
310
  target.update_available = { installed: upd.installed, latest: upd.latest, update_url: upd.update_url || null };
311
- target.update_warning = `STALE RUNTIME: running plugkit ${upd.installed} but ${upd.latest} is published and not yet running. Restart onto the new version now: bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool. This warning repeats every turn until the running version catches up.`;
311
+ target.update_warning = `STALE RUNTIME: running plugkit ${upd.installed} but ${upd.latest} is published. The watcher auto-updates when idle (cache-busted self-respawn to latest), so this usually clears on its own within a few minutes; just keep working. If it persists, re-run the idempotent boot to land latest now: bun x gm-plugkit@latest spool (add --kill-stale-watchers first only if it stays stuck). Set PLUGKIT_NO_AUTO_UPDATE=1 to pin. This warning repeats until the running version catches up.`;
312
312
  }
313
313
 
314
314
  function mergeAutoRecallIntoInstructionResponse(resultStr, autoRecall) {
@@ -1457,8 +1457,9 @@ function createWasiShim(instanceRef) {
1457
1457
  const dv = new DataView(buf);
1458
1458
  const chunks = [];
1459
1459
  let total = 0;
1460
+ const iovsBase = iovs_ptr >>> 0; // >>>0: high-bit iovs pointer is negative in JS -> getUint32 would throw
1460
1461
  for (let i = 0; i < iovs_len; i++) {
1461
- const base = iovs_ptr + i * 8;
1462
+ const base = iovsBase + i * 8;
1462
1463
  const ptr = dv.getUint32(base, true);
1463
1464
  const len = dv.getUint32(base + 4, true);
1464
1465
  if (len > 0 && ptr + len <= buf.byteLength) {
@@ -1480,7 +1481,7 @@ function createWasiShim(instanceRef) {
1480
1481
  },
1481
1482
  random_get: (buf_ptr, buf_len) => {
1482
1483
  try {
1483
- crypto.randomFillSync(new Uint8Array(getMemory(), buf_ptr, buf_len));
1484
+ crypto.randomFillSync(new Uint8Array(getMemory(), buf_ptr >>> 0, buf_len >>> 0)); // >>>0: high-bit ptr is negative in JS
1484
1485
  return 0;
1485
1486
  } catch (e) {
1486
1487
  return 28;
@@ -1489,7 +1490,7 @@ function createWasiShim(instanceRef) {
1489
1490
  clock_time_get: (clock_id, precision, time_ptr) => {
1490
1491
  try {
1491
1492
  const ns = BigInt(Date.now()) * 1000000n;
1492
- new DataView(getMemory()).setBigUint64(time_ptr, ns, true);
1493
+ new DataView(getMemory()).setBigUint64(time_ptr >>> 0, ns, true); // >>>0: high-bit ptr is negative in JS
1493
1494
  return 0;
1494
1495
  } catch (e) {
1495
1496
  return 28;
@@ -1548,8 +1549,15 @@ function decodeWasmResult(instance, result, where) {
1548
1549
 
1549
1550
  function writeWasmInput(instance, bytes, where) {
1550
1551
  if (bytes.length === 0) return 0;
1551
- const ptr = instance.exports.plugkit_alloc(bytes.length);
1552
+ // COERCE the alloc pointer to UNSIGNED 32-bit: a wasm i32 return with the high bit set (a pointer
1553
+ // > 0x7fffffff, which occurs once the linear memory grows past ~2GB in a long session) arrives in JS
1554
+ // as a NEGATIVE number, and new Uint8Array(buffer, negativePtr, len) throws the raw V8 "Start offset
1555
+ // <neg> is outside the bounds of the buffer" -- the deterministic long-session corruption that blocked
1556
+ // dispatches. >>>0 reinterprets the i32 as the true unsigned offset. Guard the range too so a genuinely
1557
+ // bad (ptr,len) raises the clean wasm-memory-read-out-of-bounds error instead of a raw typed-array throw.
1558
+ const ptr = instance.exports.plugkit_alloc(bytes.length) >>> 0;
1552
1559
  if (ptr === 0) throw new Error(`wasm-alloc-failed at ${where}: plugkit_alloc returned 0 (wasm OOM)`);
1560
+ guardWasmRange(instance.exports.memory.buffer, ptr, bytes.length, `${where}:writeWasmInput`);
1553
1561
  new Uint8Array(instance.exports.memory.buffer, ptr, bytes.length).set(bytes); // fresh buffer post-alloc
1554
1562
  return ptr;
1555
1563
  }
@@ -1571,8 +1579,11 @@ function readWasmStr(instance, ptr, len) {
1571
1579
 
1572
1580
  function writeWasmBytes(instance, bytes) {
1573
1581
  if (bytes.length === 0) return 0n;
1574
- const ptr = instance.exports.plugkit_alloc(bytes.length);
1582
+ // >>>0: same signed-pointer fix as writeWasmInput -- a high-bit alloc pointer is negative in JS and
1583
+ // throws "Start offset <neg> is outside the bounds" on the Uint8Array write below.
1584
+ const ptr = instance.exports.plugkit_alloc(bytes.length) >>> 0;
1575
1585
  if (ptr === 0) return 0n;
1586
+ guardWasmRange(instance.exports.memory.buffer, ptr, bytes.length, 'writeWasmBytes');
1576
1587
  new Uint8Array(instance.exports.memory.buffer, ptr, bytes.length).set(bytes);
1577
1588
  return (BigInt(ptr) & 0xffffffffn) | (BigInt(bytes.length) << 32n);
1578
1589
  }
@@ -2256,7 +2267,7 @@ function makeHostFunctions(instanceRef) {
2256
2267
  host_random_fill: (ptr, len) => {
2257
2268
  try {
2258
2269
  const buf = instanceRef.value.exports.memory.buffer;
2259
- crypto.randomFillSync(new Uint8Array(buf, ptr, len));
2270
+ crypto.randomFillSync(new Uint8Array(buf, ptr >>> 0, len >>> 0)); // >>>0: high-bit ptr is negative in JS
2260
2271
  return 1;
2261
2272
  } catch (_) {
2262
2273
  return 0;
@@ -2904,6 +2915,10 @@ async function runSpoolWatcher(instance, spoolDir) {
2904
2915
  let _selfStaleProbeErrorLogged = false;
2905
2916
  function probeGmPlugkitSelfStale() {
2906
2917
  try {
2918
+ if (process.env.PLUGKIT_NO_AUTO_UPDATE === '1') return;
2919
+ const { sids: _ifSids } = (typeof inflightPids === 'function') ? inflightPids() : { sids: new Set() };
2920
+ if (_ifSids.size > 0) return;
2921
+ if ((Date.now() - lastActivityMs) < 30000) return;
2907
2922
  const ownPkgVersionFile = path.join(GM_TOOLS_ROOT, 'gm-plugkit.version');
2908
2923
  const ownPkgJsonFile = path.join(__dirname, 'package.json');
2909
2924
  let own = null;
@@ -2975,7 +2990,7 @@ async function runSpoolWatcher(instance, spoolDir) {
2975
2990
  try {
2976
2991
  const cp = _childProcess;
2977
2992
  const bunPath = process.env.GM_BUN_PATH || 'bun';
2978
- const bustCache = sameStaleAsBefore || cameFromSelfRespawn;
2993
+ const bustCache = true;
2979
2994
  if (bustCache) {
2980
2995
  try { cp.execFileSync(bunPath, ['pm', 'cache', 'rm'], { stdio: 'ignore', timeout: 30000, windowsHide: true }); } catch (_) {}
2981
2996
  try {
@@ -3002,7 +3017,7 @@ async function runSpoolWatcher(instance, spoolDir) {
3002
3017
  env: { ...process.env, PLUGKIT_BOOT_REASON: 'self-respawn-from-self-stale' },
3003
3018
  });
3004
3019
  child.unref();
3005
- try { logEvent('plugkit', 'gm-plugkit.self-stale-respawn', { running_version: own, latest_version: latest, cache_busted: bustCache, attempt: (respawnGuard.attempts || 0) + 1 }); } catch (_) {}
3020
+ try { logEvent('plugkit', 'update.auto-applying', { running_version: own, latest_version: latest, cache_busted: bustCache, attempt: (respawnGuard.attempts || 0) + 1, note: 'auto-update: cache-busted self-respawn to latest' }); } catch (_) {}
3006
3021
  try { fs.writeFileSync(path.join(spoolDir, '.shutdown-reason.json'), JSON.stringify({ reason: 'gm-plugkit-self-stale', ts: Date.now(), pid: process.pid, running_version: own, latest_version: latest })); } catch (_) {}
3007
3022
  const myPid = process.pid;
3008
3023
  const respawnDeadline = Date.now() + 90000;