gm-skill 2.0.1415 → 2.0.1417
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/bin/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.561
|
package/bin/plugkit.wasm.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
458c7c843ded7aafe3f9a0eacf30f2b8df0974e86c8bd7959a643d06abd36b3f plugkit.wasm
|
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1417",
|
|
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": {
|
|
@@ -8,6 +8,11 @@ import { spawn as _rawSpawn, spawnSync as _rawSpawnSync } from 'child_process';
|
|
|
8
8
|
import net from 'net';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
|
|
11
|
+
// Set by the spool watcher's writeStatus closure once it is live. Lets long synchronous verbs
|
|
12
|
+
// (browser/chromium spawn, long exec) stamp a busy_until window into .status.json before the
|
|
13
|
+
// blocking call, so a liveness probe reads "busy" not "dead" while the event loop is blocked.
|
|
14
|
+
let _writeStatusBusy = () => {};
|
|
15
|
+
|
|
11
16
|
function spawnSync(cmd, args, opts) {
|
|
12
17
|
return _rawSpawnSync(cmd, args, { windowsHide: true, ...(opts || {}) });
|
|
13
18
|
}
|
|
@@ -725,6 +730,9 @@ function runBrowserRunner(pw, args, timeoutMs) {
|
|
|
725
730
|
const useShell = !!pw.shell;
|
|
726
731
|
const spawnCmd = useShell && /\s/.test(pw.cmd) ? `"${pw.cmd}"` : pw.cmd;
|
|
727
732
|
const spawnArgs = useShell ? allArgs.map(a => /[\s"]/.test(String(a)) ? `"${String(a).replace(/"/g, '\\"')}"` : a) : allArgs;
|
|
733
|
+
// Stamp a busy window before the synchronous spawn so the blocked event loop's stale heartbeat
|
|
734
|
+
// is not misread as a dead watcher. Pad past the spawn timeout for teardown.
|
|
735
|
+
_writeStatusBusy((timeoutMs || 30000) + 5000);
|
|
728
736
|
return spawnSync(spawnCmd, spawnArgs, {
|
|
729
737
|
encoding: 'utf-8',
|
|
730
738
|
timeout: timeoutMs,
|
|
@@ -2774,15 +2782,16 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2774
2782
|
}
|
|
2775
2783
|
|
|
2776
2784
|
const STATUS_PATH = path.join(spoolDir, '.status.json');
|
|
2777
|
-
function writeStatus() {
|
|
2785
|
+
function writeStatus(busyMs) {
|
|
2778
2786
|
try {
|
|
2779
2787
|
const fileV = readFileVersionOnly() || null;
|
|
2780
2788
|
const instV = _instanceVersionAtBoot || null;
|
|
2781
2789
|
const version = instV || fileV;
|
|
2782
2790
|
const drifted = !!(fileV && instV && fileV !== instV);
|
|
2783
|
-
|
|
2791
|
+
const now = Date.now();
|
|
2792
|
+
const rec = {
|
|
2784
2793
|
pid: process.pid,
|
|
2785
|
-
ts:
|
|
2794
|
+
ts: now,
|
|
2786
2795
|
version,
|
|
2787
2796
|
instance_version: instV,
|
|
2788
2797
|
file_version: fileV,
|
|
@@ -2791,10 +2800,19 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2791
2800
|
supervisor_pid: _supervisorPid,
|
|
2792
2801
|
wrapper_sha: _ownWrapperSha12 || null,
|
|
2793
2802
|
idle_limit_ms: IDLE_LIMIT_MS,
|
|
2794
|
-
}
|
|
2803
|
+
};
|
|
2804
|
+
// A synchronous verb (chromium spawn, long exec) blocks the event loop, so the 5s
|
|
2805
|
+
// heartbeat interval cannot fire for the duration. Without a hint, a liveness probe that
|
|
2806
|
+
// checks ts-within-15s reads the busy watcher as dead and may kill/respawn it mid-verb.
|
|
2807
|
+
// busy_until tells probes "alive but synchronously busy until this epoch ms" — read it
|
|
2808
|
+
// alongside ts: a stale ts whose busy_until is still in the future is a busy watcher, not
|
|
2809
|
+
// a dead one. The pre-verb writeStatus(busyMs) stamps it before the blocking call.
|
|
2810
|
+
if (busyMs && busyMs > 0) rec.busy_until = now + busyMs;
|
|
2811
|
+
fs.writeFileSync(STATUS_PATH, JSON.stringify(rec));
|
|
2795
2812
|
} catch (_) {}
|
|
2796
2813
|
}
|
|
2797
|
-
|
|
2814
|
+
_writeStatusBusy = (ms) => { try { writeStatus(ms); } catch (_) {} };
|
|
2815
|
+
setInterval(() => writeStatus(), 5000);
|
|
2798
2816
|
writeStatus();
|
|
2799
2817
|
|
|
2800
2818
|
const TURN_SUMMARY_PATH = path.join(spoolDir, '.turn-summary.json');
|
package/gm.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1417",
|
|
4
4
|
"description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
"publishConfig": {
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
|
-
"plugkitVersion": "0.1.
|
|
20
|
+
"plugkitVersion": "0.1.561"
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1417",
|
|
4
4
|
"description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|
package/skills/gm-skill/SKILL.md
CHANGED
|
@@ -32,7 +32,7 @@ cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-
|
|
|
32
32
|
|
|
33
33
|
`.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`. When age exceeds the threshold, your next non-orienting verb will be gated, dispatch `instruction` first. When `update_available` is non-null, the watcher has detected drift: kill the watcher then re-run the boot command below, which calls `ensureReady` to download the latest wasm and refresh the installed wrapper, then starts a fresh watcher loaded with the new code. The one-line sequence: `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool > /dev/null 2>&1 &`. Then wait 8s and re-read `.status.json` to confirm `version` matches `update_available.latest`. `deviations_30m` is the rolling count of `deviation.*` events from hook+plugkit logs in the past 30 min, read it instead of running gmsniff at session start; non-zero indicates active drift worth investigating before continuing. `watcher_uptime_ms` shows how long the current watcher process has been alive; combined with `watcher_version` and `update_available` it tells you whether the watcher's loaded code is recent, a multi-hour uptime with no `update_available` means you're on latest, an uptime of seconds means a fresh boot just happened.
|
|
34
34
|
|
|
35
|
-
Compare `.status.json` `ts` field to the printed epoch ms. If the gap is >15000, the watcher is dead, boot it:
|
|
35
|
+
Compare `.status.json` `ts` field to the printed epoch ms. If the gap is >15000, the watcher is dead, boot it. One exception: if `.status.json` carries `busy_until` and that value is still in the future (greater than the printed epoch ms), the watcher is alive but synchronously busy with a long verb (a browser/chromium spawn blocks the heartbeat for ~15-18s), wait for it, do not boot a second watcher. A stale `ts` with a future `busy_until` is busy, not dead.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
bun x gm-plugkit@latest spool > /dev/null 2>&1 &
|
|
@@ -44,7 +44,7 @@ bun x gm-plugkit@latest spool > /dev/null 2>&1 &
|
|
|
44
44
|
|
|
45
45
|
Never poll the spool dir with `sleep && ls` or `Start-Sleep && Test-Path`, plugkit is synchronous from your view; if the response is not there, the watcher is dead (re-check `.status.json` mtime) or the verb is slow (check `.gm/exec-spool/.watcher.log`), not "still processing."
|
|
46
46
|
|
|
47
|
-
**Dead-watcher recovery is mandatory, never abandon the dispatch.** If two consecutive re-Reads return "file does not exist" AND `.status.json` ts is stale (>15s gap from current epoch), the watcher is dead. Your next call is `bun x gm-plugkit@latest spool` to boot a fresh watcher (the wrapper has self-respawn paths now, one boot deploys every queued fix to disk). Then re-dispatch the original verb. Do NOT reach for an alternative tool, puppeteer-core, agent-browser, WebFetch, raw `chrome.exe`, none of these substitute for the `browser` verb. Reaching outside plugkit when the spool surface is reachable orphans state the next session cannot reap, bypasses paper §23 witness gates, and ages the project's discipline. The recovery is always: notice dead → boot → re-dispatch. The full chain from spool-write to disk-Read-success is the only admissible loop; any short-circuit produces unreconcilable state.
|
|
47
|
+
**Dead-watcher recovery is mandatory, never abandon the dispatch.** If two consecutive re-Reads return "file does not exist" AND `.status.json` ts is stale (>15s gap from current epoch) AND `busy_until` is absent or in the past, the watcher is dead. (A future `busy_until` means a long synchronous verb is running, the response will land when it finishes; wait, do not boot.) Your next call is `bun x gm-plugkit@latest spool` to boot a fresh watcher (the wrapper has self-respawn paths now, one boot deploys every queued fix to disk). Then re-dispatch the original verb. Do NOT reach for an alternative tool, puppeteer-core, agent-browser, WebFetch, raw `chrome.exe`, none of these substitute for the `browser` verb. Reaching outside plugkit when the spool surface is reachable orphans state the next session cannot reap, bypasses paper §23 witness gates, and ages the project's discipline. The recovery is always: notice dead → boot → re-dispatch. The full chain from spool-write to disk-Read-success is the only admissible loop; any short-circuit produces unreconcilable state.
|
|
48
48
|
|
|
49
49
|
When writing the spool input from PowerShell, pass `-Encoding utf8` (or use `[System.IO.File]::WriteAllText($path, $body)` which defaults to UTF-8 no-BOM). PowerShell 5.1's default `Out-File` / `Set-Content` write UTF-16 LE with BOM, which the watcher detects and re-decodes (`spool.body-encoding-recoded` event in gmsniff), but the deviation is a fingerprint of an instruction you missed. Use `bash -c "echo -n '...' > ..."` or `Write` tool instead when the body is structured JSON.
|
|
50
50
|
|