gm-skill 2.0.1987 → 2.0.1988

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.
@@ -0,0 +1 @@
1
+ 3e10e4016c1d72daf121a2136c37570a74fc12afaaa45c5fc724098ac89e1c3a plugkit-slim.wasm
@@ -223,6 +223,26 @@ function gmToolsDir() {
223
223
  return primary;
224
224
  }
225
225
 
226
+ // Slim-artifact eligibility: plugkit-core's embed.rs probes host_vec_embed
227
+ // before ever loading its wasm-embedded safetensors fallback (see
228
+ // rs-plugkit/crates/plugkit-core/src/embed.rs::init_ctx) -- a slim build
229
+ // (feature=slim, no embedded weights at all) is only safe to fetch on a host
230
+ // that actually answers host_vec_embed for real. plugkit-wasm-wrapper.js
231
+ // wires that answer through gm-runner's native candle path
232
+ // (hostEmbedViaGmRunner) or agentplug-runner's shared daemon, both gated on
233
+ // one of these two binaries existing under ~/.gm-tools -- mirrors that same
234
+ // on-disk presence check here so bootstrap-time artifact selection and
235
+ // runtime embed-delegation eligibility never disagree. Absence of both means
236
+ // no host_vec_embed answer will ever come, so fetching fat (which carries its
237
+ // own wasm-side embedding fallback) is the only safe choice.
238
+ function hasNativeEmbedRunner() {
239
+ const dir = gmToolsDir();
240
+ const names = process.platform === 'win32'
241
+ ? ['gm-runner.exe', 'agentplug-runner.exe']
242
+ : ['gm-runner', 'agentplug-runner'];
243
+ return names.some(n => { try { return fs.existsSync(path.join(dir, n)); } catch (_) { return false; } });
244
+ }
245
+
226
246
  // Root a project-dir resolution at the git COMMON dir, not the raw cwd/
227
247
  // CLAUDE_PROJECT_DIR -- a worktree (e.g. Workflow's isolation:'worktree'
228
248
  // agents, each `git worktree add`-ing a fresh physical directory) shares the
@@ -265,8 +285,26 @@ function readVersionFile() {
265
285
  function readShaManifest() {
266
286
  const p = path.join(wrapperDir, 'plugkit.sha256');
267
287
  if (!fs.existsSync(p)) return null;
288
+ const raw = fs.readFileSync(p, 'utf8');
289
+ // Two real formats seen in the wild for this file: the checked-in local
290
+ // gm-plugkit/plugkit.sha256 is a JSON manifest ({"plugkit.wasm":"<sha>"}),
291
+ // written by the version-bump automation; a GitHub-release .sha256 sidecar
292
+ // (fetched directly from plugkit-bin releases) is a standard sha256sum-format
293
+ // line ("<hash> <filename>"). Try JSON first since that's this file's own
294
+ // real on-disk shape -- the sha256sum-line regex never matched it, silently
295
+ // returning {} and skipping verification on every bootstrap call.
296
+ try {
297
+ const parsed = JSON.parse(raw);
298
+ if (parsed && typeof parsed === 'object') {
299
+ const out = {};
300
+ for (const [name, sha] of Object.entries(parsed)) {
301
+ if (typeof sha === 'string') out[name] = sha.toLowerCase();
302
+ }
303
+ return out;
304
+ }
305
+ } catch (_) { /* not JSON, fall through to sha256sum-line parsing */ }
268
306
  const out = {};
269
- for (const line of fs.readFileSync(p, 'utf8').split(/\r?\n/)) {
307
+ for (const line of raw.split(/\r?\n/)) {
270
308
  const m = line.match(/^([0-9a-f]{64})\s+(\S+)\s*$/i);
271
309
  if (m) out[m[2]] = m[1].toLowerCase();
272
310
  }
@@ -441,14 +479,39 @@ function httpGetBuffer(url, timeoutMs) {
441
479
  });
442
480
  }
443
481
 
444
- async function downloadFromGithubReleases(destPath, version) {
482
+ // artifactName selects the REMOTE release asset ('plugkit.wasm' fat or
483
+ // 'plugkit-slim.wasm' slim) -- the local destPath filename is unaffected,
484
+ // same convention gm-runner's own download.rs::bootstrap_plugkit_wasm uses
485
+ // (fixed local name, artifact-selected remote source). A slim fetch that 404s
486
+ // (older release predating the slim publish step, or the asset genuinely
487
+ // missing) falls back to fetching fat rather than failing the whole
488
+ // bootstrap -- a host capable of running the slim build is also always a
489
+ // valid fat-build host (fat is a strict superset of slim's capability), so
490
+ // this fallback never produces incorrect behavior, only a larger download.
491
+ async function downloadFromGithubReleases(destPath, version, artifactName) {
492
+ const name = artifactName || 'plugkit.wasm';
445
493
  const base = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}`;
446
- log(`gh-releases download: ${base}/plugkit.wasm`);
447
- const buf = await httpGetBuffer(`${base}/plugkit.wasm`, 60000);
448
- if (!buf || buf.length < 1024) throw new Error(`gh-releases download too small: ${buf ? buf.length : 0} bytes`);
494
+ log(`gh-releases download: ${base}/${name}`);
495
+ let buf;
496
+ try {
497
+ buf = await httpGetBuffer(`${base}/${name}`, 60000);
498
+ } catch (e) {
499
+ if (name !== 'plugkit.wasm') {
500
+ log(`gh-releases slim fetch failed (${e.message}); falling back to fat plugkit.wasm`);
501
+ return downloadFromGithubReleases(destPath, version, 'plugkit.wasm');
502
+ }
503
+ throw e;
504
+ }
505
+ if (!buf || buf.length < 1024) {
506
+ if (name !== 'plugkit.wasm') {
507
+ log(`gh-releases slim download too small (${buf ? buf.length : 0} bytes); falling back to fat plugkit.wasm`);
508
+ return downloadFromGithubReleases(destPath, version, 'plugkit.wasm');
509
+ }
510
+ throw new Error(`gh-releases download too small: ${buf ? buf.length : 0} bytes`);
511
+ }
449
512
  let remoteSha = '';
450
513
  try {
451
- const shaBuf = await httpGetBuffer(`${base}/plugkit.wasm.sha256`, 10000);
514
+ const shaBuf = await httpGetBuffer(`${base}/${name}.sha256`, 10000);
452
515
  remoteSha = shaBuf.toString('utf-8').trim().split(/\s+/)[0];
453
516
  } catch (e) { log(`gh-releases sha fetch failed: ${e.message}`); }
454
517
  if (remoteSha) {
@@ -457,7 +520,7 @@ async function downloadFromGithubReleases(destPath, version) {
457
520
  log(`gh-releases sha verified ${got.slice(0, 16)}...`);
458
521
  }
459
522
  fs.writeFileSync(destPath, buf);
460
- log(`gh-releases wrote ${buf.length} bytes to ${destPath}`);
523
+ log(`gh-releases wrote ${buf.length} bytes to ${destPath} (artifact=${name})`);
461
524
  }
462
525
 
463
526
  async function extractNpmPackageWithRetry(destPath, version) {
@@ -625,14 +688,27 @@ async function bootstrap(opts) {
625
688
  opts = opts || {};
626
689
  const version = readVersionFile();
627
690
  const shaManifest = readShaManifest();
691
+ // Artifact selection: slim (no wasm-embedded safetensors, ~130MB smaller)
692
+ // only when this host has a native host_vec_embed answerer on disk
693
+ // (gm-runner or agentplug-runner under ~/.gm-tools -- see
694
+ // hasNativeEmbedRunner) -- everyone else fetches fat, unchanged from
695
+ // before this selection logic existed. remoteArtifact is the release-asset
696
+ // name; the LOCAL cache filename stays 'plugkit.wasm' either way (matching
697
+ // gm-runner's own download.rs convention) so nothing downstream that reads
698
+ // the local wasm path needs to know which variant landed. The cache
699
+ // sub-directory is kept distinct per-kind (v<version> vs v<version>-slim)
700
+ // so a fat and slim download of the same version never collide under the
701
+ // same sha/sentinel check.
702
+ const useSlim = hasNativeEmbedRunner();
703
+ const remoteArtifact = useSlim ? 'plugkit-slim.wasm' : 'plugkit.wasm';
628
704
  const wasmName = 'plugkit.wasm';
629
- const expectedSha = shaManifest ? shaManifest[wasmName] : null;
705
+ const expectedSha = shaManifest ? (shaManifest[remoteArtifact] || (useSlim ? null : shaManifest[wasmName])) : null;
630
706
 
631
707
  let root = cacheRoot();
632
708
  try { ensureDir(root); }
633
709
  catch (_) { root = fallbackCacheRoot(); ensureDir(root); }
634
710
 
635
- const verDir = path.join(root, `v${version}`);
711
+ const verDir = path.join(root, useSlim ? `v${version}-slim` : `v${version}`);
636
712
  ensureDir(verDir);
637
713
 
638
714
  const finalPath = path.join(verDir, wasmName);
@@ -696,20 +772,38 @@ async function bootstrap(opts) {
696
772
  }
697
773
  } catch (_) {}
698
774
  }
699
- try {
700
- await extractNpmPackageWithRetry(partialPath, version);
701
- } catch (extractErr) {
702
- log(`npm-extract failed (${extractErr.message || extractErr}); falling back to GitHub Releases`);
775
+ if (useSlim) {
776
+ // The plugkit-wasm npm package only ever ships the fat artifact (see
777
+ // release.yml's npm-publish step, which cp's release-assets/plugkit.wasm
778
+ // -- never plugkit-slim.wasm -- into the package). Skip the npm-extract
779
+ // attempt entirely for a slim fetch and go straight to GitHub Releases,
780
+ // which is where slim is actually published.
703
781
  try {
704
- await downloadFromGithubReleases(partialPath, version);
782
+ await downloadFromGithubReleases(partialPath, version, remoteArtifact);
705
783
  } catch (ghErr) {
706
784
  writeBootstrapError({
707
785
  expected_version: version, cached_version: null,
708
- error_phase: 'npm-extract+gh-fallback',
709
- error_message: `npm: ${extractErr.message}; gh: ${ghErr.message}`,
786
+ error_phase: 'gh-releases-slim',
787
+ error_message: `gh: ${ghErr.message}`,
710
788
  });
711
789
  throw ghErr;
712
790
  }
791
+ } else {
792
+ try {
793
+ await extractNpmPackageWithRetry(partialPath, version);
794
+ } catch (extractErr) {
795
+ log(`npm-extract failed (${extractErr.message || extractErr}); falling back to GitHub Releases`);
796
+ try {
797
+ await downloadFromGithubReleases(partialPath, version, remoteArtifact);
798
+ } catch (ghErr) {
799
+ writeBootstrapError({
800
+ expected_version: version, cached_version: null,
801
+ error_phase: 'npm-extract+gh-fallback',
802
+ error_message: `npm: ${extractErr.message}; gh: ${ghErr.message}`,
803
+ });
804
+ throw ghErr;
805
+ }
806
+ }
713
807
  }
714
808
 
715
809
  if (expectedSha) {
@@ -740,7 +834,15 @@ async function bootstrap(opts) {
740
834
  log(`decision: fetch reason: install-complete (${finalPath})`);
741
835
  obsEvent('bootstrap', 'install.done', { path: finalPath, version, kind: 'plugkit-wasm' });
742
836
  proactiveKillForNewInstall(version);
743
- pruneOldVersions(root, version);
837
+ // pruneOldVersions keeps only the dir literally named v<keepVersion> --
838
+ // verDir uses a '-slim' suffix for the slim cache slot (see useSlim
839
+ // above), so the keep-token passed here must match that same suffix or
840
+ // pruneOldVersions deletes the directory just populated by THIS run
841
+ // before copyWasmToGmTools below can read from it (live-witnessed this
842
+ // session: a real end-to-end bootstrap() run on a native-runner host hit
843
+ // exactly this -- 'pruned .../v0.1.906-slim' followed by an ENOENT on the
844
+ // immediately-following copy, because 'v0.1.906' != 'v0.1.906-slim').
845
+ pruneOldVersions(root, useSlim ? `${version}-slim` : version);
744
846
  copyWasmToGmTools(finalPath, version);
745
847
  clearBootstrapError();
746
848
  return finalPath;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1987",
3
+ "version": "2.0.1988",
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": {
@@ -0,0 +1 @@
1
+ 3e10e4016c1d72daf121a2136c37570a74fc12afaaa45c5fc724098ac89e1c3a plugkit-slim.wasm
@@ -1936,6 +1936,10 @@ globalThis.__hostEmbedSync = function __hostEmbedSync(textPtr, textLen, outPtr,
1936
1936
 
1937
1937
  function makeHostFunctions(instanceRef) {
1938
1938
  return {
1939
+ host_cwd: () => {
1940
+ const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
1941
+ return writeWasmStr(instanceRef.value, cwd);
1942
+ },
1939
1943
  host_fs_read: (pathPtr, pathLen) => {
1940
1944
  try {
1941
1945
  const filePath = readWasmStr(instanceRef.value, pathPtr, pathLen);
@@ -4601,6 +4605,17 @@ async function runSpoolWatcher(instance, spoolDir) {
4601
4605
  await new Promise(() => {});
4602
4606
  }
4603
4607
 
4608
+ // Slim eligibility mirrors bootstrap.js's own hasNativeEmbedRunner (same
4609
+ // on-disk check, duplicated rather than shared because this file is ESM and
4610
+ // bootstrap.js is CJS with no shared module between them today) -- a slim
4611
+ // fetch here is only correct when a real host_vec_embed answerer
4612
+ // (resolveGmRunnerEmbedBin / resolveAgentplugRunnerBin, both already defined
4613
+ // above for the runtime embed-delegation path) is present on disk, since a
4614
+ // slim wasm has no fallback embedding path of its own.
4615
+ function hasNativeEmbedRunnerForSelfHeal() {
4616
+ return !!(resolveGmRunnerEmbedBin() || resolveAgentplugRunnerBin());
4617
+ }
4618
+
4604
4619
  async function selfHealFromGithubReleases() {
4605
4620
  return new Promise((resolve, reject) => {
4606
4621
  const fetchJson = (url) => new Promise((res, rej) => {
@@ -4635,10 +4650,30 @@ async function selfHealFromGithubReleases() {
4635
4650
  const version = meta && meta.version;
4636
4651
  if (!version) throw new Error('no version from npm plugkit-wasm');
4637
4652
  const base = `https://github.com/AnEntrypoint/plugkit-bin/releases/download/v${version}`;
4638
- const [wasm, sha] = await Promise.all([
4639
- fetchBuf(`${base}/plugkit.wasm`),
4640
- fetchBuf(`${base}/plugkit.wasm.sha256`).then(b => b.toString('utf-8').trim().split(/\s+/)[0]).catch(() => ''),
4641
- ]);
4653
+ // Slim only when a real host_vec_embed answerer is present -- see
4654
+ // hasNativeEmbedRunnerForSelfHeal. A slim asset that 404s (older
4655
+ // release predating slim publish) falls back to fat rather than
4656
+ // failing self-heal entirely, since fat is a strict capability
4657
+ // superset and always a safe (if larger) substitute.
4658
+ const wantSlim = hasNativeEmbedRunnerForSelfHeal();
4659
+ let artifactName = wantSlim ? 'plugkit-slim.wasm' : 'plugkit.wasm';
4660
+ let wasm, sha;
4661
+ try {
4662
+ [wasm, sha] = await Promise.all([
4663
+ fetchBuf(`${base}/${artifactName}`),
4664
+ fetchBuf(`${base}/${artifactName}.sha256`).then(b => b.toString('utf-8').trim().split(/\s+/)[0]).catch(() => ''),
4665
+ ]);
4666
+ } catch (e) {
4667
+ if (artifactName !== 'plugkit.wasm') {
4668
+ artifactName = 'plugkit.wasm';
4669
+ [wasm, sha] = await Promise.all([
4670
+ fetchBuf(`${base}/${artifactName}`),
4671
+ fetchBuf(`${base}/${artifactName}.sha256`).then(b => b.toString('utf-8').trim().split(/\s+/)[0]).catch(() => ''),
4672
+ ]);
4673
+ } else {
4674
+ throw e;
4675
+ }
4676
+ }
4642
4677
  if (sha) {
4643
4678
  const got = crypto.createHash('sha256').update(wasm).digest('hex');
4644
4679
  if (got !== sha) throw new Error(`sha mismatch: got ${got}, expected ${sha}`);
@@ -4664,7 +4699,7 @@ async function selfHealFromGithubReleases() {
4664
4699
  if (path.resolve(wrapperSrc) !== path.resolve(wrapperDst) && fs.existsSync(wrapperSrc)) {
4665
4700
  try { fs.copyFileSync(wrapperSrc, wrapperDst); } catch (_) {}
4666
4701
  }
4667
- resolve({ ok: true, version, sha });
4702
+ resolve({ ok: true, version, sha, artifact: artifactName });
4668
4703
  } catch (e) { reject(e); }
4669
4704
  })();
4670
4705
  });
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1987",
3
+ "version": "2.0.1988",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1987",
3
+ "version": "2.0.1988",
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",
@@ -36,6 +36,7 @@
36
36
  "bin/plugkit.sha256",
37
37
  "bin/plugkit.version",
38
38
  "bin/plugkit.wasm.sha256",
39
+ "bin/plugkit-slim.wasm.sha256",
39
40
  "gm-plugkit/bootstrap.js",
40
41
  "gm-plugkit/browser-idle.js",
41
42
  "gm-plugkit/cli.js",
@@ -47,6 +48,7 @@
47
48
  "gm-plugkit/gm-process.js",
48
49
  "gm-plugkit/plugkit.version",
49
50
  "gm-plugkit/plugkit.sha256",
51
+ "gm-plugkit/plugkit-slim.wasm.sha256",
50
52
  "gm-plugkit/instructions/",
51
53
  "gm-plugkit/scripts/",
52
54
  "AGENTS.md",