@instruktlabs/kiln 1.1.0 → 1.2.1

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 (56) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +28 -0
  4. package/dist/agent-run.mjs +224 -151
  5. package/dist/build.json +22 -22
  6. package/dist/cli.mjs +229 -151
  7. package/dist/mcp-engine.mjs +400 -147
  8. package/dist/mcp-server.mjs +6 -6
  9. package/dist/viewer/app.js +180 -180
  10. package/dist/viewer/chat.html +183 -183
  11. package/docs/sdk.md +23 -0
  12. package/docs/tools-operations.md +1529 -0
  13. package/lib/asset-widget-uri.d.ts +1 -1
  14. package/lib/asset-widget-uri.js +1 -1
  15. package/lib/discovery/index.d.ts +1 -1
  16. package/lib/discovery/index.js +2 -2
  17. package/lib/discovery/service.d.ts +3 -1
  18. package/lib/discovery/service.js +4 -2
  19. package/lib/engine-identity.d.ts +1 -1
  20. package/lib/engine-identity.js +1 -1
  21. package/lib/mcp-core.d.ts +2 -1
  22. package/lib/mcp-core.js +1 -0
  23. package/lib/mcp-engine.d.ts +2 -0
  24. package/lib/mcp-engine.js +8 -3
  25. package/lib/tools/discovery.d.ts +1 -1
  26. package/lib/tools/discovery.js +11 -9
  27. package/lib/tools/materials.d.ts +1 -1
  28. package/lib/tools/materials.js +44 -14
  29. package/lib/tools/operation-contract.d.ts +8 -0
  30. package/lib/tools/operation-contract.js +37 -0
  31. package/lib/tools/operations.d.ts +3 -0
  32. package/lib/tools/operations.js +154 -0
  33. package/lib/tools/registry.d.ts +7 -4
  34. package/lib/tools/registry.js +73 -24
  35. package/package.json +2 -1
  36. package/plugin.json +1 -1
  37. package/plugins/kiln-engine/.claude-plugin/plugin.json +1 -1
  38. package/plugins/kiln-engine/README.md +3 -3
  39. package/plugins/kiln-engine/package-provenance.json +6 -6
  40. package/plugins/kiln-engine/plugin.json +1 -1
  41. package/plugins/kiln-engine/runtime.json +1 -1
  42. package/src/asset-widget-uri.ts +1 -1
  43. package/src/discovery/index.ts +2 -0
  44. package/src/discovery/service.ts +3 -1
  45. package/src/engine-identity.ts +1 -1
  46. package/src/generated/mcp-manifest.json +4 -4
  47. package/src/mcp-core.ts +3 -0
  48. package/src/mcp-engine.ts +11 -1
  49. package/src/tools/discovery.ts +20 -10
  50. package/src/tools/materials.ts +59 -15
  51. package/src/tools/operation-contract.ts +51 -0
  52. package/src/tools/operations.ts +177 -0
  53. package/src/tools/registry.ts +102 -21
  54. package/src/viewer/chat-app.ts +6 -2
  55. package/src/viewer/glb-loader.ts +78 -0
  56. package/src/viewer/scene.ts +9 -26
package/dist/cli.mjs CHANGED
@@ -36171,7 +36171,7 @@ var init_program_artifacts = __esm(() => {
36171
36171
  function engineIdentity() {
36172
36172
  return { version: ENGINE_VERSION, installUrl: ENGINE_INSTALL_URL };
36173
36173
  }
36174
- var ENGINE_VERSION = "1.1.0", ENGINE_INSTALL_URL;
36174
+ var ENGINE_VERSION = "1.2.1", ENGINE_INSTALL_URL;
36175
36175
  var init_engine_identity = __esm(() => {
36176
36176
  ENGINE_INSTALL_URL = new URL("../", import.meta.url).href;
36177
36177
  });
@@ -39652,7 +39652,7 @@ var init_query_schema = __esm(() => {
39652
39652
  });
39653
39653
 
39654
39654
  // src/discovery/service.ts
39655
- function createDiscoveryService(source, index, capabilities, retirements = {}, notes = async () => []) {
39655
+ function createDiscoveryService(source, index, capabilities, retirements = {}, notes = async () => [], capabilitiesCall = "capabilities:true") {
39656
39656
  const entries = parseCatalog(source);
39657
39657
  const byId = new Map(entries.map((entry) => [entry.id, entry]));
39658
39658
  const byName = new Map(entries.filter((entry) => entry.kind !== "recipe").map((entry) => [entry.name, entry]));
@@ -39675,7 +39675,7 @@ function createDiscoveryService(source, index, capabilities, retirements = {}, n
39675
39675
  "Parts auto-add to opts.parent; do not wrap createPart in parent.add. Part rotation uses degrees.",
39676
39676
  "Custom THREE.BufferGeometry and ordinary JavaScript functions remain available. Recipes offer guidance and never restrict modeling.",
39677
39677
  "Send source once and reuse programRef for reads, edits, inspection and rendering. Review geometry, materials and destination constraints separately.",
39678
- "Search for the operation you need in ordinary language, then fetch exact contracts with ids. Use capabilities:true for the current host."
39678
+ `Search for the operation you need in ordinary language, then fetch exact contracts with ids. Use ${capabilitiesCall} for the current host.`
39679
39679
  ]
39680
39680
  };
39681
39681
  const error = (mode, code, message) => ({
@@ -39861,12 +39861,12 @@ var init_service = __esm(() => {
39861
39861
  function createDiscovery(capabilities = async () => ({
39862
39862
  host: "No host capability provider was supplied.",
39863
39863
  discovery: { mode: "lexical", offline: true, requiresModel: false }
39864
- }), notes = async () => []) {
39864
+ }), notes = async () => [], capabilitiesCall = "capabilities:true") {
39865
39865
  snapshot2 ??= (() => {
39866
39866
  const entries = listDiscoveryEntries();
39867
39867
  return { entries, index: createLexicalDiscoveryIndex(entries) };
39868
39868
  })();
39869
- return createDiscoveryService(snapshot2.entries, snapshot2.index, capabilities, REMOVED_AUTHORING_HELPERS, notes);
39869
+ return createDiscoveryService(snapshot2.entries, snapshot2.index, capabilities, REMOVED_AUTHORING_HELPERS, notes, capabilitiesCall);
39870
39870
  }
39871
39871
  var snapshot2;
39872
39872
  var init_discovery = __esm(() => {
@@ -39881,8 +39881,8 @@ var init_discovery = __esm(() => {
39881
39881
  });
39882
39882
 
39883
39883
  // src/tools/discovery.ts
39884
- function createKilnDiscoveryDef(context) {
39885
- const run = createDiscovery(() => currentCapabilities(context), async () => projectNotes(context));
39884
+ function createKilnDiscoveryDef(context, presentation = "grouped") {
39885
+ const run = createDiscovery(() => currentCapabilities(context, presentation), async () => projectNotes(context), presentation === "operations" ? "kiln_capabilities({})" : "capabilities:true");
39886
39886
  return {
39887
39887
  name: "kiln_discover",
39888
39888
  description: "Discover Kiln operations, assemblies, recipes, tool-input shapes and current host capabilities. Omit arguments for a compact overview. Search with ordinary modeling language using query; refine with family, kind or tags. Fetch complete contracts/examples with ids (up to six exact IDs, executable names or shape: ids). Overview/search pages default to six summaries. Recipes guide construction without restricting the asset. Search runs locally without models or network calls.",
@@ -39906,7 +39906,7 @@ function projectNotes(context) {
39906
39906
  `Project ${projectId} is configured for this workspace and applies when projectId is omitted: read its brief, design, inventory and material pins first with kiln_project { action: "get", projectId: "${projectId}" }; projectId: null selects standalone work.`
39907
39907
  ] : [];
39908
39908
  }
39909
- async function currentCapabilities(context) {
39909
+ async function currentCapabilities(context, presentation) {
39910
39910
  const externalEvaluator = Boolean(context.evaluatorPort) || context.evaluatorProfile === "evaluator-required";
39911
39911
  const approvedTextures = context.approvedTextureResources ? context.approvedTextureResources() : externalEvaluator ? null : approvedTextureCatalogV1();
39912
39912
  const textureSlots = approvedTextures === null ? null : {};
@@ -39935,7 +39935,7 @@ async function currentCapabilities(context) {
39935
39935
  requiresModel: false
39936
39936
  },
39937
39937
  engine: engineIdentity(),
39938
- project: {
39938
+ project: presentation === "operations" && !context.projectStore ? { available: false } : {
39939
39939
  configured: configuredProject(context),
39940
39940
  select: "projectId per call; omitted uses the configured project, null selects standalone work",
39941
39941
  read: 'kiln_project { action: "get", projectId } returns the brief, design, inventory and material pins'
@@ -39954,7 +39954,7 @@ async function currentCapabilities(context) {
39954
39954
  available: Boolean(context.assetLibrary),
39955
39955
  collections: context.assetLibrary?.collections() ?? [],
39956
39956
  save: "kiln_save persists exact GLB, source and provenance; draft renders do not populate collections",
39957
- resume: "kiln_assets action=restore imports a saved revision into the current program store",
39957
+ resume: `${presentation === "operations" ? "kiln_assets_restore" : "kiln_assets action=restore"} imports a saved revision into the current program store`,
39958
39958
  downloads: "kiln_export returns GLB/source/ZIP resource links; client presentation varies",
39959
39959
  viewer: "kiln_present opens a saved revision in supporting chat clients with 3D viewing and downloads; kiln view opens a local collection or standalone GLB/ZIP"
39960
39960
  },
@@ -40340,12 +40340,12 @@ var init_material_presets = __esm(() => {
40340
40340
 
40341
40341
  // src/tools/materials.ts
40342
40342
  import { z as z18 } from "zod";
40343
- async function onlyRevision(library, materialId) {
40343
+ async function onlyRevision(library, materialId, listCall) {
40344
40344
  const revisions = (await library.list()).filter((material) => material.materialId === materialId).map((material) => material.revisionId).sort();
40345
40345
  if (revisions.length === 1)
40346
40346
  return revisions[0];
40347
40347
  if (revisions.length === 0)
40348
- throw unknownMaterial(materialId);
40348
+ throw unknownMaterial(materialId, undefined, listCall);
40349
40349
  throw new Error(`Material ${materialId} has ${revisions.length} revisions: ${revisions.join(", ")}. Pass revisionId to get one.`);
40350
40350
  }
40351
40351
  function aliasMaterialId(raw) {
@@ -40358,11 +40358,13 @@ function aliasMaterialId(raw) {
40358
40358
  throw new Error(`kiln_material: resourceId (${String(resourceId)}) and materialId (${String(rest.materialId)}) name different materials; pass materialId alone.`);
40359
40359
  return { ...rest, materialId: resourceId };
40360
40360
  }
40361
- function createKilnMaterialDef(library) {
40361
+ function createKilnMaterialDef(library, presentation = "grouped") {
40362
+ const listCall = presentation === "operations" ? 'kiln_material_search({ scope: "saved" })' : "kiln_material { action: 'list' }";
40362
40363
  return {
40363
40364
  name: "kiln_material",
40364
40365
  description: "Manage optional immutable material resources in this workspace. presets discovers shipped architecture, wood, metal, fabric and ground recipes; create-preset bakes one with an explicit seed, creator and license; list returns compact material/revision summaries; get returns full provenance, hashes, map conventions, physical repeat scale and a code-ready portable material spec; create-procedural bakes bounded editable layer recipes including optional height-derived normals; import accepts complete normalized records with embedded PNG bytes. No action downloads URLs or executes source. Pin the returned materialId/revisionId through per-invocation materialDependencies or project dependencies before authored evaluation resolves the resources; no project is required.",
40365
40366
  inputSchema: materialToolInput,
40367
+ actionContract: MATERIAL_CONTRACT,
40366
40368
  annotations: {
40367
40369
  readOnlyHint: false,
40368
40370
  destructiveHint: false,
@@ -40372,7 +40374,7 @@ function createKilnMaterialDef(library) {
40372
40374
  async run(raw) {
40373
40375
  assertMaterialJson(raw);
40374
40376
  const input = materialToolInput.parse(aliasMaterialId(raw));
40375
- requireActionFields("kiln_material", input.action, input, REQUIREMENTS2[input.action]);
40377
+ requireActionFields("kiln_material", input.action, input, MATERIAL_CONTRACT[input.action]);
40376
40378
  switch (input.action) {
40377
40379
  case "presets":
40378
40380
  return {
@@ -40408,12 +40410,12 @@ function createKilnMaterialDef(library) {
40408
40410
  };
40409
40411
  }
40410
40412
  case "get": {
40411
- const revisionId = input.revisionId ?? await onlyRevision(library, input.materialId);
40413
+ const revisionId = input.revisionId ?? await onlyRevision(library, input.materialId, listCall);
40412
40414
  try {
40413
40415
  return materialResult((await library.read(input.materialId, revisionId)).manifest);
40414
40416
  } catch (error) {
40415
40417
  if (error.code === "ENOENT")
40416
- throw unknownMaterial(input.materialId, revisionId);
40418
+ throw unknownMaterial(input.materialId, revisionId, listCall);
40417
40419
  throw error;
40418
40420
  }
40419
40421
  }
@@ -40437,11 +40439,11 @@ function createKilnMaterialDef(library) {
40437
40439
  }
40438
40440
  };
40439
40441
  }
40440
- var MATERIAL_ACTIONS, REQUIREMENTS2, record7 = (description) => z18.record(z18.string(), z18.unknown()).optional().describe(description), tag, materialToolInput, materialResult = (material) => ({
40442
+ var MATERIAL_ACTIONS, MATERIAL_CONTRACT, record7 = (description) => z18.record(z18.string(), z18.unknown()).optional().describe(description), tag, materialToolInput, materialResult = (material) => ({
40441
40443
  ok: true,
40442
40444
  material,
40443
40445
  portableSpec: materialLibraryPortableSpec(material)
40444
- }), unknownMaterial = (materialId, revisionId) => new Error(`Unknown material ${materialId}${revisionId ? ` at ${revisionId}` : ""}. kiln_material { action: 'list' } lists the materials in this workspace with their revision IDs.`);
40446
+ }), unknownMaterial = (materialId, revisionId, listCall) => new Error(`Unknown material ${materialId}${revisionId ? ` at ${revisionId}` : ""}. ${listCall} lists the materials in this workspace with their revision IDs.`);
40445
40447
  var init_materials = __esm(() => {
40446
40448
  init_material_library();
40447
40449
  init_material_library_node();
@@ -40454,13 +40456,39 @@ var init_materials = __esm(() => {
40454
40456
  "create-procedural",
40455
40457
  "import"
40456
40458
  ];
40457
- REQUIREMENTS2 = {
40458
- presets: { required: [] },
40459
- "create-preset": { required: ["presetId", "seed", "creator", "license"] },
40460
- list: { required: [] },
40461
- get: { required: ["materialId"] },
40462
- "create-procedural": { required: ["draft"], shapes: { draft: "shape:material-draft" } },
40463
- import: { required: ["payload"], shapes: { payload: "shape:material-import" } }
40459
+ MATERIAL_CONTRACT = {
40460
+ presets: {
40461
+ fields: ["tag"],
40462
+ required: [],
40463
+ description: "List shipped material recipes, optionally filtered by tag."
40464
+ },
40465
+ "create-preset": {
40466
+ fields: ["presetId", "seed", "creator", "license", "size", "materialId"],
40467
+ required: ["presetId", "seed", "creator", "license"],
40468
+ description: "Create an immutable material from a shipped preset with explicit seed, creator and license."
40469
+ },
40470
+ list: {
40471
+ fields: ["tag"],
40472
+ required: [],
40473
+ description: "List saved immutable material revisions, optionally filtered by tag."
40474
+ },
40475
+ get: {
40476
+ fields: ["materialId", "revisionId"],
40477
+ required: ["materialId"],
40478
+ description: "Read a material revision, provenance and portable specification. Omit revisionId only when exactly one revision exists."
40479
+ },
40480
+ "create-procedural": {
40481
+ fields: ["draft"],
40482
+ required: ["draft"],
40483
+ shapes: { draft: "shape:material-draft" },
40484
+ description: "Create an immutable material from a bounded procedural draft. Does not fetch URLs or execute source."
40485
+ },
40486
+ import: {
40487
+ fields: ["payload"],
40488
+ required: ["payload"],
40489
+ shapes: { payload: "shape:material-import" },
40490
+ description: "Import complete normalized material records and embedded PNGs. Does not fetch URLs or execute source."
40491
+ }
40464
40492
  };
40465
40493
  tag = z18.string().min(1).max(80).optional().describe("presets, list: keep one tag.");
40466
40494
  materialToolInput = z18.strictObject({
@@ -40556,7 +40584,7 @@ function createKilnReviewDef(context) {
40556
40584
  },
40557
40585
  async run(raw) {
40558
40586
  const input = reviewToolInput.parse(raw);
40559
- requireActionFields("kiln_review", input.action, input, REQUIREMENTS3[input.action]);
40587
+ requireActionFields("kiln_review", input.action, input, REQUIREMENTS2[input.action]);
40560
40588
  if (input.action === "list") {
40561
40589
  const snapshot = await store.snapshot({ projectId: input.projectId });
40562
40590
  const offset = input.offset ?? 0;
@@ -40663,7 +40691,7 @@ function createKilnReviewDef(context) {
40663
40691
  }
40664
40692
  };
40665
40693
  }
40666
- var operationId, assetId, REVIEW_ACTIONS, REQUIREMENTS3, reviewToolInput, DEFAULT_PAGE = 20;
40694
+ var operationId, assetId, REVIEW_ACTIONS, REQUIREMENTS2, reviewToolInput, DEFAULT_PAGE = 20;
40667
40695
  var init_review = __esm(() => {
40668
40696
  init_assets();
40669
40697
  init_requirements_context();
@@ -40674,7 +40702,7 @@ var init_review = __esm(() => {
40674
40702
  operationId = z19.string().regex(/^op_[a-f0-9-]{36}$/);
40675
40703
  assetId = z19.string().regex(/^[a-z][a-z0-9_-]{0,79}$/);
40676
40704
  REVIEW_ACTIONS = ["list", "get", "pin", "save"];
40677
- REQUIREMENTS3 = {
40705
+ REQUIREMENTS2 = {
40678
40706
  list: { required: [] },
40679
40707
  get: { required: ["operationId"] },
40680
40708
  pin: { required: ["operationId", "pinned"] },
@@ -40765,6 +40793,16 @@ var init_workspace2 = __esm(() => {
40765
40793
  ]);
40766
40794
  });
40767
40795
 
40796
+ // src/tools/operation-contract.ts
40797
+ import { z as z21 } from "zod";
40798
+ var init_operation_contract = () => {};
40799
+
40800
+ // src/tools/operations.ts
40801
+ import { z as z22 } from "zod";
40802
+ var init_operations = __esm(() => {
40803
+ init_operation_contract();
40804
+ });
40805
+
40768
40806
  // src/edit-buffer.ts
40769
40807
  class KilnDraftBuffer {
40770
40808
  buf;
@@ -40991,6 +41029,9 @@ var init_view_render_timeout = __esm(() => {
40991
41029
  WARM_UP_STATES = new Set(["unknown", "pending", "ready", "degraded"]);
40992
41030
  });
40993
41031
 
41032
+ // src/asset-widget-uri.ts
41033
+ var KILN_ASSET_WIDGET_URI = "ui://kiln/asset-v6.html";
41034
+
40994
41035
  // src/views/port.ts
40995
41036
  async function sha256Bytes(bytes) {
40996
41037
  const input = new Uint8Array(bytes.byteLength);
@@ -42363,7 +42404,7 @@ var init_asset_widget = __esm(() => {
42363
42404
  });
42364
42405
 
42365
42406
  // src/tools/registry.ts
42366
- import { z as z21 } from "zod";
42407
+ import { z as z23 } from "zod";
42367
42408
  import * as THREE46 from "three";
42368
42409
  function proceduralTextureMaterialContract(rendered, context) {
42369
42410
  const required = [...new Set(context.requiredProceduralTextureUsages ?? [])];
@@ -43427,21 +43468,24 @@ function withCaptureCache(context) {
43427
43468
  } : {}
43428
43469
  };
43429
43470
  }
43430
- async function readSavedRevision(target, collection, assetId, revisionId) {
43471
+ async function readSavedRevision(target, collection, assetId, revisionId, listCall = `kiln_assets { action: 'list', collection: '${collection}' }`) {
43431
43472
  try {
43432
43473
  return await target.read(collection, assetId, revisionId);
43433
43474
  } catch (error) {
43434
43475
  if (error.code === "ENOENT")
43435
- throw new Error(`Unknown asset ${assetId} revision ${revisionId} in collection ${collection}. kiln_assets { action: 'list', collection: '${collection}' } lists the saved revisions.`);
43476
+ throw new Error(`Unknown asset ${assetId} revision ${revisionId} in collection ${collection}. ${listCall} lists the saved revisions.`);
43436
43477
  throw error;
43437
43478
  }
43438
43479
  }
43439
43480
  function createKilnProgramToolRegistry(suppliedContext = {}) {
43481
+ return createProgramDefs(suppliedContext, "grouped");
43482
+ }
43483
+ function createProgramDefs(suppliedContext, presentation) {
43440
43484
  toolRequirements(suppliedContext);
43441
43485
  const store = suppliedContext.programStore ?? new MemoryProgramStore;
43442
43486
  const context = withCaptureCache(withBuildCache({ ...suppliedContext, programStore: store }));
43443
43487
  return [
43444
- createKilnDiscoveryDef({ ...suppliedContext, programStore: store }),
43488
+ createKilnDiscoveryDef({ ...suppliedContext, programStore: store }, presentation),
43445
43489
  {
43446
43490
  name: "kiln_renderer",
43447
43491
  description: "Inspect status, or reprobe after renderer setup/repair to refresh this session and reset failed starts. Never installs, starts, stops or renders. Preserves CPU/local/remote selection; environment/credential changes require a host restart. Read viewFidelity after rendering.",
@@ -43481,7 +43525,7 @@ function createKilnProgramToolRegistry(suppliedContext = {}) {
43481
43525
  ].map((def) => withProgramReferences(def, store, context.readSourceFile)),
43482
43526
  createKilnSourceDef(store),
43483
43527
  ...context.projectStore ? [createKilnProjectDef(context.projectStore, context.projectBundleReader)] : [],
43484
- ...context.materialLibrary ? [createKilnMaterialDef(context.materialLibrary)] : [],
43528
+ ...context.materialLibrary ? [createKilnMaterialDef(context.materialLibrary, presentation)] : [],
43485
43529
  ...context.reviewStore && context.assetLibrary ? [
43486
43530
  createKilnReviewDef({
43487
43531
  reviewStore: context.reviewStore,
@@ -43489,7 +43533,7 @@ function createKilnProgramToolRegistry(suppliedContext = {}) {
43489
43533
  requirements: context.requirements
43490
43534
  })
43491
43535
  ] : [],
43492
- ...createKilnAssetDefs({ ...context, programStore: store })
43536
+ ...createKilnAssetDefs({ ...context, programStore: store }, presentation)
43493
43537
  ].map((def) => withWorkspaceContext(def, context)).map((def) => ({
43494
43538
  ...def,
43495
43539
  annotations: {
@@ -43501,10 +43545,10 @@ function createKilnProgramToolRegistry(suppliedContext = {}) {
43501
43545
  }
43502
43546
  }));
43503
43547
  }
43504
- async function newestRevision(target, collection, assetId) {
43548
+ async function newestRevision(target, collection, assetId, listCall = `kiln_assets { action: 'list', collection: '${collection}' }`) {
43505
43549
  const revisions = (await target.list(collection)).filter((a) => a.assetId === assetId);
43506
43550
  if (!revisions.length)
43507
- throw new Error(`Unknown asset ${assetId} in ${collection}; kiln_assets { action: 'list', collection: '${collection}' } lists them.`);
43551
+ throw new Error(`Unknown asset ${assetId} in ${collection}; ${listCall} lists them.`);
43508
43552
  const parents = new Set(revisions.map((a) => a.parentRevision).filter(Boolean));
43509
43553
  const heads = revisions.filter((a) => !parents.has(a.revisionId));
43510
43554
  const newest = (heads.length ? heads : revisions).sort((a, b) => a.createdAt < b.createdAt ? 1 : a.createdAt > b.createdAt ? -1 : 0);
@@ -43569,7 +43613,12 @@ async function buildProgramAssetDraft(code, context, backdrop) {
43569
43613
  }
43570
43614
  };
43571
43615
  }
43572
- function createKilnAssetDefs(context) {
43616
+ function createKilnAssetDefs(context, presentation = "grouped") {
43617
+ const listCall = (collection) => presentation === "operations" ? `kiln_assets_search({ collection: '${collection}' })` : `kiln_assets { action: 'list', collection: '${collection}' }`;
43618
+ const selector = presentation === "operations" ? {
43619
+ ...assetSelector,
43620
+ collection: assetSelector.collection.describe("Collection ID from kiln_assets_collections; default project.")
43621
+ } : assetSelector;
43573
43622
  const library = () => {
43574
43623
  if (!context.assetLibrary)
43575
43624
  throw new Error("No asset library configured. The local CLI/MCP host supplies workspace collections; embedded hosts must inject assetLibrary.");
@@ -43597,38 +43646,38 @@ function createKilnAssetDefs(context) {
43597
43646
  resources: (await Promise.resolve().then(() => (init_assets_resources(), exports_assets_resources))).assetLinks(collection, asset),
43598
43647
  downloadUrls: await context.assetDownloadUrls?.(collection, asset.assetId, asset.revisionId)
43599
43648
  });
43600
- const saveInput = z21.object({
43601
- collection: assetSelector.collection,
43602
- programRef: z21.string(),
43603
- name: z21.string().min(1).max(200),
43604
- assetId: assetSelector.assetId.optional(),
43605
- parentRevision: assetSelector.revisionId.optional(),
43606
- tags: z21.array(z21.string().max(80)).max(30).optional(),
43607
- brief: z21.string().max(8000).optional(),
43608
- description: z21.string().max(4000).optional(),
43609
- attribution: z21.object({
43610
- model: z21.string().max(200).optional(),
43611
- harness: z21.string().max(200).optional(),
43612
- author: z21.string().max(200).optional()
43649
+ const saveInput = z23.object({
43650
+ collection: selector.collection,
43651
+ programRef: z23.string(),
43652
+ name: z23.string().min(1).max(200),
43653
+ assetId: selector.assetId.optional(),
43654
+ parentRevision: selector.revisionId.optional(),
43655
+ tags: z23.array(z23.string().max(80)).max(30).optional(),
43656
+ brief: z23.string().max(8000).optional(),
43657
+ description: z23.string().max(4000).optional(),
43658
+ attribution: z23.object({
43659
+ model: z23.string().max(200).optional(),
43660
+ harness: z23.string().max(200).optional(),
43661
+ author: z23.string().max(200).optional()
43613
43662
  }).optional(),
43614
- backdrop: z21.enum(BACKDROP_IDS).optional().describe("Preview backdrop: the one the reviewed sheet used.")
43615
- });
43616
- const assetsInput = z21.object({
43617
- action: z21.enum(["collections", "catalog", "list", "get", "restore"]).default("list"),
43618
- collection: assetSelector.collection,
43619
- assetId: assetSelector.assetId.optional(),
43620
- revisionId: assetSelector.revisionId.optional().describe("get, restore: the saved revision; omitted, the newest."),
43621
- query: z21.string().max(200).optional(),
43622
- offset: z21.number().int().min(0).default(0),
43623
- limit: z21.number().int().min(1).max(50).default(20)
43624
- });
43625
- const exportInput = z21.object(assetSelector);
43663
+ backdrop: z23.enum(BACKDROP_IDS).optional().describe("Preview backdrop: the one the reviewed sheet used.")
43664
+ });
43665
+ const assetsInput = z23.object({
43666
+ action: z23.enum(["collections", "catalog", "list", "get", "restore"]).default("list"),
43667
+ collection: selector.collection,
43668
+ assetId: selector.assetId.optional(),
43669
+ revisionId: selector.revisionId.optional().describe("get, restore: the saved revision; omitted, the newest."),
43670
+ query: z23.string().max(200).optional(),
43671
+ offset: z23.number().int().min(0).default(0),
43672
+ limit: z23.number().int().min(1).max(50).default(20)
43673
+ });
43674
+ const exportInput = z23.object(selector);
43626
43675
  const profileExportInput = exportInput.extend({
43627
- profile: z21.enum(["editable", "runtime"]).default("editable").describe("editable preserves canonical source/GLB/build resources. runtime returns a standalone GLB and versioned review-metadata sidecar; no source bundle or geometry optimization.")
43676
+ profile: z23.enum(["editable", "runtime"]).default("editable").describe("editable preserves canonical source/GLB/build resources. runtime returns a standalone GLB and versioned review-metadata sidecar; no source bundle or geometry optimization.")
43628
43677
  });
43629
- const importInput = z21.object({
43630
- ...assetSelector,
43631
- sourceCollection: assetSelector.collection
43678
+ const importInput = z23.object({
43679
+ ...selector,
43680
+ sourceCollection: selector.collection
43632
43681
  });
43633
43682
  return [
43634
43683
  {
@@ -43644,7 +43693,7 @@ function createKilnAssetDefs(context) {
43644
43693
  requirements: activeRequirements.binding
43645
43694
  };
43646
43695
  if (input.assetId && input.parentRevision) {
43647
- const previous = await readSavedRevision(target, input.collection, input.assetId, input.parentRevision);
43696
+ const previous = await readSavedRevision(target, input.collection, input.assetId, input.parentRevision, listCall(input.collection));
43648
43697
  assertSavedRequirementsAuthorized(previous.manifest, activeRequirements);
43649
43698
  }
43650
43699
  const code = await context.programStore.get(input.programRef);
@@ -43659,6 +43708,7 @@ function createKilnAssetDefs(context) {
43659
43708
  name: "kiln_assets",
43660
43709
  description: "Browse saved assets: collections discovers storage; catalog searches all configured collections; list searches one. Both searches paginate. get returns a build record/downloads; restore loads exact source for kiln_source/kiln_edit. Collection is not project membership. Binary-only assets cannot restore source.",
43661
43710
  inputSchema: assetsInput,
43711
+ actionContract: ASSET_CONTRACT,
43662
43712
  run: async (raw) => {
43663
43713
  const input = assetsInput.parse(raw);
43664
43714
  const activeRequirements = toolRequirements(context);
@@ -43703,10 +43753,10 @@ function createKilnAssetDefs(context) {
43703
43753
  }))
43704
43754
  };
43705
43755
  }
43706
- if (!input.assetId)
43756
+ if (ASSET_CONTRACT[input.action].required.some((field) => !input[field]))
43707
43757
  throw new Error(`kiln_assets ${input.action} requires assetId; kiln_assets { action: 'list', collection: '${input.collection}' } lists them.`);
43708
- const revisionId = input.revisionId ?? await newestRevision(target, input.collection, input.assetId);
43709
- const record = await readSavedRevision(target, input.collection, input.assetId, revisionId);
43758
+ const revisionId = input.revisionId ?? await newestRevision(target, input.collection, input.assetId, listCall(input.collection));
43759
+ const record = await readSavedRevision(target, input.collection, input.assetId, revisionId, listCall(input.collection));
43710
43760
  if (input.action === "get")
43711
43761
  return links(input.collection, record.manifest);
43712
43762
  const saved = assertSavedRequirementsAuthorized(record.manifest, activeRequirements);
@@ -43726,9 +43776,9 @@ function createKilnAssetDefs(context) {
43726
43776
  name: "kiln_present",
43727
43777
  description: "Present one exact saved revision. Supporting MCP App clients show an interactive 3D card with GLB, editable ZIP, and source downloads. Every host receives exact artifact descriptors with resource URIs in the JSON result; verified hosts may also receive core MCP resource-link blocks. This tool does not launch a local browser in coding harnesses. Call after saving or when the user wants to see or download an asset.",
43728
43778
  inputSchema: exportInput,
43729
- outputSchema: z21.object({
43730
- ok: z21.literal(true),
43731
- collection: z21.string(),
43779
+ outputSchema: z23.object({
43780
+ ok: z23.literal(true),
43781
+ collection: z23.string(),
43732
43782
  asset: assetManifestSchema.pick({
43733
43783
  assetId: true,
43734
43784
  revisionId: true,
@@ -43739,25 +43789,25 @@ function createKilnAssetDefs(context) {
43739
43789
  editable: true,
43740
43790
  files: true
43741
43791
  }).extend({
43742
- build: z21.object({
43743
- engine: z21.string(),
43744
- rebuild: z21.enum(["engine-required", "external-dependencies-required"]),
43745
- warningCount: z21.number().int(),
43746
- warnings: z21.array(z21.string())
43792
+ build: z23.object({
43793
+ engine: z23.string(),
43794
+ rebuild: z23.enum(["engine-required", "external-dependencies-required"]),
43795
+ warningCount: z23.number().int(),
43796
+ warnings: z23.array(z23.string())
43747
43797
  }).optional()
43748
43798
  }),
43749
- resources: z21.array(z21.object({
43750
- type: z21.literal("resource_link"),
43751
- name: z21.string(),
43752
- uri: z21.string(),
43753
- mimeType: z21.string(),
43754
- size: z21.number().int().nonnegative(),
43755
- annotations: z21.object({
43756
- audience: z21.array(z21.enum(["user", "assistant"])),
43757
- priority: z21.number()
43799
+ resources: z23.array(z23.object({
43800
+ type: z23.literal("resource_link"),
43801
+ name: z23.string(),
43802
+ uri: z23.string(),
43803
+ mimeType: z23.string(),
43804
+ size: z23.number().int().nonnegative(),
43805
+ annotations: z23.object({
43806
+ audience: z23.array(z23.enum(["user", "assistant"])),
43807
+ priority: z23.number()
43758
43808
  })
43759
43809
  })),
43760
- downloadUrls: z21.record(z21.string(), z21.string()).optional()
43810
+ downloadUrls: z23.record(z23.string(), z23.string()).optional()
43761
43811
  }),
43762
43812
  ui: {
43763
43813
  resourceUri: KILN_ASSET_WIDGET_URI,
@@ -43774,7 +43824,7 @@ function createKilnAssetDefs(context) {
43774
43824
  inputSchema: profileExportInput,
43775
43825
  run: async (raw) => {
43776
43826
  const input = profileExportInput.parse(raw);
43777
- const record = await readSavedRevision(library(), input.collection, input.assetId, input.revisionId);
43827
+ const record = await readSavedRevision(library(), input.collection, input.assetId, input.revisionId, listCall(input.collection));
43778
43828
  await Promise.resolve().then(() => init_draw_diagnostics());
43779
43829
  if (input.profile === "runtime") {
43780
43830
  const output = await (await Promise.resolve().then(() => (init_asset_export(), exports_asset_export))).exportAssetGlb(record, {
@@ -43808,14 +43858,14 @@ function createKilnAssetDefs(context) {
43808
43858
  run: async (raw) => {
43809
43859
  const input = importInput.parse(raw);
43810
43860
  const target = library();
43811
- const record = await readSavedRevision(target, input.sourceCollection, input.assetId, input.revisionId);
43861
+ const record = await readSavedRevision(target, input.sourceCollection, input.assetId, input.revisionId, listCall(input.sourceCollection));
43812
43862
  await target.import(input.collection, [record]);
43813
43863
  return links(input.collection, record.manifest);
43814
43864
  }
43815
43865
  }
43816
43866
  ];
43817
43867
  }
43818
- var KILN_ASSET_WIDGET_URI = "ui://kiln/asset-v5.html", DEFAULT_INLOOP_VIEW_RENDER_TIMEOUT_MS = 6000, viewEvidenceHistoryByContext, VIEW_EVIDENCE_GUIDANCE = " viewEvidence.current describes ONLY this request. lastFaithful is older hash-only evidence for reference, not reused pixels and not current verification.", validateInput, renderInput, renderViewsInput, reviewDetailOptions = (context) => ({
43868
+ var DEFAULT_INLOOP_VIEW_RENDER_TIMEOUT_MS = 6000, viewEvidenceHistoryByContext, VIEW_EVIDENCE_GUIDANCE = " viewEvidence.current describes ONLY this request. lastFaithful is older hash-only evidence for reference, not reused pixels and not current verification.", validateInput, renderInput, renderViewsInput, reviewDetailOptions = (context) => ({
43819
43869
  retainedReport: () => {
43820
43870
  const operation = context.liveReview?.currentOperation?.();
43821
43871
  return operation ? {
@@ -43823,7 +43873,7 @@ var KILN_ASSET_WIDGET_URI = "ui://kiln/asset-v5.html", DEFAULT_INLOOP_VIEW_RENDE
43823
43873
  path: `.kiln/review/${operation.operationId}/evaluation.json`
43824
43874
  } : undefined;
43825
43875
  }
43826
- }), renderViewsBufferInput, screenshotAnimationInput, screenshotAnimationAdvertisedInput, viewInteriorInput, viewInteriorAdvertisedInput, partListInput, PLACEMENT_PAGE_LIMIT = 50, lod0Views = (views) => Array.from({ length: views }, () => []), KILN_RENDER_VIEWS_DESCRIPTION, kilnRenderViewsDef, KILN_SCREENSHOT_ANIMATION_DESCRIPTION, kilnScreenshotAnimationDef, KILN_VIEW_INTERIOR_DESCRIPTION, kilnViewInteriorDef, attachmentEndpointInput, surfacePairInput, inspectInput, inspectBufferInput, inspectAdvertisedInput, KILN_INSPECT_DESCRIPTION, kilnInspectDef, editOperationInput, editInput, editAdvertisedInput, KILN_EDIT_DESCRIPTION = "Patch an EXISTING Kiln program with exact-string replacements and render the result in one call. This is the refine verb: use it to change an asset you already have rather than re-emitting the whole file, so every line you did not touch stays byte-for-byte identical and the reply carries a unified diff of what actually changed. Pass the full current source as `code` and one or more { oldString, newString } edits, copied verbatim from that source. Edits apply in order and the call is all-or-nothing: if any oldString does not match, or matches more than once without replaceAll, NOTHING is applied and the reply names the edit that failed -- fix it and call again. The patched program comes back as `code`; write it to your file to keep it. Renders by default, so you see the change immediately; pass render:false to patch without rendering. Writes no files.", kilnEditDef, localCacheScope = 0, NESTED_RECORD_SHAPES, rendererInput, assetSelector;
43876
+ }), renderViewsBufferInput, screenshotAnimationInput, screenshotAnimationAdvertisedInput, viewInteriorInput, viewInteriorAdvertisedInput, partListInput, PLACEMENT_PAGE_LIMIT = 50, lod0Views = (views) => Array.from({ length: views }, () => []), KILN_RENDER_VIEWS_DESCRIPTION, kilnRenderViewsDef, KILN_SCREENSHOT_ANIMATION_DESCRIPTION, kilnScreenshotAnimationDef, KILN_VIEW_INTERIOR_DESCRIPTION, kilnViewInteriorDef, attachmentEndpointInput, surfacePairInput, inspectInput, inspectBufferInput, inspectAdvertisedInput, KILN_INSPECT_DESCRIPTION, kilnInspectDef, editOperationInput, editInput, editAdvertisedInput, KILN_EDIT_DESCRIPTION = "Patch an EXISTING Kiln program with exact-string replacements and render the result in one call. This is the refine verb: use it to change an asset you already have rather than re-emitting the whole file, so every line you did not touch stays byte-for-byte identical and the reply carries a unified diff of what actually changed. Pass the full current source as `code` and one or more { oldString, newString } edits, copied verbatim from that source. Edits apply in order and the call is all-or-nothing: if any oldString does not match, or matches more than once without replaceAll, NOTHING is applied and the reply names the edit that failed -- fix it and call again. The patched program comes back as `code`; write it to your file to keep it. Renders by default, so you see the change immediately; pass render:false to patch without rendering. Writes no files.", kilnEditDef, localCacheScope = 0, NESTED_RECORD_SHAPES, rendererInput, assetSelector, ASSET_CONTRACT;
43827
43877
  var init_registry2 = __esm(() => {
43828
43878
  init_capture_cache();
43829
43879
  init_assets();
@@ -43836,6 +43886,7 @@ var init_registry2 = __esm(() => {
43836
43886
  init_materials();
43837
43887
  init_review();
43838
43888
  init_workspace2();
43889
+ init_operations();
43839
43890
  init_capture_input();
43840
43891
  init_review_detail();
43841
43892
  init_build_cache();
@@ -43850,41 +43901,41 @@ var init_registry2 = __esm(() => {
43850
43901
  init_evidence_history();
43851
43902
  init_background();
43852
43903
  viewEvidenceHistoryByContext = new WeakMap;
43853
- validateInput = z21.object({
43854
- code: z21.string().describe("Kiln source code (defines `meta` + `build()`, optional `animate()`).")
43904
+ validateInput = z23.object({
43905
+ code: z23.string().describe("Kiln source code (defines `meta` + `build()`, optional `animate()`).")
43855
43906
  });
43856
- renderInput = z21.object({
43857
- code: z21.string().describe("Kiln source code to execute and render to an in-memory GLB.")
43907
+ renderInput = z23.object({
43908
+ code: z23.string().describe("Kiln source code to execute and render to an in-memory GLB.")
43858
43909
  });
43859
43910
  renderViewsInput = renderInput.extend({ capture: captureInput, detail: reviewDetailInput });
43860
43911
  renderViewsBufferInput = renderViewsInput.omit({ code: true });
43861
- screenshotAnimationInput = z21.object({
43912
+ screenshotAnimationInput = z23.object({
43862
43913
  shot: cameraShotInput.optional(),
43863
- measureParts: z21.array(cameraShotInput.shape.subject.unwrap()).min(1).max(16).optional().describe("Exact names or paths of subtrees measured together at each phase, independent of camera selection."),
43864
- frames: z21.number().int().min(2).max(6).optional(),
43865
- frameTimes: z21.array(z21.number().min(0).max(1)).min(1).max(9).optional().describe("Ordered phase fractions 0..1; mutually exclusive with frames."),
43866
- framing: z21.enum(["locked", "follow"]).optional(),
43867
- size: z21.number().int().min(128).max(1024).optional().describe("Frame size in px; default 256."),
43914
+ measureParts: z23.array(cameraShotInput.shape.subject.unwrap()).min(1).max(16).optional().describe("Exact names or paths of subtrees measured together at each phase, independent of camera selection."),
43915
+ frames: z23.number().int().min(2).max(6).optional(),
43916
+ frameTimes: z23.array(z23.number().min(0).max(1)).min(1).max(9).optional().describe("Ordered phase fractions 0..1; mutually exclusive with frames."),
43917
+ framing: z23.enum(["locked", "follow"]).optional(),
43918
+ size: z23.number().int().min(128).max(1024).optional().describe("Frame size in px; default 256."),
43868
43919
  detail: reviewDetailInput,
43869
- code: z21.string().describe("Kiln source code to execute; must define animate() returning the named clip."),
43870
- clip: z21.string().describe('The animation clip to view, by name (e.g. "walk", "attack"). Must be one your animate() returns.'),
43871
- camera: z21.string().optional().describe("Camera angle: right (default — side profile, best for leg swing + knee bend direction), front " + "(reveals sideways/lateral motion), back, left, top, or three-quarter."),
43872
- perFrame: z21.boolean().optional().describe("Return the frames as separate high-res images instead of one composite grid. Default false.")
43920
+ code: z23.string().describe("Kiln source code to execute; must define animate() returning the named clip."),
43921
+ clip: z23.string().describe('The animation clip to view, by name (e.g. "walk", "attack"). Must be one your animate() returns.'),
43922
+ camera: z23.string().optional().describe("Camera angle: right (default — side profile, best for leg swing + knee bend direction), front " + "(reveals sideways/lateral motion), back, left, top, or three-quarter."),
43923
+ perFrame: z23.boolean().optional().describe("Return the frames as separate high-res images instead of one composite grid. Default false.")
43873
43924
  });
43874
43925
  screenshotAnimationAdvertisedInput = screenshotAnimationInput.extend({
43875
43926
  shot: cameraShotRecordInput
43876
43927
  });
43877
- viewInteriorInput = z21.object({
43928
+ viewInteriorInput = z23.object({
43878
43929
  capture: advancedCaptureInput.optional(),
43879
- code: z21.string().describe("Kiln source code to execute and render with the roof hidden."),
43880
- nodeName: z21.string().optional().describe("Override: lift the roof by exact node name instead of by role. Matches that node and its " + "children. Normally OMIT it — Kiln finds the roof from its semantic role (anything built " + 'with createRoofPlanes/createGableRoof), falling back to historical "Roof" naming.')
43930
+ code: z23.string().describe("Kiln source code to execute and render with the roof hidden."),
43931
+ nodeName: z23.string().optional().describe("Override: lift the roof by exact node name instead of by role. Matches that node and its " + "children. Normally OMIT it — Kiln finds the roof from its semantic role (anything built " + 'with createRoofPlanes/createGableRoof), falling back to historical "Roof" naming.')
43881
43932
  });
43882
43933
  viewInteriorAdvertisedInput = viewInteriorInput.extend({ capture: captureRecordInput });
43883
- partListInput = z21.object({
43884
- query: z21.string().max(4096).optional().describe("Case-insensitive substring of name or exact encoded path; not a regex."),
43885
- offset: z21.number().int().min(0).optional(),
43886
- limit: z21.number().int().min(1).max(100).optional(),
43887
- placement: z21.boolean().optional().describe("Add world position, rotation, scale, mirroring and bounds per part; pages of 50.")
43934
+ partListInput = z23.object({
43935
+ query: z23.string().max(4096).optional().describe("Case-insensitive substring of name or exact encoded path; not a regex."),
43936
+ offset: z23.number().int().min(0).optional(),
43937
+ limit: z23.number().int().min(1).max(100).optional(),
43938
+ placement: z23.boolean().optional().describe("Add world position, rotation, scale, mirroring and bounds per part; pages of 50.")
43888
43939
  }).strict();
43889
43940
  KILN_RENDER_VIEWS_DESCRIPTION = "Build the current asset and return geometry metrics, exact part paths and images. Omit capture for six orthographic views. Choose preset/cells for a smaller orbit sheet, or version kiln.capture.v1 with shots for per-part framing, local axes, perspective and separate images. +X is forward, +Y up, +Z right. Review silhouette, attachments, proportion and ground contact. GPU PBR shading supports textured or metallic materials; a flat-shaded CPU render supports geometry review. Read viewFidelity; do not judge material fidelity from CPU views. Failed builds return errors without images." + VIEW_EVIDENCE_GUIDANCE;
43890
43941
  kilnRenderViewsDef = createKilnRenderViewsDef();
@@ -43892,49 +43943,49 @@ var init_registry2 = __esm(() => {
43892
43943
  kilnScreenshotAnimationDef = createKilnScreenshotAnimationDef();
43893
43944
  KILN_VIEW_INTERIOR_DESCRIPTION = "SEE INSIDE an enterable building: renders it with the roof lifted off, as a " + "three-view grid. (1) Floor plan: top-down — check the interior is open and walkable and the footprint " + "is right. (2) Dollhouse: a 3/4 cutaway — check built-in fixtures (hearth, counter, shelves) rest ON the " + "floor, not floating or sunk, and the walls enclose a real volume with headroom. (3) Eye-level: a low " + "angle looking in through the doorway with the near walls also removed — confirm the doorway is a REAL " + "gap you could walk through (not a panel) and no wall or glass is buried inside a solid mass. Call this before finalizing any building. Take no argument: the roof is found from its semantic role, so any roof built with createRoofPlanes/createGableRoof lifts whatever it is named. If " + "roofsHidden comes back 0 no roof was resolvable and the interior stays hidden — build the roof " + 'with a roof primitive (or name the group "Roof"). Each cell is rendered from deterministic cutaway GLB bytes: GPU PBR when available, otherwise a GLB-native geometry-flat fallback. Read viewFidelity before judging materials; writes no files.' + VIEW_EVIDENCE_GUIDANCE;
43894
43945
  kilnViewInteriorDef = createKilnViewInteriorDef();
43895
- attachmentEndpointInput = z21.object({
43896
- subject: z21.object({ path: z21.string().optional(), name: z21.string().optional() }).strict(),
43946
+ attachmentEndpointInput = z23.object({
43947
+ subject: z23.object({ path: z23.string().optional(), name: z23.string().optional() }).strict(),
43897
43948
  point: cameraVec3Input.optional()
43898
43949
  }).strict();
43899
- surfacePairInput = z21.array(z21.string().max(4096)).length(2);
43900
- inspectInput = z21.object({
43901
- image: z21.boolean().optional().describe("False: requires listParts/measure/surfacePairs/compare; no image or camera controls. Default true."),
43950
+ surfacePairInput = z23.array(z23.string().max(4096)).length(2);
43951
+ inspectInput = z23.object({
43952
+ image: z23.boolean().optional().describe("False: requires listParts/measure/surfacePairs/compare; no image or camera controls. Default true."),
43902
43953
  detail: reviewDetailInput,
43903
43954
  listParts: partListInput.optional().describe("List exported-scene paths, including nested parts. Default 80, max 100 per page. Follow partListing.nextOffset with the same programRef/query. image:false avoids rendering."),
43904
- surfacePairs: z21.array(surfacePairInput).min(1).max(12).optional().describe("[from,to] pairs of exact listParts paths or unambiguous node names; check surfaceMeasurements.status and each result."),
43905
- compare: z21.object({
43906
- programRef: z21.string().regex(programRefPattern),
43907
- offset: z21.number().int().min(0).optional(),
43908
- limit: z21.number().int().min(1).max(100).optional(),
43909
- paths: z21.array(z21.string().max(4096)).min(1).max(12).optional().describe("Exact baseline node paths, scene-prefixed without primitive children. Complete subtree summaries.")
43955
+ surfacePairs: z23.array(surfacePairInput).min(1).max(12).optional().describe("[from,to] pairs of exact listParts paths or unambiguous node names; check surfaceMeasurements.status and each result."),
43956
+ compare: z23.object({
43957
+ programRef: z23.string().regex(programRefPattern),
43958
+ offset: z23.number().int().min(0).optional(),
43959
+ limit: z23.number().int().min(1).max(100).optional(),
43960
+ paths: z23.array(z23.string().max(4096)).min(1).max(12).optional().describe("Exact baseline node paths, scene-prefixed without primitive children. Complete subtree summaries.")
43910
43961
  }).strict().optional().describe("Static geometry/material/transform/bounds under current host settings. Follow nextOffset; paths adds complete subtrees."),
43911
- measure: z21.object({
43912
- mode: z21.enum(["anchors", "surface"]).optional(),
43962
+ measure: z23.object({
43963
+ mode: z23.enum(["anchors", "surface"]).optional(),
43913
43964
  from: attachmentEndpointInput,
43914
43965
  to: attachmentEndpointInput
43915
43966
  }).strict().optional().describe("Default anchors: origin/local-point distance. Surface: disjoint mesh triangles, omit points. Rest pose, asset units. Check status/bounds; no solid clearance/attachment proof."),
43916
43967
  shot: cameraShotInput.optional().describe("Exact shot; omit part/view/orbit controls."),
43917
- code: z21.string().describe("Kiln source code to execute and inspect."),
43918
- part: z21.string().optional().describe("Frame named part and descendants (case-insensitive, substring fallback). Omit for whole asset."),
43919
- view: z21.string().optional().describe("front/right/back/left/top/three-quarter (default). Orbit angles override."),
43920
- azimuthDeg: z21.number().optional().describe("Orbit degrees: 0 front, 90 right, 180 back, 270 left. Wraps."),
43921
- elevationDeg: z21.number().optional().describe("Elevation degrees: 0 eye level, positive above. Clamped -89..89."),
43922
- zoom: z21.number().optional().describe("Bounds padding 1..4; default 1.2. Larger = more context."),
43923
- isolate: z21.boolean().optional().describe("Hide surrounding geometry. Requires part; default false.")
43968
+ code: z23.string().describe("Kiln source code to execute and inspect."),
43969
+ part: z23.string().optional().describe("Frame named part and descendants (case-insensitive, substring fallback). Omit for whole asset."),
43970
+ view: z23.string().optional().describe("front/right/back/left/top/three-quarter (default). Orbit angles override."),
43971
+ azimuthDeg: z23.number().optional().describe("Orbit degrees: 0 front, 90 right, 180 back, 270 left. Wraps."),
43972
+ elevationDeg: z23.number().optional().describe("Elevation degrees: 0 eye level, positive above. Clamped -89..89."),
43973
+ zoom: z23.number().optional().describe("Bounds padding 1..4; default 1.2. Larger = more context."),
43974
+ isolate: z23.boolean().optional().describe("Hide surrounding geometry. Requires part; default false.")
43924
43975
  });
43925
43976
  inspectBufferInput = inspectInput.omit({ code: true });
43926
43977
  inspectAdvertisedInput = inspectInput.extend({ shot: cameraShotRecordInput });
43927
43978
  KILN_INSPECT_DESCRIPTION = "List complete part paths with listParts and image:false; query filters names/paths, offset/limit paginate. Follow partListing.nextOffset. ZOOM IN on one part: renders a single 512x512 close-up framed to the named part (the node name you gave createPart, matched case-insensitively with a substring fallback) and its descendants, " + "from one camera. Use it after kiln_render reveals a suspect region — a floating part, a bad " + "joint, a wrong proportion — to see fine detail one grid cell cannot show. args: part (omit to " + "frame the whole asset in one large view), view (front/right/back/left/top/three-quarter, default three-quarter), azimuthDeg + elevationDeg (orbit to ANY angle instead of a named view: azimuth 0 = front, 90 = right, 180 = back, 270 = left; elevation 0 = eye level, positive looks down, clamped to -89..89), zoom (padding multiplier around the part bounds, 1 = tight crop up to 4 = wide context, default 1.2), isolate (hide everything except that part, default false). Reach for the orbit angles when a named view puts the thing you need to judge edge-on or " + "behind something — the reply always tells you the azimuth/elevation it used, so you can step " + "from there. " + "If the part name does not resolve you get the list of available part names back — pick one and " + "retry. By default surrounding geometry stays visible for context and can occlude the part: either pick a different view, or set isolate:true to hide everything else and see the part unobstructed (use it for anything buried inside or behind other geometry). The view is rendered from deterministic derivative GLB bytes; GPU PBR is used only when it can preserve the requested framing, otherwise the GLB-native geometry-flat fallback reports why in viewFidelity. Writes no files." + VIEW_EVIDENCE_GUIDANCE;
43928
43979
  kilnInspectDef = createKilnInspectDef();
43929
- editOperationInput = z21.object({
43930
- oldString: z21.string().describe("The exact text to replace, copied verbatim from the program (including whitespace and indentation, and with no line-number prefixes). Must be unique unless replaceAll is true."),
43931
- newString: z21.string().describe("The replacement text. Use an empty string to delete."),
43932
- replaceAll: z21.boolean().optional().describe("Replace every occurrence instead of failing when oldString matches more than once.")
43933
- });
43934
- editInput = z21.object({
43935
- code: z21.string().describe("The Kiln program to patch. The full current source."),
43936
- edits: z21.array(editOperationInput).min(1).max(20).describe("Edits applied in order against the program. If any one fails to match, none are applied and the reply says which. Batch related changes into a single call."),
43937
- render: z21.boolean().optional().describe("Render the patched program and return the views (default true). false = patch only."),
43980
+ editOperationInput = z23.object({
43981
+ oldString: z23.string().describe("The exact text to replace, copied verbatim from the program (including whitespace and indentation, and with no line-number prefixes). Must be unique unless replaceAll is true."),
43982
+ newString: z23.string().describe("The replacement text. Use an empty string to delete."),
43983
+ replaceAll: z23.boolean().optional().describe("Replace every occurrence instead of failing when oldString matches more than once.")
43984
+ });
43985
+ editInput = z23.object({
43986
+ code: z23.string().describe("The Kiln program to patch. The full current source."),
43987
+ edits: z23.array(editOperationInput).min(1).max(20).describe("Edits applied in order against the program. If any one fails to match, none are applied and the reply says which. Batch related changes into a single call."),
43988
+ render: z23.boolean().optional().describe("Render the patched program and return the views (default true). false = patch only."),
43938
43989
  capture: captureInput,
43939
43990
  detail: reviewDetailInput
43940
43991
  });
@@ -43944,13 +43995,40 @@ var init_registry2 = __esm(() => {
43944
43995
  shot: "shape:camera-shot",
43945
43996
  capture: "shape:capture"
43946
43997
  };
43947
- rendererInput = z21.strictObject({
43948
- action: z21.enum(["status", "reprobe"]).default("status")
43998
+ rendererInput = z23.strictObject({
43999
+ action: z23.enum(["status", "reprobe"]).default("status")
43949
44000
  });
43950
44001
  assetSelector = {
43951
- collection: z21.string().regex(/^[a-z][a-z0-9_-]{0,79}$/).describe("Collection ID (list with kiln_assets action=collections): the user destination, else project.").default("project"),
43952
- assetId: z21.string().regex(/^[a-z][a-z0-9_-]{0,79}$/),
43953
- revisionId: z21.string().regex(/^[a-z][a-z0-9_-]{0,79}$/)
44002
+ collection: z23.string().regex(/^[a-z][a-z0-9_-]{0,79}$/).describe("Collection ID (list with kiln_assets action=collections): the user destination, else project.").default("project"),
44003
+ assetId: z23.string().regex(/^[a-z][a-z0-9_-]{0,79}$/),
44004
+ revisionId: z23.string().regex(/^[a-z][a-z0-9_-]{0,79}$/)
44005
+ };
44006
+ ASSET_CONTRACT = {
44007
+ collections: {
44008
+ fields: [],
44009
+ required: [],
44010
+ description: "List configured asset storage collections. Collection is not project membership."
44011
+ },
44012
+ catalog: {
44013
+ fields: ["assetId", "query", "offset", "limit"],
44014
+ required: [],
44015
+ description: "Search saved assets across configured collections with pagination."
44016
+ },
44017
+ list: {
44018
+ fields: ["collection", "assetId", "query", "offset", "limit"],
44019
+ required: [],
44020
+ description: "Search saved assets in one collection with pagination."
44021
+ },
44022
+ get: {
44023
+ fields: ["collection", "assetId", "revisionId"],
44024
+ required: ["assetId"],
44025
+ description: "Read a saved revision and its download descriptors. Omit revisionId for the newest revision."
44026
+ },
44027
+ restore: {
44028
+ fields: ["collection", "assetId", "revisionId"],
44029
+ required: ["assetId"],
44030
+ description: "Restore exact saved source as programRef for editing or rendering. Omit revisionId for the newest revision. Does not overwrite the saved revision. Binary-only assets have no source."
44031
+ }
43954
44032
  };
43955
44033
  });
43956
44034
 
@@ -44750,7 +44828,7 @@ var init_loopback = __esm(() => {
44750
44828
  });
44751
44829
 
44752
44830
  // src/workspace-http.ts
44753
- import { z as z22 } from "zod";
44831
+ import { z as z24 } from "zod";
44754
44832
  async function readJson(req, origin) {
44755
44833
  if (req.headers.origin !== origin)
44756
44834
  throw new WorkspaceHttpError(403, "Same-origin writes only");
@@ -44919,11 +44997,11 @@ var init_workspace_http = __esm(() => {
44919
44997
  this.status = status;
44920
44998
  }
44921
44999
  };
44922
- updateSchema = z22.strictObject({
45000
+ updateSchema = z24.strictObject({
44923
45001
  expectedRevision: projectRevisionIdSchema,
44924
45002
  patch: projectPatchSchema
44925
45003
  });
44926
- pinSchema = z22.strictObject({ pinned: z22.boolean() });
45004
+ pinSchema = z24.strictObject({ pinned: z24.boolean() });
44927
45005
  filePattern3 = /^(asset\.glb|source\.kiln\.js|evaluation\.json|capture-\d{1,2}\.png)$/;
44928
45006
  });
44929
45007