gm-skill 2.0.1221 → 2.0.1223
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/README.md +1 -1
- package/bin/plugkit.version +1 -1
- package/bin/plugkit.wasm +0 -0
- package/bin/plugkit.wasm.sha256 +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +160 -14
- package/gm.json +2 -2
- package/lib/spool-dispatch.js +53 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ An earlier generation fanned out fifteen per-platform downstream repos (gm-cc, g
|
|
|
35
35
|
|
|
36
36
|
## Version
|
|
37
37
|
|
|
38
|
-
`2.0.
|
|
38
|
+
`2.0.1223` — auto-bumped from the canonical `gm` repo. Every push to `AnEntrypoint/gm` (or any cascading sibling crate) republishes this package.
|
|
39
39
|
|
|
40
40
|
## Source of truth
|
|
41
41
|
|
package/bin/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.449
|
package/bin/plugkit.wasm
CHANGED
|
Binary file
|
package/bin/plugkit.wasm.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0cac0a48e7346d2f75779c9972efe47c3f4ab38063a077660906a53028d2e70a plugkit.wasm
|
|
@@ -78,6 +78,17 @@ function isBrowserRunningFileLocal(rel) {
|
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function hashFileShortLocal(cwd, rel) {
|
|
82
|
+
try {
|
|
83
|
+
const fs = require('fs');
|
|
84
|
+
const path = require('path');
|
|
85
|
+
const crypto = require('crypto');
|
|
86
|
+
const abs = path.isAbsolute(rel) ? rel : path.join(cwd, rel);
|
|
87
|
+
const buf = fs.readFileSync(abs);
|
|
88
|
+
return crypto.createHash('sha256').update(buf).digest('hex').slice(0, 12);
|
|
89
|
+
} catch (_) { return ''; }
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
function recordBrowserEditLocal(cwd, filePath) {
|
|
82
93
|
try {
|
|
83
94
|
const fs = require('fs');
|
|
@@ -89,8 +100,11 @@ function recordBrowserEditLocal(cwd, filePath) {
|
|
|
89
100
|
fs.mkdirSync(path.dirname(editsFile), { recursive: true });
|
|
90
101
|
let list = [];
|
|
91
102
|
try { list = JSON.parse(fs.readFileSync(editsFile, 'utf8')); if (!Array.isArray(list)) list = []; } catch (_) {}
|
|
92
|
-
const
|
|
93
|
-
|
|
103
|
+
const relPath = rel.replace(/\\\\/g, '/');
|
|
104
|
+
const hash = hashFileShortLocal(cwd, relPath);
|
|
105
|
+
const idx = list.findIndex(e => e && e.file === relPath);
|
|
106
|
+
const entry = { file: relPath, ts: Date.now(), hash };
|
|
107
|
+
if (idx === -1) list.push(entry); else list[idx] = entry;
|
|
94
108
|
fs.writeFileSync(editsFile, JSON.stringify(list));
|
|
95
109
|
return true;
|
|
96
110
|
} catch (_) { return false; }
|
|
@@ -322,9 +336,24 @@ function dispatchAutoRecall(instance, queryPrompt) {
|
|
|
322
336
|
function tryAutoRecallForTurnEntry(instance, sess, cwd) {
|
|
323
337
|
try {
|
|
324
338
|
const prompt = readUserPromptForRecall(cwd);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
339
|
+
let emptyPromptFallback = false;
|
|
340
|
+
let effectivePrompt = prompt;
|
|
341
|
+
if (!prompt || !String(prompt).trim()) {
|
|
342
|
+
emptyPromptFallback = true;
|
|
343
|
+
const key = sess || '(no-session)';
|
|
344
|
+
const t = _turns.get(key);
|
|
345
|
+
const phase = (t && t.lastPhase) || 'PLAN';
|
|
346
|
+
const phaseQueryMap = {
|
|
347
|
+
PLAN: 'PLAN orient',
|
|
348
|
+
EXECUTE: 'EXECUTE work',
|
|
349
|
+
EMIT: 'EMIT closure',
|
|
350
|
+
VERIFY: 'VERIFY trajectory',
|
|
351
|
+
COMPLETE: 'COMPLETE residual',
|
|
352
|
+
};
|
|
353
|
+
effectivePrompt = phaseQueryMap[phase] || 'PLAN orient';
|
|
354
|
+
}
|
|
355
|
+
const primary = dispatchAutoRecall(instance, effectivePrompt);
|
|
356
|
+
const fallbackQuery = deriveFallbackQuery(effectivePrompt);
|
|
328
357
|
let fallback = null;
|
|
329
358
|
if (fallbackQuery && fallbackQuery !== (primary && primary.query)) {
|
|
330
359
|
fallback = dispatchAutoRecall(instance, fallbackQuery);
|
|
@@ -344,12 +373,16 @@ function tryAutoRecallForTurnEntry(instance, sess, cwd) {
|
|
|
344
373
|
if (primary && primary.query) queries.push(primary.query);
|
|
345
374
|
if (fallback && fallback.query && !queries.includes(fallback.query)) queries.push(fallback.query);
|
|
346
375
|
const payload = {
|
|
347
|
-
query: (primary && primary.query) || '',
|
|
376
|
+
query: (primary && primary.query) || effectivePrompt || '',
|
|
348
377
|
queries,
|
|
349
378
|
hits: merged.slice(0, 20),
|
|
350
379
|
fired_at: new Date().toISOString(),
|
|
351
380
|
turn_entry: true,
|
|
352
381
|
};
|
|
382
|
+
if (emptyPromptFallback) {
|
|
383
|
+
payload.fallback_reason = 'empty-prompt';
|
|
384
|
+
if (!payload.query) payload.query = effectivePrompt;
|
|
385
|
+
}
|
|
353
386
|
logEvent('plugkit', 'auto_recall.turn-entry', { sess, queries, count: merged.length });
|
|
354
387
|
return payload;
|
|
355
388
|
} catch (e) {
|
|
@@ -618,9 +651,48 @@ function ensureGitignored(cwd, entry) {
|
|
|
618
651
|
} catch (_) {}
|
|
619
652
|
}
|
|
620
653
|
|
|
654
|
+
function isProcessAliveSync(pid) {
|
|
655
|
+
if (!pid || typeof pid !== 'number' || pid <= 0) return false;
|
|
656
|
+
try {
|
|
657
|
+
process.kill(pid, 0);
|
|
658
|
+
return true;
|
|
659
|
+
} catch (e) {
|
|
660
|
+
return e && e.code === 'EPERM';
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function readSingletonLockPid(profileDir) {
|
|
665
|
+
const lock = path.join(profileDir, 'SingletonLock');
|
|
666
|
+
try {
|
|
667
|
+
let target;
|
|
668
|
+
try {
|
|
669
|
+
target = fs.readlinkSync(lock);
|
|
670
|
+
} catch (_) {
|
|
671
|
+
try { target = fs.readFileSync(lock, 'utf-8'); } catch (__) { return null; }
|
|
672
|
+
}
|
|
673
|
+
if (!target) return null;
|
|
674
|
+
const m = String(target).match(/-(\d+)\s*$/);
|
|
675
|
+
if (m) return parseInt(m[1], 10);
|
|
676
|
+
const m2 = String(target).match(/(\d+)/);
|
|
677
|
+
if (m2) return parseInt(m2[1], 10);
|
|
678
|
+
} catch (_) {}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
|
|
621
682
|
function isProfileLocked(profileDir) {
|
|
622
683
|
const lock = path.join(profileDir, 'SingletonLock');
|
|
623
|
-
|
|
684
|
+
if (!fs.existsSync(lock)) return false;
|
|
685
|
+
const holderPid = readSingletonLockPid(profileDir);
|
|
686
|
+
if (holderPid != null && !isProcessAliveSync(holderPid)) {
|
|
687
|
+
try { fs.unlinkSync(lock); } catch (_) {}
|
|
688
|
+
try { fs.unlinkSync(path.join(profileDir, 'SingletonCookie')); } catch (_) {}
|
|
689
|
+
try { fs.unlinkSync(path.join(profileDir, 'SingletonSocket')); } catch (_) {}
|
|
690
|
+
logEvent('bootstrap', 'browser-profile.lock-cleared', {
|
|
691
|
+
profileDir, dead_pid: holderPid,
|
|
692
|
+
});
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
return true;
|
|
624
696
|
}
|
|
625
697
|
|
|
626
698
|
function acquireProfileDir(cwd) {
|
|
@@ -636,6 +708,31 @@ function acquireProfileDir(cwd) {
|
|
|
636
708
|
return fallback;
|
|
637
709
|
}
|
|
638
710
|
|
|
711
|
+
function cleanDeadProfileFragments(cwd) {
|
|
712
|
+
try {
|
|
713
|
+
const gmDir = path.join(cwd, '.gm');
|
|
714
|
+
if (!fs.existsSync(gmDir)) return { cleaned: 0 };
|
|
715
|
+
let cleaned = 0;
|
|
716
|
+
for (const name of fs.readdirSync(gmDir)) {
|
|
717
|
+
const m = name.match(/^browser-profile-(\d+)$/);
|
|
718
|
+
if (!m) continue;
|
|
719
|
+
const pid = parseInt(m[1], 10);
|
|
720
|
+
if (!isProcessAliveSync(pid)) {
|
|
721
|
+
try {
|
|
722
|
+
fs.rmSync(path.join(gmDir, name), { recursive: true, force: true });
|
|
723
|
+
cleaned++;
|
|
724
|
+
} catch (_) {}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (cleaned > 0) {
|
|
728
|
+
logEvent('bootstrap', 'browser-profile.hygiene', { cwd, cleaned });
|
|
729
|
+
}
|
|
730
|
+
return { cleaned };
|
|
731
|
+
} catch (_) {
|
|
732
|
+
return { cleaned: 0 };
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
639
736
|
function findFreePortSync() {
|
|
640
737
|
const r = spawnSync(process.execPath, ['-e', `
|
|
641
738
|
const net = require('net');
|
|
@@ -689,10 +786,31 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
689
786
|
const ports = readJsonFile(portsFile, {});
|
|
690
787
|
const sessions = readJsonFile(sessionsFile, {});
|
|
691
788
|
const existing = ports[claudeSessionId];
|
|
692
|
-
if (existing && existing.port
|
|
693
|
-
const
|
|
694
|
-
|
|
789
|
+
if (existing && existing.port) {
|
|
790
|
+
const wantProfile = path.join(cwd, '.gm', 'browser-profile');
|
|
791
|
+
const pidOk = existing.pid && isProcessAliveSync(existing.pid);
|
|
792
|
+
const profileOk = !existing.profileDir || existing.profileDir === wantProfile || existing.profileDir.startsWith(path.join(cwd, '.gm', 'browser-profile'));
|
|
793
|
+
const portOk = isPortAliveSync(existing.port);
|
|
794
|
+
if (pidOk && profileOk && portOk) {
|
|
795
|
+
const pwIds = sessions[claudeSessionId] || [];
|
|
796
|
+
if (pwIds.length > 0) return pwIds[0];
|
|
797
|
+
} else {
|
|
798
|
+
const reason = !pidOk ? 'pid-dead' : !profileOk ? 'profile-drift' : 'port-dead';
|
|
799
|
+
logEvent('hook', 'deviation.browser-profile-collision', {
|
|
800
|
+
sid: claudeSessionId,
|
|
801
|
+
stale_pid: existing.pid || null,
|
|
802
|
+
stale_port: existing.port || null,
|
|
803
|
+
stale_profile: existing.profileDir || null,
|
|
804
|
+
want_profile: wantProfile,
|
|
805
|
+
reason,
|
|
806
|
+
});
|
|
807
|
+
delete ports[claudeSessionId];
|
|
808
|
+
delete sessions[claudeSessionId];
|
|
809
|
+
try { writeJsonFile(portsFile, ports); } catch (_) {}
|
|
810
|
+
try { writeJsonFile(sessionsFile, sessions); } catch (_) {}
|
|
811
|
+
}
|
|
695
812
|
}
|
|
813
|
+
cleanDeadProfileFragments(cwd);
|
|
696
814
|
const chrome = findChrome();
|
|
697
815
|
if (!chrome) throw new Error('Chrome not found. Please install Google Chrome.');
|
|
698
816
|
const profileDir = acquireProfileDir(cwd);
|
|
@@ -1276,6 +1394,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
1276
1394
|
const lang = opts.lang || 'nodejs';
|
|
1277
1395
|
const cwd = opts.cwd || process.cwd();
|
|
1278
1396
|
const rawTimeout = opts.timeoutMs;
|
|
1397
|
+
const MIN_TIMEOUT_MS = 100;
|
|
1279
1398
|
if (rawTimeout === undefined || rawTimeout === null || typeof rawTimeout !== 'number' || !Number.isFinite(rawTimeout) || rawTimeout <= 0 || !Number.isInteger(rawTimeout)) {
|
|
1280
1399
|
return writeWasmJson(instanceRef.value, {
|
|
1281
1400
|
ok: false,
|
|
@@ -1285,6 +1404,15 @@ function makeHostFunctions(instanceRef) {
|
|
|
1285
1404
|
received: rawTimeout === undefined ? null : rawTimeout,
|
|
1286
1405
|
});
|
|
1287
1406
|
}
|
|
1407
|
+
if (rawTimeout < MIN_TIMEOUT_MS) {
|
|
1408
|
+
return writeWasmJson(instanceRef.value, {
|
|
1409
|
+
ok: false,
|
|
1410
|
+
error: 'timeoutMs below floor',
|
|
1411
|
+
min: MIN_TIMEOUT_MS,
|
|
1412
|
+
received: rawTimeout,
|
|
1413
|
+
paper_ref: '§20',
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1288
1416
|
const timeoutMs = rawTimeout;
|
|
1289
1417
|
let cmd, args;
|
|
1290
1418
|
if (lang === 'nodejs' || lang === 'js') { cmd = process.execPath; args = ['-e', code]; }
|
|
@@ -1342,7 +1470,12 @@ function makeHostFunctions(instanceRef) {
|
|
|
1342
1470
|
});
|
|
1343
1471
|
}
|
|
1344
1472
|
const pwSessionId = getOrCreateBrowserSession(cwd, sessionId, pw);
|
|
1345
|
-
const
|
|
1473
|
+
const portsAfter = readJsonFile(browserPortsFile(cwd), {});
|
|
1474
|
+
const livePort = portsAfter[sessionId] && portsAfter[sessionId].port;
|
|
1475
|
+
const directArgs = (livePort && isPortAliveSync(livePort))
|
|
1476
|
+
? [`--direct=localhost:${livePort}`]
|
|
1477
|
+
: [];
|
|
1478
|
+
const r = runPlaywriter(pw, ['-s', pwSessionId, '--timeout', '14000', ...directArgs, '-e', body], 60000);
|
|
1346
1479
|
return writeWasmJson(instanceRef.value, {
|
|
1347
1480
|
ok: r.status === 0,
|
|
1348
1481
|
stdout: scrubBrowserRunnerText(r.stdout || ''),
|
|
@@ -1852,10 +1985,23 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1852
1985
|
|
|
1853
1986
|
if (verb === 'browser') {
|
|
1854
1987
|
try {
|
|
1855
|
-
const
|
|
1988
|
+
const cwd_ = process.cwd();
|
|
1989
|
+
const editsFile = path.join(cwd_, '.gm', 'exec-spool', '.turn-browser-edits.json');
|
|
1990
|
+
const witnessFile = path.join(cwd_, '.gm', 'exec-spool', '.turn-browser-witnessed');
|
|
1856
1991
|
fs.mkdirSync(path.dirname(witnessFile), { recursive: true });
|
|
1857
|
-
|
|
1858
|
-
|
|
1992
|
+
let edits = [];
|
|
1993
|
+
try { edits = JSON.parse(fs.readFileSync(editsFile, 'utf8')); if (!Array.isArray(edits)) edits = []; } catch (_) {}
|
|
1994
|
+
const witnessed_hashes = {};
|
|
1995
|
+
for (const e of edits) {
|
|
1996
|
+
if (!e || !e.file) continue;
|
|
1997
|
+
try {
|
|
1998
|
+
const abs = path.isAbsolute(e.file) ? e.file : path.join(cwd_, e.file);
|
|
1999
|
+
const buf = fs.readFileSync(abs);
|
|
2000
|
+
witnessed_hashes[e.file] = crypto.createHash('sha256').update(buf).digest('hex').slice(0, 12);
|
|
2001
|
+
} catch (_) { witnessed_hashes[e.file] = ''; }
|
|
2002
|
+
}
|
|
2003
|
+
fs.writeFileSync(witnessFile, JSON.stringify({ ts: Date.now(), task: taskBase, dur_ms, witnessed_hashes }));
|
|
2004
|
+
logEvent('plugkit', 'browser.witness-marked', { task: taskBase, files: Object.keys(witnessed_hashes) });
|
|
1859
2005
|
} catch (_) {}
|
|
1860
2006
|
}
|
|
1861
2007
|
|
package/gm.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1223",
|
|
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.449"
|
|
21
21
|
}
|
package/lib/spool-dispatch.js
CHANGED
|
@@ -77,6 +77,14 @@ function browserWitnessFile(cwd) {
|
|
|
77
77
|
return path.join(cwd || process.cwd(), '.gm', 'exec-spool', '.turn-browser-witnessed');
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function hashFileShort(root, rel) {
|
|
81
|
+
try {
|
|
82
|
+
const abs = path.isAbsolute(rel) ? rel : path.join(root, rel);
|
|
83
|
+
const buf = fs.readFileSync(abs);
|
|
84
|
+
return require('crypto').createHash('sha256').update(buf).digest('hex').slice(0, 12);
|
|
85
|
+
} catch (_) { return ''; }
|
|
86
|
+
}
|
|
87
|
+
|
|
80
88
|
function recordBrowserEdit(cwd, filePath) {
|
|
81
89
|
try {
|
|
82
90
|
const root = cwd || process.cwd();
|
|
@@ -87,8 +95,11 @@ function recordBrowserEdit(cwd, filePath) {
|
|
|
87
95
|
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
88
96
|
let list = [];
|
|
89
97
|
try { list = JSON.parse(fs.readFileSync(f, 'utf8')); if (!Array.isArray(list)) list = []; } catch (_) {}
|
|
90
|
-
const
|
|
91
|
-
|
|
98
|
+
const relPath = rel.replace(/\\/g, '/');
|
|
99
|
+
const hash = hashFileShort(root, relPath);
|
|
100
|
+
const idx = list.findIndex(e => e && e.file === relPath);
|
|
101
|
+
const entry = { file: relPath, ts: Date.now(), hash };
|
|
102
|
+
if (idx === -1) list.push(entry); else list[idx] = entry;
|
|
92
103
|
fs.writeFileSync(f, JSON.stringify(list));
|
|
93
104
|
return true;
|
|
94
105
|
} catch (_) { return false; }
|
|
@@ -103,12 +114,27 @@ function clearBrowserTurnMarkers(cwd) {
|
|
|
103
114
|
|
|
104
115
|
function markBrowserWitnessed(cwd, meta) {
|
|
105
116
|
try {
|
|
106
|
-
const
|
|
117
|
+
const root = cwd || process.cwd();
|
|
118
|
+
const f = browserWitnessFile(root);
|
|
107
119
|
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
108
|
-
|
|
120
|
+
const edits = readBrowserEdits(root);
|
|
121
|
+
const witnessed_hashes = {};
|
|
122
|
+
for (const e of edits) {
|
|
123
|
+
if (!e || !e.file) continue;
|
|
124
|
+
witnessed_hashes[e.file] = hashFileShort(root, e.file);
|
|
125
|
+
}
|
|
126
|
+
fs.writeFileSync(f, JSON.stringify({ ts: Date.now(), witnessed_hashes, ...(meta || {}) }));
|
|
109
127
|
} catch (_) {}
|
|
110
128
|
}
|
|
111
129
|
|
|
130
|
+
function readBrowserWitness(cwd) {
|
|
131
|
+
try {
|
|
132
|
+
const f = browserWitnessFile(cwd);
|
|
133
|
+
if (!fs.existsSync(f)) return null;
|
|
134
|
+
return JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
135
|
+
} catch (_) { return null; }
|
|
136
|
+
}
|
|
137
|
+
|
|
112
138
|
function readBrowserEdits(cwd) {
|
|
113
139
|
try {
|
|
114
140
|
const f = browserEditsFile(cwd);
|
|
@@ -354,6 +380,29 @@ function checkDispatchGates(sessionId, operation, extra) {
|
|
|
354
380
|
const shown = files.slice(0, 5).join(', ') + (files.length > 5 ? `, +${files.length - 5} more` : '');
|
|
355
381
|
residuals.push(`Browser Witness required: you edited ${shown} without dispatching the browser verb to witness the change in a live page. Per paper §23 this is non-negotiable. Either dispatch browser to verify the edit works in-browser, or revert the changes.`);
|
|
356
382
|
logDeviation('deviation.browser-witness-missing', { files, operation });
|
|
383
|
+
} else if (browserEdits.length > 0 && isBrowserWitnessed(cwd)) {
|
|
384
|
+
const witness = readBrowserWitness(cwd) || {};
|
|
385
|
+
const wh = witness.witnessed_hashes || {};
|
|
386
|
+
const mismatches = [];
|
|
387
|
+
for (const e of browserEdits) {
|
|
388
|
+
if (!e || !e.file) continue;
|
|
389
|
+
const witnessed = wh[e.file];
|
|
390
|
+
if (!witnessed) {
|
|
391
|
+
mismatches.push({ file: e.file, reason: 'no witnessed hash recorded (file edited after witness, or witness predates edit)' });
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const current = hashFileShort(cwd || process.cwd(), e.file);
|
|
395
|
+
if (current !== witnessed) {
|
|
396
|
+
mismatches.push({ file: e.file, witnessed_hash: witnessed, current_hash: current || '(unreadable)' });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (mismatches.length > 0) {
|
|
400
|
+
const summary = mismatches.slice(0, 3).map(m =>
|
|
401
|
+
`${m.file} (witnessed=${m.witnessed_hash || 'none'}, current=${m.current_hash || '(none)'}${m.reason ? '; ' + m.reason : ''})`
|
|
402
|
+
).join('; ');
|
|
403
|
+
residuals.push(`Browser Witness hash mismatch: you witnessed file(s) at one state, but their current content differs. Either the witness was on a different state or the file was reverted/re-edited without re-witnessing. Re-run the browser verb against the current state. Mismatches: ${summary}${mismatches.length > 3 ? `, +${mismatches.length - 3} more` : ''}`);
|
|
404
|
+
logDeviation('deviation.browser-witness-hash-mismatch', { mismatches, operation });
|
|
405
|
+
}
|
|
357
406
|
}
|
|
358
407
|
if (residuals.length > 0) {
|
|
359
408
|
logDeviation('deviation.gate-deny', { operation, reason: 'stop-gate residuals', residuals });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1223",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"gm.json"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"gm-plugkit": "^2.0.
|
|
42
|
+
"gm-plugkit": "^2.0.1223"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=16.0.0"
|