@energy8platform/golem 0.6.0 → 0.8.0

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.
Files changed (73) hide show
  1. package/README.md +14 -0
  2. package/dist/editor.css +3 -4
  3. package/dist/editor.js +202 -101
  4. package/dist/lib/cli.js +225 -118
  5. package/dist/lib/cli.js.map +4 -4
  6. package/dist/lib/e8/agent.d.ts +35 -0
  7. package/dist/lib/e8/host.d.ts +22 -0
  8. package/dist/lib/e8/node.d.ts +66 -0
  9. package/dist/lib/e8/runtime.d.ts +10 -0
  10. package/dist/lib/e8/schema.d.ts +3 -0
  11. package/dist/lib/e8/types.d.ts +80 -0
  12. package/dist/lib/e8-agent.js +6865 -0
  13. package/dist/lib/e8-agent.js.map +7 -0
  14. package/dist/lib/e8-client.js +116 -0
  15. package/dist/lib/e8-host.js +7005 -0
  16. package/dist/lib/e8-host.js.map +7 -0
  17. package/dist/lib/e8-runtime.js +2079 -0
  18. package/dist/lib/e8-runtime.js.map +7 -0
  19. package/dist/lib/e8-schema.js +7 -0
  20. package/dist/lib/e8-schema.js.map +7 -0
  21. package/dist/lib/editor/api.d.ts +1 -0
  22. package/dist/lib/editor/embed.d.ts +9 -0
  23. package/dist/lib/editor/server.d.ts +23 -1
  24. package/dist/lib/editor/store.d.ts +34 -32
  25. package/dist/lib/editor/timeline.d.ts +15 -0
  26. package/dist/lib/editor-entry.d.ts +1 -1
  27. package/dist/lib/editor-entry.js +226 -118
  28. package/dist/lib/editor-entry.js.map +4 -4
  29. package/dist/lib/harness.js +54 -1
  30. package/dist/lib/interactive-editor.js.map +1 -1
  31. package/dist/lib/rig-check.d.ts +11 -0
  32. package/dist/lib/rig-control-qa.d.ts +8 -8
  33. package/dist/lib/rig-format.d.ts +184 -184
  34. package/dist/lib/rig-import-layers.d.ts +2 -0
  35. package/dist/lib/rig-symbol.d.ts +5 -2
  36. package/dist/lib/rig-tools.d.ts +2 -1
  37. package/dist/lib/rig-version.d.ts +12 -0
  38. package/dist/lib/runtime.js +60 -3
  39. package/dist/lib/runtime.js.map +3 -3
  40. package/dist/lib/tool-schema.d.ts +3 -0
  41. package/dist/lib/tools.d.ts +2 -0
  42. package/dist/lib/tools.js +179 -58
  43. package/dist/lib/tools.js.map +4 -4
  44. package/editor.html +2 -2
  45. package/package.json +38 -3
  46. package/skills/e8-golem/SKILL.md +71 -0
  47. package/skills/golem-symbol-animation/SKILL.md +87 -0
  48. package/skills/golem-symbol-animation/agents/openai.yaml +4 -0
  49. package/skills/golem-symbol-animation/references/facial-controls.md +48 -0
  50. package/skills/golem-symbol-animation/references/game-engine-integration.md +133 -0
  51. package/skills/golem-symbol-animation/references/golem-authoring.md +138 -0
  52. package/skills/golem-symbol-animation/references/interactive-editor.md +17 -0
  53. package/skills/golem-symbol-animation/references/motion-craft.md +116 -0
  54. package/skills/golem-symbol-animation/references/packed-delivery.md +88 -0
  55. package/skills/golem-symbol-animation/references/quantitative-qa.md +109 -0
  56. package/skills/golem-symbol-animation/references/spine-rive-study.md +87 -0
  57. package/skills/golem-symbol-animation/scripts/atlas_parts.py +87 -0
  58. package/skills/golem-symbol-animation/scripts/check_package.py +71 -0
  59. package/skills/golem-symbol-animation/scripts/image_gates.py +143 -0
  60. package/skills/golem-symbol-cutting/SKILL.md +70 -0
  61. package/skills/golem-symbol-cutting/agents/openai.yaml +4 -0
  62. package/skills/golem-symbol-cutting/assets/h1/h1.atlas.png +0 -0
  63. package/skills/golem-symbol-cutting/assets/h1/h1.png +0 -0
  64. package/skills/golem-symbol-cutting/references/cutting-workflow.md +92 -0
  65. package/skills/golem-symbol-cutting/references/facial-layers.md +44 -0
  66. package/skills/golem-symbol-cutting/references/generation-workflow.md +109 -0
  67. package/skills/golem-symbol-cutting/references/golem-handoff.md +62 -0
  68. package/skills/golem-symbol-cutting/references/h1-example.md +46 -0
  69. package/skills/golem-symbol-cutting/references/hybrid-workflow.md +119 -0
  70. package/skills/golem-symbol-cutting/references/registration-and-motion.md +89 -0
  71. package/skills/golem-symbol-cutting/references/spine-rive-construction.md +73 -0
  72. package/skills/golem-symbol-cutting/scripts/atlas_parts.py +87 -0
  73. package/skills/golem-symbol-cutting/scripts/hybrid_parts.py +279 -0
@@ -0,0 +1,3 @@
1
+ import type { ToolDef } from "./rig-api";
2
+ /** draft 2020-12, every ref inlined, no `$schema` key — the tool's own strict zod object, so unknown keys stay refused */
3
+ export declare function jsonSchemaOf(tool: ToolDef): Record<string, unknown>;
@@ -1,2 +1,4 @@
1
1
  /** `@energy8platform/golem/tools` — the tool registry (node): MCP, CLI and the editor call the same list. */
2
2
  export { tools, callTool, closeTools, type ToolDef, type ToolResult } from "./rig-api";
3
+ export { rigVersion, callToolVersioned } from "./rig-version";
4
+ export { jsonSchemaOf } from "./tool-schema";
package/dist/lib/tools.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/rig-api.ts
2
2
  import { z as z3 } from "zod";
3
3
  import { readFile as readFile9, writeFile as writeFile7, stat as stat2, mkdir as mkdir6, readdir as readdir4 } from "node:fs/promises";
4
- import { dirname as dirname8, join as join9, relative as relative2, isAbsolute } from "node:path";
4
+ import { dirname as dirname8, join as join9, relative as relative3, isAbsolute as isAbsolute2 } from "node:path";
5
5
 
6
6
  // src/rig-io.ts
7
7
  import { mkdir, rename, rm, writeFile } from "node:fs/promises";
@@ -24,8 +24,10 @@ var saveJson = (path, value) => saveBytes(path, JSON.stringify(value, null, 2));
24
24
  // src/rig-queue.ts
25
25
  import { AsyncLocalStorage } from "node:async_hooks";
26
26
  import { resolve } from "node:path";
27
- var pending = /* @__PURE__ */ new Map();
28
- var active = new AsyncLocalStorage();
27
+ var { pending, active } = globalThis[Symbol.for("golem.rigQueue")] ??= {
28
+ pending: /* @__PURE__ */ new Map(),
29
+ active: new AsyncLocalStorage()
30
+ };
29
31
  async function withRigQueue(path, op) {
30
32
  const key = resolve(path);
31
33
  const held = active.getStore();
@@ -291,7 +293,7 @@ var Key = z.object({
291
293
  /** Optional independent easing for numeric-array components (for example Spine RGBA curves). */
292
294
  componentEase: z.array(Ease).min(1).optional()
293
295
  });
294
- var Interpolation = { interpolation: z.enum(["segment", "pchip"]).optional() };
296
+ var Interpolation = { interpolation: z.enum(["segment", "pchip", "spline"]).optional() };
295
297
  var Track = z.discriminatedUnion("target", [
296
298
  z.object({ target: z.literal("control"), id: Id, prop: z.literal("value"), ...Interpolation, keys: z.array(Key).min(1) }),
297
299
  z.object({ target: z.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: z.array(Key).min(1) }),
@@ -725,6 +727,57 @@ function smoothValue(track, i, t, component, loop) {
725
727
  const u = (t - k[i].t) / h(i), u2 = u * u, u3 = u2 * u;
726
728
  return (2 * u3 - 3 * u2 + 1) * v(i) + (u3 - 2 * u2 + u) * h(i) * tangent(i) + (-2 * u3 + 3 * u2) * v(i + 1) + (u3 - u2) * h(i) * tangent(i + 1);
727
729
  }
730
+ function tridiagonal(a, b, c, r) {
731
+ const n = b.length, cp = [], x = [];
732
+ let beta = b[0];
733
+ x[0] = r[0] / beta;
734
+ for (let i = 1; i < n; i++) {
735
+ cp[i] = c[i - 1] / beta;
736
+ beta = b[i] - a[i] * cp[i];
737
+ x[i] = (r[i] - a[i] * x[i - 1]) / beta;
738
+ }
739
+ for (let i = n - 2; i >= 0; i--) x[i] -= cp[i + 1] * x[i + 1];
740
+ return x;
741
+ }
742
+ function splineMoments(t, v, periodic) {
743
+ const n = t.length, h = (j2) => t[j2 + 1] - t[j2], d = (j2) => (v[j2 + 1] - v[j2]) / h(j2);
744
+ if (!periodic) {
745
+ const a2 = [], b2 = [], c2 = [], r2 = [];
746
+ for (let i = 0; i < n; i++) {
747
+ const hp = i > 0 ? h(i - 1) : 0, hn = i < n - 1 ? h(i) : 0;
748
+ a2.push(hp);
749
+ b2.push(2 * (hp + hn));
750
+ c2.push(hn);
751
+ r2.push(6 * ((i < n - 1 ? d(i) : 0) - (i > 0 ? d(i - 1) : 0)));
752
+ }
753
+ return tridiagonal(a2, b2, c2, r2);
754
+ }
755
+ const m = n - 1, a = [], b = [], c = [], r = [];
756
+ for (let i = 0; i < m; i++) {
757
+ const p = (i + m - 1) % m;
758
+ a.push(h(p));
759
+ b.push(2 * (h(p) + h(i)));
760
+ c.push(h(i));
761
+ r.push(6 * (d(i) - d(p)));
762
+ }
763
+ const top = a[0], bottom = c[m - 1], gamma = -b[0];
764
+ const bb = [...b];
765
+ bb[0] -= gamma;
766
+ bb[m - 1] -= bottom * top / gamma;
767
+ const x = tridiagonal(a, bb, c, r), u = new Array(m).fill(0);
768
+ u[0] = gamma;
769
+ u[m - 1] = bottom;
770
+ const z4 = tridiagonal(a, bb, c, u);
771
+ const f = (x[0] + top * x[m - 1] / gamma) / (1 + z4[0] + top * z4[m - 1] / gamma);
772
+ const M = x.map((xi, i) => xi - f * z4[i]);
773
+ return [...M, M[0]];
774
+ }
775
+ function splineValue(track, i, t, component, loop) {
776
+ const k = track.keys, ts = k.map((x) => x.t), vs = k.map((x) => component === void 0 ? x.v : x.v[component]);
777
+ const M = splineMoments(ts, vs, loop && k.length > 3 && vs[0] === vs[k.length - 1]);
778
+ const H = ts[i + 1] - ts[i], A = (ts[i + 1] - t) / H, B = (t - ts[i]) / H;
779
+ return A * vs[i] + B * vs[i + 1] + ((A ** 3 - A) * M[i] + (B ** 3 - B) * M[i + 1]) * H * H / 6;
780
+ }
728
781
  function sample(track, t, loop = false) {
729
782
  const keys = track.keys;
730
783
  if (t <= keys[0].t) return keys[0].v;
@@ -736,11 +789,13 @@ function sample(track, t, loop = false) {
736
789
  if (a.ease === "step") return a.v;
737
790
  if (typeof a.v === "number" && typeof b.v === "number") {
738
791
  if (track.interpolation === "pchip") return smoothValue(track, i, t, void 0, loop);
792
+ if (track.interpolation === "spline") return splineValue(track, i, t, void 0, loop);
739
793
  const u = ease(a.ease)((t - a.t) / (b.t - a.t));
740
794
  return a.v + (b.v - a.v) * u;
741
795
  }
742
796
  if (Array.isArray(a.v) && Array.isArray(b.v) && typeof a.v[0] === "number") {
743
797
  if (track.interpolation === "pchip") return a.v.map((_, j2) => smoothValue(track, i, t, j2, loop));
798
+ if (track.interpolation === "spline") return a.v.map((_, j2) => splineValue(track, i, t, j2, loop));
744
799
  const phase = (t - a.t) / (b.t - a.t), av = a.v, bv = b.v;
745
800
  return av.map((v, k) => v + ((bv[k] ?? v) - v) * ease(a.componentEase?.[k] ?? a.ease)(phase));
746
801
  }
@@ -2001,8 +2056,9 @@ function createDoc(name, width, height, notes) {
2001
2056
  function createSymbol(name, asset, margin = 0.15, notes) {
2002
2057
  const mx = Math.ceil(asset.width * margin), my = Math.ceil(asset.height * margin);
2003
2058
  const width = asset.width + 2 * mx, height = asset.height + 2 * my;
2004
- const fromFile = asset.src.replace(/^.*\//, "").replace(/\.[^.]+$/, "").toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/^[^a-z]+/, "");
2005
- const id = asset.id ?? (fromFile || name);
2059
+ const stem = asset.src.replace(/^.*\//, "").replace(/\.[^.]+$/, "").toLowerCase().replace(/[^a-z0-9_]/g, "_");
2060
+ const fromFile = stem.replace(/^[^a-z]+/, "") || (stem ? `symbol_${stem}` : "");
2061
+ const id = asset.id ?? (ID_RE.test(fromFile) ? fromFile : ID_RE.test(name) ? name : "symbol");
2006
2062
  let doc = createDoc(name, width, height, notes);
2007
2063
  doc = { ...doc, bones: [{ ...doc.bones[0], transform: { ...doc.bones[0].transform, x: width / 2, y: height - my } }] };
2008
2064
  doc = { ...doc, assets: [RigSchema.shape.assets.element.parse({ id, src: asset.src, width: asset.width, height: asset.height })] };
@@ -2471,7 +2527,8 @@ function adjustTrack(doc, animId, track, adj) {
2471
2527
  return { ...k, t: r42(adj.timeScale ? k.t * adj.timeScale : k.t), v: r42(v) };
2472
2528
  });
2473
2529
  const duration = Math.max(anim.duration, keys[keys.length - 1].t);
2474
- const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys } : t), preset: void 0 };
2530
+ const interpolation = track.interpolation ?? tr.interpolation;
2531
+ const next = { ...anim, duration, tracks: anim.tracks.map((t) => same(t) ? { ...t, keys, ...interpolation && { interpolation } } : t), preset: void 0 };
2475
2532
  return { ...doc, animations: doc.animations.map((a) => a.id === animId ? next : a) };
2476
2533
  }
2477
2534
  function removeAnimation(doc, id) {
@@ -2758,7 +2815,7 @@ function setGoal(doc, bone, x, y, opts) {
2758
2815
 
2759
2816
  // src/rig-import-layers.ts
2760
2817
  import { readdir, readFile } from "node:fs/promises";
2761
- import { join as join2, basename as basename2 } from "node:path";
2818
+ import { join as join2, basename as basename2, isAbsolute, relative, sep } from "node:path";
2762
2819
  var DEFAULT_PARENT = {
2763
2820
  head: "neck",
2764
2821
  neck: "torso",
@@ -2784,6 +2841,10 @@ function pngSize(buf) {
2784
2841
  if (buf.readUInt32BE(12) !== 1229472850) throw new Error("not a PNG (IHDR missing)");
2785
2842
  return [buf.readUInt32BE(16), buf.readUInt32BE(20)];
2786
2843
  }
2844
+ function outsideDir(dir, file) {
2845
+ const r = relative(dir, join2(dir, file));
2846
+ return isAbsolute(file) || r === ".." || r.startsWith(".." + sep);
2847
+ }
2787
2848
  async function importLayers(doc, dir, relPrefix = "layers") {
2788
2849
  let specs;
2789
2850
  try {
@@ -2796,6 +2857,7 @@ async function importLayers(doc, dir, relPrefix = "layers") {
2796
2857
  let next = doc;
2797
2858
  const parsed = [];
2798
2859
  for (const s of specs) {
2860
+ if (outsideDir(dir, s.file)) throw new Error(`layers.json file "${s.file}" is outside the layers folder (${dir})`);
2799
2861
  const buf = await readFile(join2(dir, s.file));
2800
2862
  const [w, h] = pngSize(buf);
2801
2863
  const m = basename2(s.file, ".png").match(/^(?:(\d+)_)?(.+)$/);
@@ -2883,19 +2945,20 @@ var requireSlot = (doc, id) => {
2883
2945
  if (!s) throw new Error(`unknown slot "${id}"`);
2884
2946
  return s;
2885
2947
  };
2886
- function sampled(duration, n, f, ease2 = "linear") {
2948
+ function sampled(duration, n, f) {
2887
2949
  const keys = [];
2888
2950
  for (let i = 0; i <= n; i++) {
2889
2951
  const t = +(duration * i / n).toFixed(4);
2890
- keys.push({ t, v: +f(t).toFixed(4) + 0, ease: ease2 });
2952
+ keys.push({ t, v: +f(t).toFixed(4) + 0, ease: "linear" });
2891
2953
  }
2892
2954
  return keys;
2893
2955
  }
2894
- function boneTrack(bone, prop, keys) {
2895
- return { target: "bone", id: bone.id, prop, keys };
2956
+ var smooth = "spline";
2957
+ function boneTrack(bone, prop, keys, interpolation) {
2958
+ return { target: "bone", id: bone.id, prop, keys, ...interpolation && { interpolation } };
2896
2959
  }
2897
- function deformTrack(slot, prop, keys) {
2898
- return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys } : void 0;
2960
+ function deformTrack(slot, prop, keys, interpolation) {
2961
+ return slot.attachment ? { target: "deform", id: slot.attachment, prop, keys, ...interpolation && { interpolation } } : void 0;
2899
2962
  }
2900
2963
  var pushTrack = (tracks, t) => {
2901
2964
  if (t) tracks.push(t);
@@ -2919,18 +2982,18 @@ var idleBreathing = {
2919
2982
  const armsR = byRole(doc, "arm_r", "arm_upper_r");
2920
2983
  const chest = slotOfBone(doc, torso);
2921
2984
  if (chest?.attachment) {
2922
- pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t), "ease_in_out")));
2985
+ pushTrack(tracks, deformTrack(chest, "bulge", sampled(T2, 8, (t) => p.amplitude * Math.sin(w * t)), smooth));
2923
2986
  } else {
2924
2987
  const r = torso.transform;
2925
- tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t), "ease_in_out")));
2926
- tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t), "ease_in_out")));
2988
+ tracks.push(boneTrack(torso, "scaleY", sampled(T2, 8, (t) => r.scaleY + p.amplitude * Math.sin(w * t)), smooth));
2989
+ tracks.push(boneTrack(torso, "scaleX", sampled(T2, 8, (t) => r.scaleX - p.amplitude * 0.4 * Math.sin(w * t)), smooth));
2927
2990
  }
2928
2991
  for (const h of heads)
2929
- tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4), "ease_in_out")));
2992
+ tracks.push(boneTrack(h, "y", sampled(T2, 8, (t) => h.transform.y - p.headBob * Math.sin(w * t + 0.4)), smooth));
2930
2993
  for (const a of armsL)
2931
- tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8), "ease_in_out")));
2994
+ tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation + p.armSway * Math.sin(w * t + 0.8)), smooth));
2932
2995
  for (const a of armsR)
2933
- tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8), "ease_in_out")));
2996
+ tracks.push(boneTrack(a, "rotation", sampled(T2, 8, (t) => a.transform.rotation - p.armSway * Math.sin(w * t + 0.8)), smooth));
2934
2997
  return finish(this.name, p.id, T2, true, tracks, p);
2935
2998
  }
2936
2999
  };
@@ -2939,22 +3002,20 @@ var winBounce = {
2939
3002
  defaults: { id: "win", duration: 1.2, height: 40, bounces: 2, squash: 0.15 },
2940
3003
  build(doc, p) {
2941
3004
  const b = root(doc), r = b.transform, tracks = [];
2942
- const yK = [[0, r.y, "ease_out"]];
2943
- const sxK = [[0, r.scaleX, "ease_out"]];
2944
- const syK = [[0, r.scaleY, "ease_out"]];
3005
+ const yK = [[0, r.y]], sxK = [[0, r.scaleX]], syK = [[0, r.scaleY]];
2945
3006
  const per = p.duration / p.bounces;
2946
3007
  for (let i = 0; i < p.bounces; i++) {
2947
3008
  const t0 = i * per, h = p.height * 0.55 ** i, s = p.squash * 0.7 ** i;
2948
- sxK.push([t0 + per * 0.08, r.scaleX * (1 + s), "ease_out"], [t0 + per * 0.5, r.scaleX * (1 - s * 0.6), "ease_in"], [t0 + per * 0.95, r.scaleX * (1 + s), "ease_out"]);
2949
- syK.push([t0 + per * 0.08, r.scaleY * (1 - s), "ease_out"], [t0 + per * 0.5, r.scaleY * (1 + s * 0.6), "ease_in"], [t0 + per * 0.95, r.scaleY * (1 - s), "ease_out"]);
2950
- yK.push([t0 + per * 0.08, r.y, "ease_out"], [t0 + per * 0.5, r.y - h, "ease_in"], [t0 + per * 0.95, r.y, "ease_out"]);
3009
+ sxK.push([t0 + per * 0.08, r.scaleX * (1 + s)], [t0 + per * 0.5, r.scaleX * (1 - s * 0.6)], [t0 + per * 0.95, r.scaleX * (1 + s)]);
3010
+ syK.push([t0 + per * 0.08, r.scaleY * (1 - s)], [t0 + per * 0.5, r.scaleY * (1 + s * 0.6)], [t0 + per * 0.95, r.scaleY * (1 - s)]);
3011
+ yK.push([t0 + per * 0.08, r.y], [t0 + per * 0.5, r.y - h], [t0 + per * 0.95, r.y]);
2951
3012
  }
2952
3013
  yK.push([p.duration, r.y]);
2953
3014
  sxK.push([p.duration, r.scaleX]);
2954
3015
  syK.push([p.duration, r.scaleY]);
2955
- tracks.push(boneTrack(b, "y", K(...yK)), boneTrack(b, "scaleX", K(...sxK)), boneTrack(b, "scaleY", K(...syK)));
3016
+ tracks.push(boneTrack(b, "y", K(...yK), "pchip"), boneTrack(b, "scaleX", K(...sxK), smooth), boneTrack(b, "scaleY", K(...syK), smooth));
2956
3017
  for (const h of byRole(doc, "head"))
2957
- tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6, "back_out"], [p.duration * 0.6, h.transform.rotation + 4], [p.duration, h.transform.rotation])));
3018
+ tracks.push(boneTrack(h, "rotation", K([0, h.transform.rotation], [p.duration * 0.25, h.transform.rotation - 6], [p.duration * 0.6, h.transform.rotation + 4], [p.duration, h.transform.rotation]), smooth));
2958
3019
  const anim = finish(this.name, p.id, p.duration, false, tracks, p);
2959
3020
  anim.events.push({ t: 0, name: "win_start" }, { t: per * 0.95, name: "land" });
2960
3021
  return anim;
@@ -2967,8 +3028,8 @@ var anticipationShake = {
2967
3028
  const b = root(doc), r = b.transform, tracks = [];
2968
3029
  const n = Math.ceil(p.duration * p.frequency * 4);
2969
3030
  const env = (t) => p.grow ? t / p.duration : 1;
2970
- tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t), "linear")));
2971
- tracks.push(boneTrack(b, "rotation", sampled(p.duration, n, (t) => r.rotation + 0.6 * p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t + 1.2), "linear")));
3031
+ tracks.push(boneTrack(b, "x", sampled(p.duration, n, (t) => r.x + p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t)), smooth));
3032
+ tracks.push(boneTrack(b, "rotation", sampled(p.duration, n, (t) => r.rotation + 0.6 * p.amplitude * env(t) * Math.sin(2 * Math.PI * p.frequency * t + 1.2)), smooth));
2972
3033
  tracks.push(boneTrack(b, "scaleX", K([0, r.scaleX, "ease_in"], [p.duration, r.scaleX * p.scaleUp])));
2973
3034
  tracks.push(boneTrack(b, "scaleY", K([0, r.scaleY, "ease_in"], [p.duration, r.scaleY * p.scaleUp])));
2974
3035
  return finish(this.name, p.id, p.duration, false, tracks, p);
@@ -2993,6 +3054,7 @@ var mouthTalk = {
2993
3054
  if (!p.frames.length) throw new Error("mouth_talk: frames must not be empty");
2994
3055
  const n = Math.max(1, Math.round(p.duration * p.rate));
2995
3056
  const keys = S(...Array.from({ length: n }, (_, i) => [+(i / p.rate).toFixed(4), p.frames[i % p.frames.length]]));
3057
+ if (keys.at(-1).t < p.duration && keys.at(-1).v !== keys[0].v) keys.push(...S([p.duration, keys[0].v]));
2996
3058
  return finish(this.name, p.id, p.duration, true, [slotTrack(slot, "attachment", keys)], p);
2997
3059
  }
2998
3060
  };
@@ -3010,10 +3072,10 @@ var symbolIdle = {
3010
3072
  build(doc, p) {
3011
3073
  const b = root(doc), r = b.transform, T2 = p.period, w = 2 * Math.PI / T2, tracks = [];
3012
3074
  const rise = (t) => 0.5 - 0.5 * Math.cos(w * t);
3013
- tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t), "ease_in_out")));
3014
- if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t), "ease_in_out")));
3075
+ tracks.push(boneTrack(b, "y", sampled(T2, 8, (t) => r.y - p.float * rise(t)), smooth));
3076
+ if (p.tilt) tracks.push(boneTrack(b, "rotation", sampled(T2, 8, (t) => r.rotation + p.tilt * Math.sin(w * t)), smooth));
3015
3077
  if (p.pulse) for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment))
3016
- pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t), "ease_in_out")));
3078
+ pushTrack(tracks, deformTrack(s, "bulge", sampled(T2, 8, (t) => p.pulse * rise(t)), smooth));
3017
3079
  return finish(this.name, p.id, T2, true, tracks, p);
3018
3080
  }
3019
3081
  };
@@ -3023,12 +3085,12 @@ var symbolWin = {
3023
3085
  build(doc, p) {
3024
3086
  const b = root(doc), r = b.transform, d = p.duration, tracks = [];
3025
3087
  for (const [prop, rest] of [["scaleX", r.scaleX], ["scaleY", r.scaleY]])
3026
- tracks.push(boneTrack(b, prop, K([0, rest, "ease_out"], [d * 0.2, rest * (1 + p.pop), "ease_in_out"], [d * 0.5, rest * (1 - p.pop * 0.3), "ease_out"], [d, rest])));
3088
+ tracks.push(boneTrack(b, prop, K([0, rest], [d * 0.2, rest * (1 + p.pop)], [d * 0.5, rest * (1 - p.pop * 0.3)], [d, rest]), smooth));
3027
3089
  if (p.wobble) {
3028
3090
  const n = Math.max(8, p.wobbles * 4);
3029
- const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d), "ease_in_out");
3091
+ const keys = sampled(d, n, (t) => p.wobble * Math.exp(-3 * t / d) * Math.sin(2 * Math.PI * p.wobbles * t / d));
3030
3092
  keys[keys.length - 1].v = 0;
3031
- for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys));
3093
+ for (const s of doc.slots.filter((s2) => s2.bone === b.id && s2.attachment)) pushTrack(tracks, deformTrack(s, "sway", keys, smooth));
3032
3094
  }
3033
3095
  const anim = finish(this.name, p.id, d, false, tracks, p);
3034
3096
  anim.events.push({ t: 0, name: "win_start" });
@@ -3042,11 +3104,11 @@ var weightShift = {
3042
3104
  const T2 = p.period, w = 2 * Math.PI / T2, N = 16, tracks = [];
3043
3105
  const hips = byRole(doc, "hips")[0] ?? root(doc), hr = hips.transform;
3044
3106
  const s = (t) => Math.sin(w * t);
3045
- tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t))));
3046
- if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2)));
3107
+ tracks.push(boneTrack(hips, "x", sampled(T2, N, (t) => hr.x + p.shift * s(t)), smooth));
3108
+ if (p.dip) tracks.push(boneTrack(hips, "y", sampled(T2, N, (t) => hr.y + p.dip * s(t) ** 2), smooth));
3047
3109
  const torso = byRole(doc, "torso", "body")[0];
3048
- if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t))));
3049
- for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t))));
3110
+ if (torso && torso !== hips) tracks.push(boneTrack(torso, "rotation", sampled(T2, N, (t) => torso.transform.rotation - p.lean * s(t)), smooth));
3111
+ for (const h of byRole(doc, "head")) tracks.push(boneTrack(h, "rotation", sampled(T2, N, (t) => h.transform.rotation + p.lean / 2 * s(t)), smooth));
3050
3112
  for (const leg of byRole(doc, "leg_l", "leg_r")) {
3051
3113
  const slot = slotOfBone(doc, leg), att = slot && doc.attachments.find((a) => a.id === slot.attachment && a.type === "region");
3052
3114
  const asset = att && att.type === "region" ? doc.assets.find((a) => a.id === att.asset) : void 0;
@@ -3056,8 +3118,8 @@ var weightShift = {
3056
3118
  length = Math.max(...[[0, -h / 2], [w2 / 2, 0], [0, h / 2], [-w2 / 2, 0]].map(([x, y]) => Math.hypot(...apply(m, x, y))));
3057
3119
  }
3058
3120
  if (!(length > p.shift)) continue;
3059
- tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI)));
3060
- if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2)));
3121
+ tracks.push(boneTrack(leg, "rotation", sampled(T2, N, (t) => leg.transform.rotation + Math.asin(p.shift * s(t) / length) * 180 / Math.PI), smooth));
3122
+ if (p.dip) tracks.push(boneTrack(leg, "y", sampled(T2, N, (t) => leg.transform.y - p.dip * s(t) ** 2), smooth));
3061
3123
  }
3062
3124
  return finish(this.name, p.id, T2, true, tracks, p);
3063
3125
  }
@@ -3300,13 +3362,13 @@ function motionQuality(doc, anim, opts = {}) {
3300
3362
  const before = delta(values(k.v), values(p.v)), after = delta(values(n.v), values(k.v));
3301
3363
  const pass = before.some((v, j2) => v * after[j2] > 1e-6);
3302
3364
  const speed = Math.max(magnitude(before) / (k.t - p.t), magnitude(after) / (n.t - k.t));
3303
- if (pass && Math.max(magnitude(left), magnitude(right)) < speed * 0.02) report("pass_key_stop", id, `motion stops at a pass-through key; use track interpolation pchip or matched tangents`, k.t);
3365
+ if (pass && Math.max(magnitude(left), magnitude(right)) < speed * 0.02) report("pass_key_stop", id, `motion stops at a pass-through key; use track interpolation pchip or spline, or matched tangents`, k.t);
3304
3366
  const jump = magnitude(delta(left, right));
3305
3367
  if (jump > Math.max(0.1, speed * 0.1)) report("velocity_jump", id, `velocity jumps ${jump.toFixed(3)} units/s across this key`, k.t);
3306
3368
  const acceleration = (a, b, c) => values(at(b)).map((v, j2) => (values(at(c))[j2] - 2 * v + values(at(a))[j2]) / (h * h));
3307
3369
  const al = acceleration(k.t - 2 * h, k.t - h, k.t), ar = acceleration(k.t, k.t + h, k.t + 2 * h);
3308
3370
  const aj = magnitude(delta(al, ar));
3309
- if (aj > Math.max(1, speed / Math.min(k.t - p.t, n.t - k.t) * 2)) report("acceleration_jump", id, `acceleration changes by ${aj.toFixed(2)} units/s\xB2 at this key; PCHIP guarantees velocity, not acceleration continuity`, k.t, "info");
3371
+ if (aj > Math.max(1, speed / Math.min(k.t - p.t, n.t - k.t) * 2)) report("acceleration_jump", id, `acceleration changes by ${aj.toFixed(2)} units/s\xB2 at this key; PCHIP guarantees velocity, not acceleration continuity \u2014 spline does both`, k.t, "info");
3310
3372
  }
3311
3373
  }
3312
3374
  const steps = Math.max(2, Math.ceil(D * 60));
@@ -3788,6 +3850,17 @@ function checkRig(doc, opts = {}) {
3788
3850
  out.push({ level: "warn", code: "never_visible", subject: s.id, message: `slot "${s.id}" has no attachment and no animation ever shows one` });
3789
3851
  return out;
3790
3852
  }
3853
+ function summarizeIgnoring(findings, rules, max) {
3854
+ const hits = (r, f) => r.code === f.code && (r.subject ?? f.subject) === f.subject && (r.animation ?? f.animation) === f.animation;
3855
+ const kept = findings.filter((f) => !rules.some((r) => hits(r, f)));
3856
+ const idle = rules.filter((r) => !findings.some((f) => hits(r, f)));
3857
+ const notes = [
3858
+ ...kept.length < findings.length ? [` (${findings.length - kept.length} ignored)`] : [],
3859
+ ...idle.map((r) => ` ignore matched nothing: ${JSON.stringify(r)}`)
3860
+ ];
3861
+ return [summarize(kept, max), ...notes].join("\n");
3862
+ }
3863
+ var where = (f) => `${f.subject}${f.animation ? ` in ${f.animation}` : ""}${f.time !== void 0 ? ` @${+f.time.toFixed(4)}s` : ""}`;
3791
3864
  function summarize(findings, max = 12) {
3792
3865
  if (!findings.length) return "check: OK";
3793
3866
  const rank = { error: 0, warn: 1, info: 2 };
@@ -3802,7 +3875,7 @@ function summarize(findings, max = 12) {
3802
3875
  const lines = [];
3803
3876
  for (const f of sorted) {
3804
3877
  const shown = shownPerCode.get(f.code) ?? 0;
3805
- if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code}: ${f.message}`);
3878
+ if (shown < PER_CODE) lines.push(` [${f.level}] ${f.code} ${where(f)}: ${f.message}`);
3806
3879
  else if (shown === PER_CODE) lines.push(` \u2026 ${codeTotal.get(f.code) - PER_CODE} more ${f.code}`);
3807
3880
  shownPerCode.set(f.code, shown + 1);
3808
3881
  }
@@ -3993,7 +4066,7 @@ function packRects(rects, opts = {}) {
3993
4066
  shelf = Math.max(shelf, r.height);
3994
4067
  }
3995
4068
  const height = y + shelf + pad;
3996
- if (height <= max) return { width, height, frames };
4069
+ if (height <= max) return { width: Math.max(pad, ...[...frames.values()].map(([fx, , fw]) => fx + fw)) + pad, height, frames };
3997
4070
  if (width >= max) throw new Error(`atlas does not fit in ${max}\xD7${max} (needs ${width}\xD7${height})`);
3998
4071
  width *= 2;
3999
4072
  }
@@ -4052,7 +4125,7 @@ async function buildAtlas(doc, assetsDir, outDir, opts = {}) {
4052
4125
 
4053
4126
  // src/spine-import.ts
4054
4127
  import { readFile as readFile7, stat, writeFile as writeFile5 } from "node:fs/promises";
4055
- import { basename as basename4, dirname as dirname6, extname, join as join8, relative } from "node:path";
4128
+ import { basename as basename4, dirname as dirname6, extname, join as join8, relative as relative2 } from "node:path";
4056
4129
 
4057
4130
  // src/ktx2.ts
4058
4131
  import { readFile as readFile6 } from "node:fs/promises";
@@ -4858,6 +4931,7 @@ async function importSpineProject(opts) {
4858
4931
  const rigDir = dirname6(opts.rigPath);
4859
4932
  const pagePaths = /* @__PURE__ */ new Map();
4860
4933
  for (const page2 of pages) {
4934
+ if (!opts.image && outsideDir(dirname6(atlasPath), page2.name)) throw new Error(`atlas page "${page2.name}" is outside the atlas folder (${dirname6(atlasPath)})`);
4861
4935
  const pageStem = join8(dirname6(atlasPath), page2.name.replace(/\.[a-z0-9]+$/i, ""));
4862
4936
  const imagePath = opts.image ?? await firstExisting([join8(dirname6(atlasPath), page2.name), pageStem + ".png", pageStem + ".ktx2", ...pages.length === 1 ? [stem + ".png", stem + ".ktx2"] : []], `atlas page image ${page2.name}`);
4863
4937
  let pngPath = imagePath;
@@ -4870,7 +4944,7 @@ async function importSpineProject(opts) {
4870
4944
  }
4871
4945
  pagePaths.set(page2.name, pngPath);
4872
4946
  }
4873
- const pageImage = (page2) => relative(rigDir, pagePaths.get(page2)).split("\\").join("/");
4947
+ const pageImage = (page2) => relative2(rigDir, pagePaths.get(page2)).split("\\").join("/");
4874
4948
  const name = opts.name ?? basename4(stem);
4875
4949
  const imagePaths = [...pagePaths.values()];
4876
4950
  return { ...importSpine(json, atlasText, { name, skin: opts.skin, pageImage, margin: opts.margin, notes: opts.notes }), imagePath: imagePaths[0], imagePaths };
@@ -5563,8 +5637,8 @@ async function load(path) {
5563
5637
  }
5564
5638
  var save = (path, doc) => saveJson(path, doc);
5565
5639
  var exists2 = (p) => stat2(p).then(() => true, () => false);
5566
- var resolveFrom = (rigPath, p) => isAbsolute(p) ? p : join9(dirname8(rigPath), p);
5567
- var srcFor = (rigPath, file) => relative2(dirname8(rigPath), file).split("\\").join("/");
5640
+ var resolveFrom = (rigPath, p) => isAbsolute2(p) ? p : join9(dirname8(rigPath), p);
5641
+ var srcFor = (rigPath, file) => relative3(dirname8(rigPath), file).split("\\").join("/");
5568
5642
  var libraryFor = (rigPath, lib) => lib ? resolveFrom(rigPath, lib) : join9(dirname8(rigPath), "presets.json");
5569
5643
  async function pngInfo(rigPath, file) {
5570
5644
  const abs = resolveFrom(rigPath, file);
@@ -5599,7 +5673,8 @@ function previewCheck(doc, animation) {
5599
5673
  const a = animation ? doc.animations.find((x) => x.id === animation) : void 0;
5600
5674
  return a ? `check: OK for "${a.id}" (${a.tracks.length} tracks, ${a.duration}s${a.loop ? " loop" : ""})` : "check: OK (rest pose)";
5601
5675
  }
5602
- var TrackRef = { interpolation: z3.enum(["segment", "pchip"]).optional().describe("pchip: smooth through numeric keys, preserving extrema; segment: per-key easing"), target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
5676
+ var TrackId = { target: z3.enum(["bone", "slot", "deform", "ik", "transform", "path", "drawOrder", "control"]), id: z3.string(), prop: z3.string() };
5677
+ var TrackRef = { interpolation: z3.enum(["segment", "pchip", "spline"]).optional().describe("segment: per-key easing; pchip: smooth through numeric keys, never past them (continuous velocity); spline: C2 cubic through them, continuous acceleration too, may overshoot between keys \u2014 for sampled curves"), ...TrackId };
5603
5678
  tool(
5604
5679
  "rig_set_control",
5605
5680
  "Create or replace a semantic control with a finite min/max/default range. Animate target=control, prop=value. Changing a range requires matching binding grids.",
@@ -5792,11 +5867,21 @@ tool(
5792
5867
  );
5793
5868
  tool(
5794
5869
  "rig_check",
5795
- "Cheap self-critique without rendering: out_of_canvas (slot leaves the canvas, worst time), static_track, loop_seam, deform_flip (lattice folds over itself \u2014 an error), mesh_fold (a skinned/deformed mesh folds over itself: error when \u2265 5 % of the mesh folds, warning below \u2014 a dense mesh bent hard at a joint pinches the odd hairline triangle), weights_sum (skinned mesh vertex weights not summing to 1), patch_offset (a slot sits away from its asset's source bbox), rest_overlap, unused_asset, never_visible, and for constraints: constraint_dead (every mix is 0 at rest and no track lifts one \u2014 the solver skips it), constraint_cycle (the target is a bone the constraint drives, or hangs off one: it reads what it writes \u2014 an error), ik_unreachable (the target sits beyond the chain's reach for over 20 % of an animation, so the limb hangs straight), path_missing (a path constraint whose target slot never shows a path attachment \u2014 an error). Motion diagnostics also cover pass_key_stop, velocity_jump, acceleration_jump, loop_velocity and constraint_order; provide secondaryBones, frontOf slot pairs and named attachment UV landmarks for brief-specific frozen-motion, ordering and swap-registration checks. Findings are sorted by severity. Run it before spending vision tokens on a preview.",
5796
- { path: Path, animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"), fps: z3.number().positive().optional(), deformFps: z3.number().positive().optional(), secondaryBones: z3.array(z3.string()).optional(), frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(), landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(), landmarkTolerance: z3.number().nonnegative().optional() },
5797
- async ({ path, ...opts }) => {
5870
+ "Cheap self-critique without rendering: out_of_canvas (slot leaves the canvas, worst time), static_track, loop_seam, deform_flip (lattice folds over itself \u2014 an error), mesh_fold (a skinned/deformed mesh folds over itself: error when \u2265 5 % of the mesh folds, warning below \u2014 a dense mesh bent hard at a joint pinches the odd hairline triangle), weights_sum (skinned mesh vertex weights not summing to 1), patch_offset (a slot sits away from its asset's source bbox), rest_overlap, unused_asset, never_visible, and for constraints: constraint_dead (every mix is 0 at rest and no track lifts one \u2014 the solver skips it), constraint_cycle (the target is a bone the constraint drives, or hangs off one: it reads what it writes \u2014 an error), ik_unreachable (the target sits beyond the chain's reach for over 20 % of an animation, so the limb hangs straight), path_missing (a path constraint whose target slot never shows a path attachment \u2014 an error). Motion diagnostics also cover pass_key_stop, velocity_jump, acceleration_jump, loop_velocity and constraint_order; provide secondaryBones, frontOf slot pairs and named attachment UV landmarks for brief-specific frozen-motion, ordering and swap-registration checks. Findings are sorted by severity; each line names its subject (a track as target.id.prop), animation and time. `ignore` drops findings you have read and accept \u2014 by code, narrowed by subject and animation; the reply counts them and names a rule that matched nothing. Run it before spending vision tokens on a preview.",
5871
+ {
5872
+ path: Path,
5873
+ animation: z3.string().optional().describe("limit to one animation (rest pose is always checked)"),
5874
+ fps: z3.number().positive().optional(),
5875
+ deformFps: z3.number().positive().optional(),
5876
+ secondaryBones: z3.array(z3.string()).optional(),
5877
+ frontOf: z3.array(z3.tuple([z3.string(), z3.string()])).optional(),
5878
+ landmarks: z3.record(z3.record(z3.tuple([z3.number(), z3.number()]))).optional(),
5879
+ landmarkTolerance: z3.number().nonnegative().optional(),
5880
+ ignore: z3.array(z3.object({ code: z3.string(), subject: z3.string().optional(), animation: z3.string().optional() }).strict()).optional()
5881
+ },
5882
+ async ({ path, ignore = [], ...opts }) => {
5798
5883
  try {
5799
- return text(summarize(checkRig(await load(path), opts), 100));
5884
+ return text(summarizeIgnoring(checkRig(await load(path), opts), ignore, 100));
5800
5885
  } catch (e) {
5801
5886
  return fail(e);
5802
5887
  }
@@ -6413,9 +6498,9 @@ tool(
6413
6498
  );
6414
6499
  tool(
6415
6500
  "rig_adjust_track",
6416
- "Re-tune an existing track without retyping keys: amplitude scales its deviation from the rest value (0.5 = half as strong, 2 = twice; scale props as ratios), offset adds a constant, timeScale stretches its key times (duration grows to fit). Drops the preset link.",
6501
+ "Re-tune an existing track without retyping keys: amplitude scales its deviation from the rest value (0.5 = half as strong, 2 = twice; scale props as ratios), offset adds a constant, timeScale stretches its key times (duration grows to fit), interpolation switches segment / pchip / spline. Drops the preset link.",
6417
6502
  { path: Path, animation: z3.string(), ...TrackRef, amplitude: z3.number().optional(), offset: z3.number().optional(), timeScale: z3.number().positive().optional() },
6418
- async ({ path, animation, target, id, prop, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop }, adj))
6503
+ async ({ path, animation, target, id, prop, interpolation, ...adj }) => mutate(path, (d) => adjustTrack(d, animation, { target, id, prop, interpolation }, adj))
6419
6504
  );
6420
6505
  tool(
6421
6506
  "rig_set_animation",
@@ -6433,7 +6518,7 @@ tool(
6433
6518
  tool(
6434
6519
  "rig_remove_keys",
6435
6520
  "Remove the keys of one track at the given times; an emptied track is dropped. Drops the preset link.",
6436
- { path: Path, animation: z3.string(), ...TrackRef, times: z3.array(z3.number()).min(1) },
6521
+ { path: Path, animation: z3.string(), ...TrackId, times: z3.array(z3.number()).min(1) },
6437
6522
  async ({ path, animation, target, id, prop, times: times2 }) => mutate(path, (d) => removeKeys(d, animation, { target, id, prop }, times2))
6438
6523
  );
6439
6524
  tool(
@@ -6588,9 +6673,45 @@ tool(
6588
6673
  }
6589
6674
  }
6590
6675
  );
6676
+
6677
+ // src/rig-version.ts
6678
+ import { stat as stat3 } from "node:fs/promises";
6679
+ async function rigVersion(abs) {
6680
+ try {
6681
+ const s = await stat3(abs);
6682
+ return `${s.mtimeMs}:${s.size}`;
6683
+ } catch {
6684
+ return "missing";
6685
+ }
6686
+ }
6687
+ async function callToolVersioned(name, args) {
6688
+ const path = typeof args.path === "string" ? args.path : void 0;
6689
+ if (!path) return { result: await callTool(name, args), before: "missing", after: "missing" };
6690
+ return withRigQueue(path, async () => {
6691
+ const before = await rigVersion(path);
6692
+ const result = await callTool(name, args);
6693
+ return { result, before, after: await rigVersion(path) };
6694
+ });
6695
+ }
6696
+
6697
+ // src/tool-schema.ts
6698
+ import { zodToJsonSchema } from "zod-to-json-schema";
6699
+ function prefixItems(node) {
6700
+ if (Array.isArray(node)) return node.map(prefixItems);
6701
+ if (!node || typeof node !== "object") return node;
6702
+ const { items, ...rest } = Object.fromEntries(Object.entries(node).map(([k, v]) => [k, prefixItems(v)]));
6703
+ return items === void 0 ? rest : Array.isArray(items) ? { ...rest, prefixItems: items } : { ...rest, items };
6704
+ }
6705
+ function jsonSchemaOf(tool2) {
6706
+ const { $schema: _drop, ...schema } = zodToJsonSchema(tool2.input, { $refStrategy: "none" });
6707
+ return prefixItems(schema);
6708
+ }
6591
6709
  export {
6592
6710
  callTool,
6711
+ callToolVersioned,
6593
6712
  closeTools,
6713
+ jsonSchemaOf,
6714
+ rigVersion,
6594
6715
  tools
6595
6716
  };
6596
6717
  //# sourceMappingURL=tools.js.map