@ai-ecoverse/mcpcat 0.17.5 → 0.17.7

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 CHANGED
@@ -26,14 +26,12 @@ mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Slicc
26
26
 
27
27
  ## Releases: certified = latest
28
28
 
29
- Every change that alters what mcpcat ships is certified before it reaches `main`. A pull request that touches `src/`, the build, the dependencies or the release workflows gets mcpcat's cert-review on a pack of its head, and again in the merge queue on the exact tree that lands on `main`; a FAIL blocks the merge. Docs-, test- and CI-only changes skip it as "not behaviour". A draft pull request is not reviewed: its `cert-review` is red with `draft: not reviewed, mark it ready for review`, and marking it ready starts the review (converted_to_draft cancels a running one). Mark ready = reviewed, and merges on green; a pull request held for anything stays a draft. For a PR that needs the coordinator's security read, the order is: the coordinator reads the draft, it is marked ready, the cert runs, then the queue. Each merge to `main` is then released to npm as `latest` in one step by semantic-release, with provenance, so `npm install @ai-ecoverse/mcpcat` and the installer always get certified code. The verdict line is a comment on the pull request and the `cert-review` commit status. It reads `certified …`, `cert-ci: FAIL …` when the reviewer or a check in code found a defect (status `failure`), or `cert-ci: INCOMPLETE …` when the review couldn't decide: the review job didn't succeed or was skipped, no verdict could be read, or the artifact or the runner's own `gh` was missing (status `error`). A fork's change to shipped behaviour also reads INCOMPLETE, since it gets no review. INCOMPLETE never certifies; re-run the failed jobs. A run that ends without `record` posting its status (a crashed plan, record or verdict job) still posts one on the head: `error` with its INCOMPLETE line, `failure` for a FAIL, or `pending` while the coordinator approval is missing. So the required `cert-review` check never waits forever, and a label change reuses only a `success` or `failure` verdict, never an INCOMPLETE one. The reviewer's verdict can add defects but never clear one found in code: `record` re-reads the suites' evidence (`stage.json`, `suites.json`), uploaded before the reviewer runs and bound by the artifact id and digest the review job output. A suite or security row that failed on the host or a pinned kernel, a changed test that fails on the head, an import in the kernel bundle or an unexplained rebuild difference is a FAIL. Missing suites, kernels or rows are INCOMPLETE. The NEGATIVE runs each changed test on the base's code: the subject's tests, helpers and fixtures with the base's `dist`, `scripts/` and `.github/workflows/`, all from the base sha, so a test of the cert tooling runs against main's tooling. The record names both shas. A changed test that also passes on the base is named in the line as `gaps: no-negative <file>`. Every artifact a later job reads (the tarball, the verdict, the suites' evidence) is downloaded by the artifact id its producer output, after `bind.mjs` checks the artifact's name, run and digest against that producer's outputs. Any other artifact from this attempt of the run under `mcpcat-tarball-`, `mcpcat-npm-tarball-` or `cert-review-` makes the line INCOMPLETE. An earlier attempt's leftovers, created before this attempt started, are never read.
29
+ Every change that alters what mcpcat ships is certified before it reaches `main`. A pull request that touches `src/`, the build, the dependencies or the release workflows gets mcpcat's cert-review on a pack of its head, and again in the merge queue on the exact tree that lands on `main`; a FAIL blocks the merge. Docs-, test- and CI-only changes skip it as "not behaviour". A draft pull request is not reviewed: its `cert-review` is red with `draft: not reviewed, mark it ready for review`, and marking it ready starts the review (converted_to_draft cancels a running one). Mark ready = reviewed, and merges on green; a pull request held for anything stays a draft. For a PR that needs the coordinator's security read, the order is: the coordinator reads the draft, it is marked ready, the cert runs, then the queue. Each merge to `main` is then released to npm as `latest` in one step by semantic-release, with provenance, so `npm install @ai-ecoverse/mcpcat` and the installer always get certified code. The verdict line is a comment on the pull request and the `cert-review` commit status. It reads `certified …`, `cert-ci: FAIL …` when the reviewer or a check in code found a defect (status `failure`), or `cert-ci: INCOMPLETE …` when the review couldn't decide: the review job didn't succeed or was skipped, no verdict could be read, or the artifact or the runner's own `gh` was missing (status `error`). A fork's change to shipped behaviour also reads INCOMPLETE, since it gets no review. INCOMPLETE never certifies; re-run the failed jobs. A run that ends without `record` posting its status (a crashed plan, record or verdict job) still posts one on the head: `error` with its INCOMPLETE line, or `failure` for a FAIL. So the required `cert-review` check never waits forever. The reviewer's verdict can add defects but never clear one found in code: `record` re-reads the suites' evidence (`stage.json`, `suites.json`), uploaded before the reviewer runs and bound by the artifact id and digest the review job output. A suite or security row that failed on the host or a pinned kernel, a changed test that fails on the head, an import in the kernel bundle or an unexplained rebuild difference is a FAIL. Missing suites, kernels or rows are INCOMPLETE. The NEGATIVE runs each changed test on the base's code: the subject's tests, helpers and fixtures with the base's `dist`, `scripts/` and `.github/workflows/`, all from the base sha, so a test of the cert tooling runs against main's tooling. The record names both shas. A changed test that also passes on the base is named in the line as `gaps: no-negative <file>`. Every artifact a later job reads (the tarball, the verdict, the suites' evidence) is downloaded by the artifact id its producer output, after `bind.mjs` checks the artifact's name, run and digest against that producer's outputs. Any other artifact from this attempt of the run under `mcpcat-tarball-`, `mcpcat-npm-tarball-` or `cert-review-` makes the line INCOMPLETE. An earlier attempt's leftovers, created before this attempt started, are never read.
30
30
 
31
31
  After each release, `cert-verify.yml` (on the Release run's success, or dispatched from `main` with a `version`) checks again that npm serves the SLSA subject built by `release.yml` on `main` from the tag, and that every pull request merged since the previous tag carries a green `cert-review` status on its head. Its hosted, write-only `notes` job then puts one line into the GitHub release notes, between `<!-- cert-verify -->` markers: `certified @ai-ecoverse/mcpcat@<version> sha256=<sha> (…; pr-certified since v<prev>: …)`, or `verified …` when some pull request lacks a green cert-review. A re-run with the same bytes leaves the notes as they are. Notes that already name another sha for the version, or npm bytes that don't verify, are a supply-chain alert: nothing is written and an issue is filed. Checks that couldn't run read `cert-ci: INCOMPLETE` and write nothing.
32
32
 
33
33
  Silence never passes: every gate in the workflows prints a verdict line, and its caller requires that line, not just an exit code. These are `all-green: PASS|FAIL …`, the cert job's `cert: kernel <v> — N kernel tests passed`, shipped.mjs's `behaviour:`/`not behaviour:`, release verify's `verified …`, and the installer tests' `All installer tests passed.`. A gate that exits 0 without its line fails.
34
34
 
35
- A pull request or merge group that touches the certification itself (the `cert-review` and `ci` workflows, `scripts/cert-review/` and `rows.json`, `scripts/cert.mjs`, `scripts/kernel-test-files.mjs`, and the cert-row, cert-gate, cert-review, cert-runner, cert-self-guard, cert-verify and cert-verdicts tests) fails `cert-review` with "needs coordinator review" until the label `cert-review-approved` is applied after its head arrived. The check is the shared [cert self-guard](https://github.com/ai-ecoverse/slicc-shared-web/tree/f342d7cf558c817ce517bbbbc4cc46df6eac2f97/.github/actions/cert-self-guard), pinned to a full sha in `cert-review.yml`. Only the lane coordinator applies the label, after reading the diff; workers never add or remove it. Any push removes it, so every new head needs a fresh approval; a label change re-runs the checks but reuses the recorded cert-review verdict of an unchanged head. To re-check an approval, run the label event, not the earlier push. The label is procedural: every agent acts as one admin account, so the permission check cannot tell a coordinator from a worker, and a deliberate rewrite of the workflow is not stopped. The `kernel-cert-*` pins in `package.json` are not guarded, because the guard matches whole files and guarding `package.json` would hold every Renovate bump for approval.
36
-
37
35
  ## Quick start
38
36
 
39
37
  ```sh
@@ -9855,6 +9855,15 @@ function containsPlaceholders(value) {
9855
9855
  function expandPlaceholders(value, env) {
9856
9856
  return value.replace(PLACEHOLDER, (_, name) => env[name] ?? "");
9857
9857
  }
9858
+ var MIN_VAR_SECRET = 8;
9859
+ function placeholderValues(value, env) {
9860
+ const values = [];
9861
+ for (const m of value.matchAll(PLACEHOLDER)) {
9862
+ const v = env[m[1]];
9863
+ if (v !== void 0 && v.length >= MIN_VAR_SECRET) values.push(v);
9864
+ }
9865
+ return values;
9866
+ }
9858
9867
  function missingPlaceholders(value, env) {
9859
9868
  const missing = [];
9860
9869
  for (const m of value.matchAll(PLACEHOLDER)) {
@@ -11147,6 +11156,29 @@ async function runDiscover(platform2, args2) {
11147
11156
  return 0;
11148
11157
  }
11149
11158
 
11159
+ // src/render/redact.ts
11160
+ function redactDeep(value, redact2) {
11161
+ if (typeof value === "string") return redact2(value);
11162
+ if (Array.isArray(value)) return value.map((v) => redactDeep(v, redact2));
11163
+ if (value && typeof value === "object") {
11164
+ return Object.fromEntries(
11165
+ Object.entries(value).map(([k, v]) => [redact2(k), redactDeep(v, redact2)])
11166
+ );
11167
+ }
11168
+ return value;
11169
+ }
11170
+ function redactWrites(platform2, redact2) {
11171
+ return Object.assign(Object.create(platform2), {
11172
+ write: (fd, data) => platform2.write(fd, typeof data === "string" ? redact2(data) : data)
11173
+ });
11174
+ }
11175
+ function redactSecrets(text, secrets) {
11176
+ const ordered = [...secrets].filter((s) => s.length > 0).sort((a, b) => b.length - a.length);
11177
+ let result = text;
11178
+ for (const secret of ordered) result = result.replaceAll(secret, "[REDACTED]");
11179
+ return result;
11180
+ }
11181
+
11150
11182
  // src/render/result.ts
11151
11183
  function formatBytes(n) {
11152
11184
  if (n < 1024) return `${n} bytes`;
@@ -15345,15 +15377,7 @@ function isSensitiveHeader(name) {
15345
15377
  return SENSITIVE_HEADER_RE.test(name) || SENSITIVE_NAME_RE.test(name);
15346
15378
  }
15347
15379
  function redact(text, secrets) {
15348
- let result = text;
15349
- for (const secret of secrets) {
15350
- if (secret.length > 0) {
15351
- while (result.includes(secret)) {
15352
- result = result.replace(secret, "[REDACTED]");
15353
- }
15354
- }
15355
- }
15356
- return result;
15380
+ return redactSecrets(text, secrets);
15357
15381
  }
15358
15382
  function createVerboseLogger(platform2, enabled) {
15359
15383
  const secrets = /* @__PURE__ */ new Set();
@@ -16431,7 +16455,7 @@ function expandAndCollect(headers, env) {
16431
16455
  }
16432
16456
  const e = expandPlaceholders(v, env);
16433
16457
  result[k] = e;
16434
- if (e !== v) secrets.push(e);
16458
+ if (e !== v) secrets.push(e, ...placeholderValues(v, env));
16435
16459
  if (v.length > 0) secrets.push(v);
16436
16460
  }
16437
16461
  return { expanded: result, secrets, unset };
@@ -16542,18 +16566,24 @@ async function loadRespondData(platform2, respondArg, json) {
16542
16566
  return { exitCode: EXIT_USAGE };
16543
16567
  }
16544
16568
  }
16545
- async function handleInputRequired(platform2, conn, err, respondData, json) {
16569
+ async function handleInputRequired(platform2, conn, err, respondData, json, redact2) {
16546
16570
  const parsed2 = JSON.parse(err.requestJson);
16571
+ const requestOut = sanitizeJson(JSON.stringify(redactDeep(parsed2, redact2), null, 2));
16547
16572
  const inputReq = parseInputRequest(parsed2.result ?? parsed2);
16548
16573
  if (!inputReq) {
16549
- await platform2.write(2, `${sanitizeJson(err.requestJson)}
16574
+ await platform2.write(2, `${requestOut}
16550
16575
  `);
16551
16576
  return EXIT_INPUT_REQUIRED;
16552
16577
  }
16553
16578
  const isTty = platform2.kind === "host" && platform2.isatty(0) && platform2.isatty(2);
16554
- const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
16579
+ const elicitResult = await handleElicitation(
16580
+ redactWrites(platform2, redact2),
16581
+ inputReq,
16582
+ isTty,
16583
+ respondData
16584
+ );
16555
16585
  if ("exitCode" in elicitResult) {
16556
- await platform2.write(2, `${sanitizeJson(err.requestJson)}
16586
+ await platform2.write(2, `${requestOut}
16557
16587
  `);
16558
16588
  return EXIT_INPUT_REQUIRED;
16559
16589
  }
@@ -16565,7 +16595,7 @@ async function handleInputRequired(platform2, conn, err, respondData, json) {
16565
16595
  return renderCallResult(platform2, retryResult, { json });
16566
16596
  } catch (retryErr) {
16567
16597
  const e2 = classifyError(retryErr);
16568
- await writeErr(platform2, json, e2.kind, e2.msg);
16598
+ await writeErrRedacted(platform2, json, e2.kind, e2.msg, redact2);
16569
16599
  return e2.code;
16570
16600
  }
16571
16601
  }
@@ -16638,17 +16668,7 @@ async function runClientVerb(platform2, version, parsed) {
16638
16668
  const configSecrets = collectConfigSecrets(resolved.headers, platform2.env);
16639
16669
  for (const s of configSecrets) verbose.secrets.add(s);
16640
16670
  const allSecrets = /* @__PURE__ */ new Set([...secrets, ...configSecrets]);
16641
- const redact2 = (text) => {
16642
- let result = text;
16643
- for (const secret of allSecrets) {
16644
- if (secret.length > 0) {
16645
- while (result.includes(secret)) {
16646
- result = result.replace(secret, "[REDACTED]");
16647
- }
16648
- }
16649
- }
16650
- return result;
16651
- };
16671
+ const redact2 = (text) => redactSecrets(text, allSecrets);
16652
16672
  const mergedHeaders = { ...configHeaders, ...expanded };
16653
16673
  const hasAuthHeader = "Authorization" in mergedHeaders;
16654
16674
  const authResolved = await resolveAuthProvider(
@@ -16723,7 +16743,7 @@ async function runClientVerb(platform2, version, parsed) {
16723
16743
  return termExitCode;
16724
16744
  }
16725
16745
  if (err instanceof InputRequiredError) {
16726
- return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput);
16746
+ return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput, redact2);
16727
16747
  }
16728
16748
  const e = classifyError(err);
16729
16749
  await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
@@ -16759,7 +16779,7 @@ function collectConfigSecrets(headers, env) {
16759
16779
  const secrets = [];
16760
16780
  for (const [, v] of Object.entries(headers)) {
16761
16781
  const expanded = expandPlaceholders(v, env);
16762
- if (expanded !== v && expanded.length > 0) secrets.push(expanded);
16782
+ if (expanded !== v && expanded.length > 0) secrets.push(expanded, ...placeholderValues(v, env));
16763
16783
  if (v.length > 0 && v !== expanded) secrets.push(v);
16764
16784
  }
16765
16785
  return secrets;
@@ -17025,5 +17045,5 @@ try {
17025
17045
  });
17026
17046
  } catch {
17027
17047
  }
17028
- var code = await run(platform, "0.17.5", args);
17048
+ var code = await run(platform, "0.17.7", args);
17029
17049
  process.exit(code);
@@ -9476,6 +9476,15 @@ function containsPlaceholders(value) {
9476
9476
  function expandPlaceholders(value, env2) {
9477
9477
  return value.replace(PLACEHOLDER, (_, name) => env2[name] ?? "");
9478
9478
  }
9479
+ var MIN_VAR_SECRET = 8;
9480
+ function placeholderValues(value, env2) {
9481
+ const values = [];
9482
+ for (const m of value.matchAll(PLACEHOLDER)) {
9483
+ const v = env2[m[1]];
9484
+ if (v !== void 0 && v.length >= MIN_VAR_SECRET) values.push(v);
9485
+ }
9486
+ return values;
9487
+ }
9479
9488
  function missingPlaceholders(value, env2) {
9480
9489
  const missing = [];
9481
9490
  for (const m of value.matchAll(PLACEHOLDER)) {
@@ -10768,6 +10777,29 @@ async function runDiscover(platform2, args) {
10768
10777
  return 0;
10769
10778
  }
10770
10779
 
10780
+ // src/render/redact.ts
10781
+ function redactDeep(value, redact2) {
10782
+ if (typeof value === "string") return redact2(value);
10783
+ if (Array.isArray(value)) return value.map((v) => redactDeep(v, redact2));
10784
+ if (value && typeof value === "object") {
10785
+ return Object.fromEntries(
10786
+ Object.entries(value).map(([k, v]) => [redact2(k), redactDeep(v, redact2)])
10787
+ );
10788
+ }
10789
+ return value;
10790
+ }
10791
+ function redactWrites(platform2, redact2) {
10792
+ return Object.assign(Object.create(platform2), {
10793
+ write: (fd, data) => platform2.write(fd, typeof data === "string" ? redact2(data) : data)
10794
+ });
10795
+ }
10796
+ function redactSecrets(text, secrets) {
10797
+ const ordered = [...secrets].filter((s) => s.length > 0).sort((a, b) => b.length - a.length);
10798
+ let result = text;
10799
+ for (const secret of ordered) result = result.replaceAll(secret, "[REDACTED]");
10800
+ return result;
10801
+ }
10802
+
10771
10803
  // src/render/result.ts
10772
10804
  function formatBytes(n) {
10773
10805
  if (n < 1024) return `${n} bytes`;
@@ -14966,15 +14998,7 @@ function isSensitiveHeader(name) {
14966
14998
  return SENSITIVE_HEADER_RE.test(name) || SENSITIVE_NAME_RE.test(name);
14967
14999
  }
14968
15000
  function redact(text, secrets) {
14969
- let result = text;
14970
- for (const secret of secrets) {
14971
- if (secret.length > 0) {
14972
- while (result.includes(secret)) {
14973
- result = result.replace(secret, "[REDACTED]");
14974
- }
14975
- }
14976
- }
14977
- return result;
15001
+ return redactSecrets(text, secrets);
14978
15002
  }
14979
15003
  function createVerboseLogger(platform2, enabled) {
14980
15004
  const secrets = /* @__PURE__ */ new Set();
@@ -16052,7 +16076,7 @@ function expandAndCollect(headers, env2) {
16052
16076
  }
16053
16077
  const e = expandPlaceholders(v, env2);
16054
16078
  result[k] = e;
16055
- if (e !== v) secrets.push(e);
16079
+ if (e !== v) secrets.push(e, ...placeholderValues(v, env2));
16056
16080
  if (v.length > 0) secrets.push(v);
16057
16081
  }
16058
16082
  return { expanded: result, secrets, unset };
@@ -16163,18 +16187,24 @@ async function loadRespondData(platform2, respondArg, json) {
16163
16187
  return { exitCode: EXIT_USAGE };
16164
16188
  }
16165
16189
  }
16166
- async function handleInputRequired(platform2, conn, err, respondData, json) {
16190
+ async function handleInputRequired(platform2, conn, err, respondData, json, redact2) {
16167
16191
  const parsed2 = JSON.parse(err.requestJson);
16192
+ const requestOut = sanitizeJson(JSON.stringify(redactDeep(parsed2, redact2), null, 2));
16168
16193
  const inputReq = parseInputRequest(parsed2.result ?? parsed2);
16169
16194
  if (!inputReq) {
16170
- await platform2.write(2, `${sanitizeJson(err.requestJson)}
16195
+ await platform2.write(2, `${requestOut}
16171
16196
  `);
16172
16197
  return EXIT_INPUT_REQUIRED;
16173
16198
  }
16174
16199
  const isTty = platform2.kind === "host" && platform2.isatty(0) && platform2.isatty(2);
16175
- const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
16200
+ const elicitResult = await handleElicitation(
16201
+ redactWrites(platform2, redact2),
16202
+ inputReq,
16203
+ isTty,
16204
+ respondData
16205
+ );
16176
16206
  if ("exitCode" in elicitResult) {
16177
- await platform2.write(2, `${sanitizeJson(err.requestJson)}
16207
+ await platform2.write(2, `${requestOut}
16178
16208
  `);
16179
16209
  return EXIT_INPUT_REQUIRED;
16180
16210
  }
@@ -16186,7 +16216,7 @@ async function handleInputRequired(platform2, conn, err, respondData, json) {
16186
16216
  return renderCallResult(platform2, retryResult, { json });
16187
16217
  } catch (retryErr) {
16188
16218
  const e2 = classifyError(retryErr);
16189
- await writeErr(platform2, json, e2.kind, e2.msg);
16219
+ await writeErrRedacted(platform2, json, e2.kind, e2.msg, redact2);
16190
16220
  return e2.code;
16191
16221
  }
16192
16222
  }
@@ -16259,17 +16289,7 @@ async function runClientVerb(platform2, version, parsed) {
16259
16289
  const configSecrets = collectConfigSecrets(resolved.headers, platform2.env);
16260
16290
  for (const s of configSecrets) verbose.secrets.add(s);
16261
16291
  const allSecrets = /* @__PURE__ */ new Set([...secrets, ...configSecrets]);
16262
- const redact2 = (text) => {
16263
- let result = text;
16264
- for (const secret of allSecrets) {
16265
- if (secret.length > 0) {
16266
- while (result.includes(secret)) {
16267
- result = result.replace(secret, "[REDACTED]");
16268
- }
16269
- }
16270
- }
16271
- return result;
16272
- };
16292
+ const redact2 = (text) => redactSecrets(text, allSecrets);
16273
16293
  const mergedHeaders = { ...configHeaders, ...expanded };
16274
16294
  const hasAuthHeader = "Authorization" in mergedHeaders;
16275
16295
  const authResolved = await resolveAuthProvider(
@@ -16344,7 +16364,7 @@ async function runClientVerb(platform2, version, parsed) {
16344
16364
  return termExitCode;
16345
16365
  }
16346
16366
  if (err instanceof InputRequiredError) {
16347
- return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput);
16367
+ return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput, redact2);
16348
16368
  }
16349
16369
  const e = classifyError(err);
16350
16370
  await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
@@ -16380,7 +16400,7 @@ function collectConfigSecrets(headers, env2) {
16380
16400
  const secrets = [];
16381
16401
  for (const [, v] of Object.entries(headers)) {
16382
16402
  const expanded = expandPlaceholders(v, env2);
16383
- if (expanded !== v && expanded.length > 0) secrets.push(expanded);
16403
+ if (expanded !== v && expanded.length > 0) secrets.push(expanded, ...placeholderValues(v, env2));
16384
16404
  if (v.length > 0 && v !== expanded) secrets.push(v);
16385
16405
  }
16386
16406
  return secrets;
@@ -16747,7 +16767,7 @@ async function main(ctx) {
16747
16767
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
16748
16768
  });
16749
16769
  const args = ctx.argv.slice(1);
16750
- return run(platform2, "0.17.5", args);
16770
+ return run(platform2, "0.17.7", args);
16751
16771
  }
16752
16772
  export {
16753
16773
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -61,15 +61,15 @@
61
61
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
62
62
  },
63
63
  "devDependencies": {
64
- "@ai-ecoverse/slicc-bios": "1.35.0",
65
- "@ai-ecoverse/slicc-kernel": "1.141.2",
66
- "@ai-ecoverse/slicc-shared-web": "1.29.2",
64
+ "@ai-ecoverse/slicc-bios": "1.36.1",
65
+ "@ai-ecoverse/slicc-kernel": "1.141.3",
66
+ "@ai-ecoverse/slicc-shared-web": "1.29.8",
67
67
  "@biomejs/biome": "2.5.15",
68
68
  "@earendil-works/pi-codemode": "1.2.0",
69
69
  "@earendil-works/pi-mcp": "1.2.0",
70
70
  "@types/node": "26.6.5",
71
71
  "esbuild": "0.28.2",
72
- "kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.141.2",
72
+ "kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.141.3",
73
73
  "lefthook": "2.2.2",
74
74
  "monocart-coverage-reports": "2.13.2",
75
75
  "proper-lockfile": "4.1.2",