wave3d-agent-sdk 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +535 -147
- package/dist/sdk/README.md +3 -3
- package/dist/sdk/manifest.json +26 -26
- package/dist/sdk/wave-engine-sdk-2026-06-24.4.wMh4twvnrJPgyEgJhV6QtcSQvoZClKr3.zip +0 -0
- package/package.json +1 -1
- package/dist/sdk/wave-engine-sdk-2026-06-24.4.TfG1gvbOLSyom3MfrRMlDZCm3Rji9aGt.zip +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { mkdir as
|
|
4
|
+
import { mkdir as mkdir4, readFile as readFile2, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
|
|
5
5
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6
6
|
import { execFile } from "node:child_process";
|
|
7
7
|
import { createHash as createHash6 } from "node:crypto";
|
|
8
8
|
import { homedir as homedir2 } from "node:os";
|
|
9
|
-
import
|
|
9
|
+
import path5 from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
import JSZip from "jszip";
|
|
@@ -14,7 +14,7 @@ import MiniSearch2 from "minisearch";
|
|
|
14
14
|
|
|
15
15
|
// ../../scripts/wavegenie-sdk/server.ts
|
|
16
16
|
import { createServer } from "node:http";
|
|
17
|
-
import { randomUUID as
|
|
17
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18
18
|
import { createReadStream } from "node:fs";
|
|
19
19
|
|
|
20
20
|
// ../../src/lib/waveStudio/aiAssist/core/resultEnvelope.ts
|
|
@@ -182,10 +182,10 @@ var WaveGenieBridgeCommandBroker = class {
|
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
// ../../scripts/wavegenie-sdk/mcp.ts
|
|
185
|
-
import { randomUUID } from "node:crypto";
|
|
186
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
187
|
-
import { tmpdir } from "node:os";
|
|
188
|
-
import
|
|
185
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
186
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
187
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
188
|
+
import path3 from "node:path";
|
|
189
189
|
|
|
190
190
|
// ../../wave-engine/dist/src/core/assetPipeline/registry/gaussianSplatAssetTypes.js
|
|
191
191
|
var WAVE_GAUSSIAN_SPLAT_RUNTIME_ASSET_ROOT = "/defaultAssets/runtime/adobe-spz";
|
|
@@ -25422,12 +25422,12 @@ var Tools = class {
|
|
|
25422
25422
|
* @param path defines the path to use
|
|
25423
25423
|
* @returns the filename
|
|
25424
25424
|
*/
|
|
25425
|
-
static GetFilename(
|
|
25426
|
-
const index =
|
|
25425
|
+
static GetFilename(path6) {
|
|
25426
|
+
const index = path6.lastIndexOf("/");
|
|
25427
25427
|
if (index < 0) {
|
|
25428
|
-
return
|
|
25428
|
+
return path6;
|
|
25429
25429
|
}
|
|
25430
|
-
return
|
|
25430
|
+
return path6.substring(index + 1);
|
|
25431
25431
|
}
|
|
25432
25432
|
/**
|
|
25433
25433
|
* Extracts the "folder" part of a path (everything before the filename).
|
|
@@ -42520,8 +42520,8 @@ function normalizeWaveGenieAssetUploadRequest(request) {
|
|
|
42520
42520
|
|
|
42521
42521
|
// ../../src/lib/waveStudio/aiAssist/bridge/localAgentSdkContract.ts
|
|
42522
42522
|
var WAVE3D_AGENT_SDK_PACKAGE_NAME = "wave3d-agent-sdk";
|
|
42523
|
-
var WAVE3D_AGENT_SDK_REQUIRED_VERSION = "0.2.
|
|
42524
|
-
var WAVE3D_AGENT_SDK_REQUIRED_BUILD = "agent-sdk-20260625.
|
|
42523
|
+
var WAVE3D_AGENT_SDK_REQUIRED_VERSION = "0.2.14";
|
|
42524
|
+
var WAVE3D_AGENT_SDK_REQUIRED_BUILD = "agent-sdk-20260625.2";
|
|
42525
42525
|
var WAVE3D_AGENT_SDK_PACKAGE_SPEC = `${WAVE3D_AGENT_SDK_PACKAGE_NAME}@${WAVE3D_AGENT_SDK_REQUIRED_VERSION}`;
|
|
42526
42526
|
var WAVE3D_AGENT_SDK_START_COMMAND = `npx -y ${WAVE3D_AGENT_SDK_PACKAGE_SPEC} start`;
|
|
42527
42527
|
var WAVE3D_AGENT_SDK_TOKEN_ENV_VAR = "WAVE3D_MCP_TOKEN";
|
|
@@ -42660,7 +42660,12 @@ var WAVE_MCP_TOOL_ALIAS_CANONICAL_NAMES = {
|
|
|
42660
42660
|
mcp_status: "get_wave_mcp_health",
|
|
42661
42661
|
get_mcp_health: "get_wave_mcp_health",
|
|
42662
42662
|
get_mcp_status: "get_wave_mcp_health",
|
|
42663
|
-
get_wave_mcp_status: "get_wave_mcp_health"
|
|
42663
|
+
get_wave_mcp_status: "get_wave_mcp_health",
|
|
42664
|
+
generate_wave_3d_model: "create_wave_3d_modeling_job",
|
|
42665
|
+
create_wave_3d_model: "create_wave_3d_modeling_job",
|
|
42666
|
+
create_wave_3d_model_from_screenshots: "create_wave_3d_modeling_job",
|
|
42667
|
+
make_wave_3d_model_from_screenshot: "create_wave_3d_modeling_job",
|
|
42668
|
+
scaffold_wave_3d_modeling_job: "create_wave_3d_modeling_job"
|
|
42664
42669
|
};
|
|
42665
42670
|
function canonicalizeWaveMcpToolName(toolName) {
|
|
42666
42671
|
return WAVE_MCP_TOOL_ALIAS_CANONICAL_NAMES[toolName] ?? toolName;
|
|
@@ -42802,6 +42807,7 @@ var WAVE_MCP_TOOL_CONTRACTS = {
|
|
|
42802
42807
|
get_wave_runtime_entity_snapshot: { workMode: "observe", domain: "runtime", safety: "readOnly", targetPolicy: "none", requiresTaskRoute: false, supportsTaskRoute: true },
|
|
42803
42808
|
list_wave_runtime_markers: { workMode: "observe", domain: "marker", safety: "readOnly", targetPolicy: "none", requiresTaskRoute: false, supportsTaskRoute: true },
|
|
42804
42809
|
capture_wave_screenshot: { workMode: "observe", domain: "visual", safety: "readOnly", targetPolicy: "none", requiresTaskRoute: false, supportsTaskRoute: true },
|
|
42810
|
+
create_wave_3d_modeling_job: { workMode: "operate", domain: "modeling", safety: "workspaceOperation", targetPolicy: "none", requiresTaskRoute: false, supportsTaskRoute: true, directIntentKind: "asset.manage", directEditIntent: "modifyAssets" },
|
|
42805
42811
|
query_wave_api: { workMode: "author", domain: "code", safety: "authoringMutation", targetPolicy: "none", requiresTaskRoute: true, supportsTaskRoute: true },
|
|
42806
42812
|
edit_wave_file: { workMode: "author", domain: "code", safety: "authoringMutation", targetPolicy: "exactPathRequired", requiresTaskRoute: true, supportsTaskRoute: true },
|
|
42807
42813
|
apply_wave_patch: { workMode: "author", domain: "code", safety: "authoringMutation", targetPolicy: "exactPathRequired", requiresTaskRoute: true, supportsTaskRoute: true },
|
|
@@ -42944,6 +42950,9 @@ var WAVE_MCP_ASSET_STAGING_STORE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
42944
42950
|
"get_wave_asset_upload_status",
|
|
42945
42951
|
"abort_wave_asset_upload"
|
|
42946
42952
|
]);
|
|
42953
|
+
var WAVE_MCP_LOCAL_SDK_WORKSPACE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
42954
|
+
"create_wave_3d_modeling_job"
|
|
42955
|
+
]);
|
|
42947
42956
|
function getWaveMcpToolExecutionScope(toolName) {
|
|
42948
42957
|
const canonicalToolName = canonicalizeWaveMcpToolName(toolName);
|
|
42949
42958
|
if (WAVE_MCP_SESSION_DISCOVERY_TOOL_NAMES.has(canonicalToolName)) return "sessionDiscovery";
|
|
@@ -42952,6 +42961,7 @@ function getWaveMcpToolExecutionScope(toolName) {
|
|
|
42952
42961
|
if (WAVE_MCP_REFERENCE_LOOKUP_TOOL_NAMES.has(canonicalToolName)) return "referenceLookup";
|
|
42953
42962
|
if (canonicalToolName === "get_wave_command_result") return "commandStatus";
|
|
42954
42963
|
if (WAVE_MCP_ASSET_STAGING_STORE_TOOL_NAMES.has(canonicalToolName)) return "assetStagingStore";
|
|
42964
|
+
if (WAVE_MCP_LOCAL_SDK_WORKSPACE_TOOL_NAMES.has(canonicalToolName)) return "localSdkWorkspace";
|
|
42955
42965
|
return "pairedStudioSession";
|
|
42956
42966
|
}
|
|
42957
42967
|
function waveMcpToolRequiresStudioSession(toolName) {
|
|
@@ -42973,6 +42983,7 @@ function waveMcpToolFamily(toolName) {
|
|
|
42973
42983
|
const contract = getWaveMcpToolContract(toolName);
|
|
42974
42984
|
if (contract?.domain === "vfs") return "studio.vfs";
|
|
42975
42985
|
if (contract?.domain === "asset") return "studio.assets";
|
|
42986
|
+
if (contract?.domain === "modeling") return "wave.modeling";
|
|
42976
42987
|
if (contract?.domain === "project") return "studio.project";
|
|
42977
42988
|
if (contract?.domain === "preview") return "studio.preview";
|
|
42978
42989
|
if (contract?.domain === "runtime") return "studio.preview";
|
|
@@ -43720,8 +43731,8 @@ function hasStringProperty(args, names) {
|
|
|
43720
43731
|
return names.some((name) => typeof args[name] === "string");
|
|
43721
43732
|
}
|
|
43722
43733
|
function editWaveFileArgsAreExact(args) {
|
|
43723
|
-
const
|
|
43724
|
-
if (!
|
|
43734
|
+
const path6 = trimString(args?.path, 240);
|
|
43735
|
+
if (!path6) return false;
|
|
43725
43736
|
const hasExactLineRange = isPositiveInteger(args?.startLine) && isPositiveInteger(args?.endLine) && hasStringProperty(args, ["text", "replacement"]);
|
|
43726
43737
|
const hasExactTextReplace = typeof args?.oldText === "string" && args.oldText.length > 0 && hasStringProperty(args, ["newText", "replacement"]);
|
|
43727
43738
|
return hasExactLineRange || hasExactTextReplace;
|
|
@@ -43755,7 +43766,7 @@ function waveMcpToolRoutingNote(toolName) {
|
|
|
43755
43766
|
return " Direct Observe tool: call directly without start_wave_task. Pass taskRouteId only to attach this observation to an active Author/Diagnose route.";
|
|
43756
43767
|
}
|
|
43757
43768
|
if (contract.workMode === "operate") {
|
|
43758
|
-
return " Direct
|
|
43769
|
+
return " Direct Operate tool: when the user asks for this exact operation, call directly without start_wave_task. Pass taskRouteId only if this operation belongs to an active Author/Diagnose route.";
|
|
43759
43770
|
}
|
|
43760
43771
|
if (contract.workMode === "author" && contract.safety === "readOnly") {
|
|
43761
43772
|
return " Direct Author lookup tool: callable without start_wave_task, but for normal code authoring use after intent chunks/Core Intent Pass and skill-family selection.";
|
|
@@ -44090,14 +44101,14 @@ function validateWaveMcpTaskToolCall(input) {
|
|
|
44090
44101
|
function recordWaveMcpTaskEvidenceUse(input) {
|
|
44091
44102
|
let evidence = input.evidence;
|
|
44092
44103
|
const canonicalToolName = canonicalizeWaveMcpToolName(input.toolName);
|
|
44093
|
-
const
|
|
44104
|
+
const path6 = trimString(input.args?.path, 240);
|
|
44094
44105
|
const skillId = trimString(input.args?.skillId, 120) || trimString(input.args?.id, 120);
|
|
44095
44106
|
if (canonicalToolName === "list_wave_files") evidence = { ...evidence, filesListed: true };
|
|
44096
44107
|
if (canonicalToolName === "read_wave_file") {
|
|
44097
44108
|
evidence = {
|
|
44098
44109
|
...evidence,
|
|
44099
44110
|
filesListed: true,
|
|
44100
|
-
filesRead: uniqueStrings([...evidence.filesRead,
|
|
44111
|
+
filesRead: uniqueStrings([...evidence.filesRead, path6].filter(Boolean), 64)
|
|
44101
44112
|
};
|
|
44102
44113
|
}
|
|
44103
44114
|
if (canonicalToolName === "find_wave_assets_by_category") evidence = { ...evidence, assetsListed: true };
|
|
@@ -44794,8 +44805,8 @@ function optionalStringArray(value) {
|
|
|
44794
44805
|
const strings = rawItems.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
44795
44806
|
return strings.length > 0 ? strings : void 0;
|
|
44796
44807
|
}
|
|
44797
|
-
function normalizeWaveFilePath(
|
|
44798
|
-
const trimmed =
|
|
44808
|
+
function normalizeWaveFilePath(path6) {
|
|
44809
|
+
const trimmed = path6.trim();
|
|
44799
44810
|
if (!trimmed) return trimmed;
|
|
44800
44811
|
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
44801
44812
|
}
|
|
@@ -44931,9 +44942,9 @@ function readPathArray(value) {
|
|
|
44931
44942
|
return value.map((item) => {
|
|
44932
44943
|
if (typeof item === "string") return item;
|
|
44933
44944
|
const record = readRecord(item);
|
|
44934
|
-
const
|
|
44935
|
-
return typeof
|
|
44936
|
-
}).filter((
|
|
44945
|
+
const path6 = record?.path;
|
|
44946
|
+
return typeof path6 === "string" ? path6 : null;
|
|
44947
|
+
}).filter((path6) => typeof path6 === "string" && path6.length > 0);
|
|
44937
44948
|
}
|
|
44938
44949
|
function readRecord(value) {
|
|
44939
44950
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -45910,6 +45921,318 @@ async function runLocalSdkLookupTool(input) {
|
|
|
45910
45921
|
);
|
|
45911
45922
|
}
|
|
45912
45923
|
|
|
45924
|
+
// ../../scripts/wavegenie-sdk/modeling/modelingJob.ts
|
|
45925
|
+
import { randomUUID } from "node:crypto";
|
|
45926
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
45927
|
+
import { tmpdir } from "node:os";
|
|
45928
|
+
import path2 from "node:path";
|
|
45929
|
+
function sanitizeAssetName(rawName) {
|
|
45930
|
+
const sanitized = rawName.trim().toLowerCase().replace(/[^a-z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
|
|
45931
|
+
return sanitized || "generated_model";
|
|
45932
|
+
}
|
|
45933
|
+
function jsonValueOrNull(value) {
|
|
45934
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
45935
|
+
return typeof value === "number" && Number.isNaN(value) ? null : value;
|
|
45936
|
+
}
|
|
45937
|
+
if (Array.isArray(value)) {
|
|
45938
|
+
const items = value.map((item) => jsonValueOrNull(item));
|
|
45939
|
+
return items.every((item) => item !== null) ? items : null;
|
|
45940
|
+
}
|
|
45941
|
+
if (isRecord2(value)) {
|
|
45942
|
+
const entries = Object.entries(value);
|
|
45943
|
+
const result = {};
|
|
45944
|
+
for (const [key, item] of entries) {
|
|
45945
|
+
const normalized = jsonValueOrNull(item);
|
|
45946
|
+
if (normalized === null) return null;
|
|
45947
|
+
result[key] = normalized;
|
|
45948
|
+
}
|
|
45949
|
+
return result;
|
|
45950
|
+
}
|
|
45951
|
+
return null;
|
|
45952
|
+
}
|
|
45953
|
+
function normalizeKnownDimensionsMm(value) {
|
|
45954
|
+
if (!isRecord2(value)) return null;
|
|
45955
|
+
const result = {};
|
|
45956
|
+
for (const [key, item] of Object.entries(value)) {
|
|
45957
|
+
if (!key.trim()) continue;
|
|
45958
|
+
const normalized = jsonValueOrNull(item);
|
|
45959
|
+
if (normalized === null) return null;
|
|
45960
|
+
result[key.trim()] = normalized;
|
|
45961
|
+
}
|
|
45962
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
45963
|
+
}
|
|
45964
|
+
function parseModelingJobArgs(args) {
|
|
45965
|
+
const sourcePrompt = normalizeOptionalString(args?.sourcePrompt) ?? normalizeOptionalString(args?.prompt) ?? normalizeOptionalString(args?.description);
|
|
45966
|
+
if (!sourcePrompt) {
|
|
45967
|
+
return errorToolResult(
|
|
45968
|
+
"invalid_arguments",
|
|
45969
|
+
"`create_wave_3d_modeling_job` needs sourcePrompt, prompt, or description."
|
|
45970
|
+
);
|
|
45971
|
+
}
|
|
45972
|
+
const rawAssetName = normalizeOptionalString(args?.assetName) ?? normalizeOptionalString(args?.name) ?? sourcePrompt.split(/\s+/u).slice(0, 5).join("_");
|
|
45973
|
+
const knownDimensionsMm = typeof args?.knownDimensionsMm === "undefined" ? null : normalizeKnownDimensionsMm(args.knownDimensionsMm);
|
|
45974
|
+
if (typeof args?.knownDimensionsMm !== "undefined" && !knownDimensionsMm) {
|
|
45975
|
+
return errorToolResult(
|
|
45976
|
+
"invalid_arguments",
|
|
45977
|
+
"`knownDimensionsMm` must be a JSON object with only JSON-safe values."
|
|
45978
|
+
);
|
|
45979
|
+
}
|
|
45980
|
+
return {
|
|
45981
|
+
assetName: sanitizeAssetName(rawAssetName),
|
|
45982
|
+
sourcePrompt,
|
|
45983
|
+
screenshotPaths: optionalStringArray(args?.screenshotPaths) ?? [],
|
|
45984
|
+
referenceImagePaths: optionalStringArray(args?.referenceImagePaths) ?? [],
|
|
45985
|
+
targetStyle: normalizeOptionalString(args?.targetStyle) ?? null,
|
|
45986
|
+
intendedUse: normalizeOptionalString(args?.intendedUse) ?? null,
|
|
45987
|
+
requiredParts: optionalStringArray(args?.requiredParts) ?? [],
|
|
45988
|
+
knownDimensionsMm
|
|
45989
|
+
};
|
|
45990
|
+
}
|
|
45991
|
+
function markdownList(items, emptyText) {
|
|
45992
|
+
if (items.length === 0) return `- ${emptyText}`;
|
|
45993
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
45994
|
+
}
|
|
45995
|
+
function buildModelingSupportFiles(input) {
|
|
45996
|
+
const briefJson = JSON.stringify({
|
|
45997
|
+
assetName: input.assetName,
|
|
45998
|
+
sourcePrompt: input.sourcePrompt,
|
|
45999
|
+
screenshotPaths: input.screenshotPaths,
|
|
46000
|
+
referenceImagePaths: input.referenceImagePaths,
|
|
46001
|
+
targetStyle: input.targetStyle,
|
|
46002
|
+
intendedUse: input.intendedUse,
|
|
46003
|
+
requiredParts: input.requiredParts,
|
|
46004
|
+
knownDimensionsMm: input.knownDimensionsMm,
|
|
46005
|
+
outputContract: {
|
|
46006
|
+
preferredModelFile: `${input.assetName}.glb`,
|
|
46007
|
+
optionalCadFile: `${input.assetName}.step`,
|
|
46008
|
+
waveStudioImport: "Use create_wave_asset_upload/upload_wave_asset after GLB exists, then author code with models.<assetName>."
|
|
46009
|
+
}
|
|
46010
|
+
}, null, 2);
|
|
46011
|
+
const parameterTemplate = JSON.stringify({
|
|
46012
|
+
assetName: input.assetName,
|
|
46013
|
+
units: "millimeter",
|
|
46014
|
+
parameters: {
|
|
46015
|
+
overall_width: input.knownDimensionsMm?.overall_width ?? 1e3,
|
|
46016
|
+
overall_depth: input.knownDimensionsMm?.overall_depth ?? 600,
|
|
46017
|
+
overall_height: input.knownDimensionsMm?.overall_height ?? 500,
|
|
46018
|
+
bevel_radius: 12
|
|
46019
|
+
},
|
|
46020
|
+
parts: input.requiredParts.length > 0 ? input.requiredParts.map((partName) => ({ name: partName, materialHint: "default" })) : [{ name: "body", materialHint: "default" }]
|
|
46021
|
+
}, null, 2);
|
|
46022
|
+
return [
|
|
46023
|
+
{
|
|
46024
|
+
fileName: "README.md",
|
|
46025
|
+
role: "workflow",
|
|
46026
|
+
content: `# WaveEngine 3D Modeling Job: ${input.assetName}
|
|
46027
|
+
|
|
46028
|
+
This folder is a local SDK modeling scaffold. It does not mean a GLB has been generated yet.
|
|
46029
|
+
|
|
46030
|
+
## Source request
|
|
46031
|
+
|
|
46032
|
+
${input.sourcePrompt}
|
|
46033
|
+
|
|
46034
|
+
## Screenshot inputs
|
|
46035
|
+
|
|
46036
|
+
${markdownList(input.screenshotPaths, "No screenshot paths provided. Use capture_wave_screenshot first when visual matching matters.")}
|
|
46037
|
+
|
|
46038
|
+
## Reference image inputs
|
|
46039
|
+
|
|
46040
|
+
${markdownList(input.referenceImagePaths, "No reference image paths provided.")}
|
|
46041
|
+
|
|
46042
|
+
## Workflow
|
|
46043
|
+
|
|
46044
|
+
1. Use \`cad-sheet-prompt.md\` to derive a CAD sheet or parameter spec from screenshots/reference images.
|
|
46045
|
+
2. Fill \`parameters.template.json\`.
|
|
46046
|
+
3. Run \`python build_step.py parameters.template.json out\` after installing CadQuery/OCP locally.
|
|
46047
|
+
4. Convert the STEP/mesh result to GLB with your approved modeling pipeline.
|
|
46048
|
+
5. Upload the GLB through Wave Studio MCP asset upload tools.
|
|
46049
|
+
6. Author scene code with \`models.${input.assetName}\`.
|
|
46050
|
+
|
|
46051
|
+
## Safety
|
|
46052
|
+
|
|
46053
|
+
Keep API keys and private screenshots out of commits. Generated assets must be reviewed before upload.
|
|
46054
|
+
`
|
|
46055
|
+
},
|
|
46056
|
+
{
|
|
46057
|
+
fileName: "asset-brief.json",
|
|
46058
|
+
role: "brief",
|
|
46059
|
+
content: `${briefJson}
|
|
46060
|
+
`
|
|
46061
|
+
},
|
|
46062
|
+
{
|
|
46063
|
+
fileName: "cad-sheet-prompt.md",
|
|
46064
|
+
role: "prompt",
|
|
46065
|
+
content: `Create a CAD-ready model specification for this WaveEngine asset.
|
|
46066
|
+
|
|
46067
|
+
Asset name: ${input.assetName}
|
|
46068
|
+
Request: ${input.sourcePrompt}
|
|
46069
|
+
Target style: ${input.targetStyle ?? "match source/reference"}
|
|
46070
|
+
Intended use: ${input.intendedUse ?? "WaveEngine scene asset"}
|
|
46071
|
+
Required parts: ${input.requiredParts.length > 0 ? input.requiredParts.join(", ") : "infer minimal public-facing parts"}
|
|
46072
|
+
|
|
46073
|
+
Use the screenshots/reference images as visual evidence. Return:
|
|
46074
|
+
|
|
46075
|
+
- Orthographic front/side/top proportions.
|
|
46076
|
+
- Named visible parts.
|
|
46077
|
+
- Dimensions in millimeters.
|
|
46078
|
+
- Bevels, thickness, symmetry, repeating details.
|
|
46079
|
+
- Material/color hints only. Do not invent private engine internals.
|
|
46080
|
+
- A JSON parameter block compatible with parameters.template.json.
|
|
46081
|
+
`
|
|
46082
|
+
},
|
|
46083
|
+
{
|
|
46084
|
+
fileName: "parameter-spec.schema.json",
|
|
46085
|
+
role: "schema",
|
|
46086
|
+
content: `${JSON.stringify({
|
|
46087
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
46088
|
+
title: "WaveEngine 3D Modeling Parameters",
|
|
46089
|
+
type: "object",
|
|
46090
|
+
required: ["assetName", "units", "parameters", "parts"],
|
|
46091
|
+
additionalProperties: false,
|
|
46092
|
+
properties: {
|
|
46093
|
+
assetName: { type: "string", minLength: 1 },
|
|
46094
|
+
units: { type: "string", enum: ["millimeter"] },
|
|
46095
|
+
parameters: {
|
|
46096
|
+
type: "object",
|
|
46097
|
+
additionalProperties: { type: ["number", "string", "boolean"] }
|
|
46098
|
+
},
|
|
46099
|
+
parts: {
|
|
46100
|
+
type: "array",
|
|
46101
|
+
minItems: 1,
|
|
46102
|
+
items: {
|
|
46103
|
+
type: "object",
|
|
46104
|
+
required: ["name"],
|
|
46105
|
+
additionalProperties: false,
|
|
46106
|
+
properties: {
|
|
46107
|
+
name: { type: "string", minLength: 1 },
|
|
46108
|
+
materialHint: { type: "string" }
|
|
46109
|
+
}
|
|
46110
|
+
}
|
|
46111
|
+
}
|
|
46112
|
+
}
|
|
46113
|
+
}, null, 2)}
|
|
46114
|
+
`
|
|
46115
|
+
},
|
|
46116
|
+
{
|
|
46117
|
+
fileName: "parameters.template.json",
|
|
46118
|
+
role: "parameters",
|
|
46119
|
+
content: `${parameterTemplate}
|
|
46120
|
+
`
|
|
46121
|
+
},
|
|
46122
|
+
{
|
|
46123
|
+
fileName: "build_step.py",
|
|
46124
|
+
role: "python-cad-scaffold",
|
|
46125
|
+
content: `#!/usr/bin/env python3
|
|
46126
|
+
"""WaveEngine local CAD scaffold.
|
|
46127
|
+
|
|
46128
|
+
This is a starting point, not a magic model generator. Replace build_model()
|
|
46129
|
+
with the CAD recipe derived from cad-sheet-prompt.md.
|
|
46130
|
+
"""
|
|
46131
|
+
|
|
46132
|
+
from __future__ import annotations
|
|
46133
|
+
|
|
46134
|
+
import json
|
|
46135
|
+
import pathlib
|
|
46136
|
+
import sys
|
|
46137
|
+
|
|
46138
|
+
try:
|
|
46139
|
+
import cadquery as cq
|
|
46140
|
+
except ImportError as exc:
|
|
46141
|
+
raise SystemExit(
|
|
46142
|
+
"CadQuery is required. Install it in your local modeling environment before running this scaffold."
|
|
46143
|
+
) from exc
|
|
46144
|
+
|
|
46145
|
+
|
|
46146
|
+
def read_params(path: pathlib.Path) -> dict:
|
|
46147
|
+
with path.open("r", encoding="utf-8") as handle:
|
|
46148
|
+
return json.load(handle)
|
|
46149
|
+
|
|
46150
|
+
|
|
46151
|
+
def number_param(params: dict, name: str, fallback: float) -> float:
|
|
46152
|
+
value = params.get("parameters", {}).get(name, fallback)
|
|
46153
|
+
if isinstance(value, (int, float)):
|
|
46154
|
+
return float(value)
|
|
46155
|
+
return fallback
|
|
46156
|
+
|
|
46157
|
+
|
|
46158
|
+
def build_model(params: dict) -> cq.Workplane:
|
|
46159
|
+
width = number_param(params, "overall_width", 1000)
|
|
46160
|
+
depth = number_param(params, "overall_depth", 600)
|
|
46161
|
+
height = number_param(params, "overall_height", 500)
|
|
46162
|
+
bevel_radius = number_param(params, "bevel_radius", 12)
|
|
46163
|
+
model = cq.Workplane("XY").box(width, depth, height)
|
|
46164
|
+
if bevel_radius > 0:
|
|
46165
|
+
model = model.edges().fillet(bevel_radius)
|
|
46166
|
+
return model
|
|
46167
|
+
|
|
46168
|
+
|
|
46169
|
+
def main() -> None:
|
|
46170
|
+
if len(sys.argv) != 3:
|
|
46171
|
+
raise SystemExit("usage: python build_step.py parameters.template.json out")
|
|
46172
|
+
params_path = pathlib.Path(sys.argv[1])
|
|
46173
|
+
out_dir = pathlib.Path(sys.argv[2])
|
|
46174
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
46175
|
+
params = read_params(params_path)
|
|
46176
|
+
asset_name = str(params.get("assetName") or "${input.assetName}")
|
|
46177
|
+
step_path = out_dir / f"{asset_name}.step"
|
|
46178
|
+
cq.exporters.export(build_model(params), str(step_path))
|
|
46179
|
+
print(f"Wrote {step_path}")
|
|
46180
|
+
|
|
46181
|
+
|
|
46182
|
+
if __name__ == "__main__":
|
|
46183
|
+
main()
|
|
46184
|
+
`
|
|
46185
|
+
},
|
|
46186
|
+
{
|
|
46187
|
+
fileName: "validation-checklist.md",
|
|
46188
|
+
role: "validation",
|
|
46189
|
+
content: `# Validation checklist
|
|
46190
|
+
|
|
46191
|
+
- Silhouette matches source screenshots from front/side/top.
|
|
46192
|
+
- Scale is correct for WaveEngine world use.
|
|
46193
|
+
- Parts are named for future objectModel/part authoring where useful.
|
|
46194
|
+
- Material slots are clean and simple.
|
|
46195
|
+
- GLB loads in Wave Studio before code authoring.
|
|
46196
|
+
- Uploaded asset appears as \`models.${input.assetName}\`.
|
|
46197
|
+
`
|
|
46198
|
+
}
|
|
46199
|
+
];
|
|
46200
|
+
}
|
|
46201
|
+
async function createWave3dModelingJobTool(args) {
|
|
46202
|
+
const parsed = parseModelingJobArgs(args);
|
|
46203
|
+
if ("content" in parsed) return parsed;
|
|
46204
|
+
const jobId = `wave_model_${parsed.assetName}_${randomUUID().slice(0, 8)}`;
|
|
46205
|
+
const jobDir = path2.join(tmpdir(), "wave3d-agent-sdk", "modeling-jobs", jobId);
|
|
46206
|
+
const supportFiles = buildModelingSupportFiles(parsed);
|
|
46207
|
+
await mkdir(jobDir, { recursive: true });
|
|
46208
|
+
await Promise.all(supportFiles.map((file) => writeFile(path2.join(jobDir, file.fileName), file.content, "utf8")));
|
|
46209
|
+
return successToolResult({
|
|
46210
|
+
ok: true,
|
|
46211
|
+
tool: "create_wave_3d_modeling_job",
|
|
46212
|
+
status: "scaffolded",
|
|
46213
|
+
localSdk: true,
|
|
46214
|
+
generatedAssetReady: false,
|
|
46215
|
+
jobId,
|
|
46216
|
+
jobDir,
|
|
46217
|
+
assetName: parsed.assetName,
|
|
46218
|
+
supportFiles: supportFiles.map((file) => ({
|
|
46219
|
+
fileName: file.fileName,
|
|
46220
|
+
role: file.role,
|
|
46221
|
+
path: path2.join(jobDir, file.fileName)
|
|
46222
|
+
})),
|
|
46223
|
+
nextActions: [
|
|
46224
|
+
"Use cad-sheet-prompt.md plus screenshots/reference images to produce a CAD parameter spec.",
|
|
46225
|
+
"Fill parameters.template.json and run the Python scaffold only in a local modeling environment with CadQuery installed.",
|
|
46226
|
+
"Convert generated model output to GLB, then upload with create_wave_asset_upload/upload_wave_asset.",
|
|
46227
|
+
`After upload, author WaveEngine scene code with models.${parsed.assetName}.`
|
|
46228
|
+
],
|
|
46229
|
+
caveats: [
|
|
46230
|
+
"This tool creates a modeling job scaffold only; it does not call a cloud model generator.",
|
|
46231
|
+
"Do not commit private screenshots, prompts, or generated assets without review."
|
|
46232
|
+
]
|
|
46233
|
+
});
|
|
46234
|
+
}
|
|
46235
|
+
|
|
45913
46236
|
// ../../src/lib/waveStudio/aiAssist/bridge/assetListGuidance.ts
|
|
45914
46237
|
function buildWaveAssetListGuidance(input) {
|
|
45915
46238
|
const hasQuery = typeof input.query === "string" && input.query.trim().length > 0;
|
|
@@ -46401,7 +46724,7 @@ function reportHotReloadUnsafeUsage(params) {
|
|
|
46401
46724
|
})
|
|
46402
46725
|
});
|
|
46403
46726
|
}
|
|
46404
|
-
function collectRuntimeHotReloadSafetyDiagnostics(
|
|
46727
|
+
function collectRuntimeHotReloadSafetyDiagnostics(path6, code, limit = HOT_RELOAD_UNSAFE_ERROR_LIMIT) {
|
|
46405
46728
|
const diagnostics = [];
|
|
46406
46729
|
if (limit <= 0) return diagnostics;
|
|
46407
46730
|
const reported = /* @__PURE__ */ new Set();
|
|
@@ -46446,34 +46769,34 @@ function collectRuntimeHotReloadSafetyDiagnostics(path5, code, limit = HOT_RELOA
|
|
|
46446
46769
|
const receiverPath = previousChar === "." ? readPropertyReceiverPath(code, tokenStart) : "";
|
|
46447
46770
|
const globalGuidance = HOT_RELOAD_UNSAFE_GLOBAL_CALLBACKS.get(token);
|
|
46448
46771
|
if (globalGuidance) {
|
|
46449
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46772
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: globalGuidance, diagnostics, reported });
|
|
46450
46773
|
continue;
|
|
46451
46774
|
}
|
|
46452
46775
|
const listenerGuidance = HOT_RELOAD_UNSAFE_LISTENER_CALLBACKS.get(token);
|
|
46453
46776
|
if (listenerGuidance && isCall) {
|
|
46454
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46777
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: listenerGuidance, diagnostics, reported });
|
|
46455
46778
|
continue;
|
|
46456
46779
|
}
|
|
46457
46780
|
const promiseGuidance = HOT_RELOAD_UNSAFE_PROMISE_CALLBACKS.get(token);
|
|
46458
46781
|
if (promiseGuidance && previousChar === "." && isCall) {
|
|
46459
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46782
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: promiseGuidance, diagnostics, reported });
|
|
46460
46783
|
continue;
|
|
46461
46784
|
}
|
|
46462
46785
|
const eventBusGuidance = HOT_RELOAD_UNSAFE_EVENTBUS_CALLBACKS.get(token);
|
|
46463
46786
|
if (eventBusGuidance && previousChar === "." && isCall && isWaveEventBusReceiverPath(receiverPath)) {
|
|
46464
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46787
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: eventBusGuidance, diagnostics, reported });
|
|
46465
46788
|
continue;
|
|
46466
46789
|
}
|
|
46467
46790
|
const sceneEventGuidance = HOT_RELOAD_UNSAFE_SCENE_EVENT_CALLBACKS.get(token);
|
|
46468
46791
|
if (sceneEventGuidance && previousChar === "." && isCall && isWaveSceneEventReceiverPath(receiverPath)) {
|
|
46469
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46792
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: sceneEventGuidance, diagnostics, reported });
|
|
46470
46793
|
continue;
|
|
46471
46794
|
}
|
|
46472
46795
|
const forbiddenStudioSceneMethodGuidance = HOT_RELOAD_FORBIDDEN_STUDIO_SCENE_METHODS.get(token);
|
|
46473
46796
|
if (forbiddenStudioSceneMethodGuidance && previousChar === "." && isCall && isWaveSceneEventReceiverPath(receiverPath)) {
|
|
46474
46797
|
reportHotReloadUnsafeUsage({
|
|
46475
46798
|
code,
|
|
46476
|
-
path:
|
|
46799
|
+
path: path6,
|
|
46477
46800
|
token,
|
|
46478
46801
|
offset: tokenStart,
|
|
46479
46802
|
severity: "error",
|
|
@@ -46485,12 +46808,12 @@ function collectRuntimeHotReloadSafetyDiagnostics(path5, code, limit = HOT_RELOA
|
|
|
46485
46808
|
}
|
|
46486
46809
|
const constructorGuidance = HOT_RELOAD_UNSAFE_CONSTRUCTORS.get(token);
|
|
46487
46810
|
if (constructorGuidance && (previousIdentifier === "new" || previousChar === "." && isCall)) {
|
|
46488
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46811
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: constructorGuidance, diagnostics, reported });
|
|
46489
46812
|
continue;
|
|
46490
46813
|
}
|
|
46491
46814
|
const eventHandlerGuidance = HOT_RELOAD_UNSAFE_EVENT_HANDLER_ASSIGNMENTS.get(token);
|
|
46492
46815
|
if (eventHandlerGuidance && previousChar === "." && nextNonWhitespace === "=") {
|
|
46493
|
-
reportHotReloadUnsafeUsage({ code, path:
|
|
46816
|
+
reportHotReloadUnsafeUsage({ code, path: path6, token, offset: tokenStart, guidance: eventHandlerGuidance, diagnostics, reported });
|
|
46494
46817
|
}
|
|
46495
46818
|
}
|
|
46496
46819
|
return diagnostics;
|
|
@@ -46883,16 +47206,16 @@ function parseMutationOperations(value) {
|
|
|
46883
47206
|
}
|
|
46884
47207
|
return operations;
|
|
46885
47208
|
}
|
|
46886
|
-
function isAuthoredCodePath(
|
|
46887
|
-
const normalized =
|
|
47209
|
+
function isAuthoredCodePath(path6) {
|
|
47210
|
+
const normalized = path6.trim().toLowerCase();
|
|
46888
47211
|
return normalized.endsWith(".ts") || normalized.endsWith(".js");
|
|
46889
47212
|
}
|
|
46890
|
-
function warnUnsafeCallbackUsage(
|
|
46891
|
-
if (!isAuthoredCodePath(
|
|
46892
|
-
const diagnostics = collectRuntimeHotReloadSafetyDiagnostics(
|
|
47213
|
+
function warnUnsafeCallbackUsage(path6, code) {
|
|
47214
|
+
if (!isAuthoredCodePath(path6) || !code.trim()) return;
|
|
47215
|
+
const diagnostics = collectRuntimeHotReloadSafetyDiagnostics(path6, code);
|
|
46893
47216
|
if (diagnostics.length === 0) return;
|
|
46894
47217
|
console.warn([
|
|
46895
|
-
`[WaveStudio][HotReloadSafety] Strong warning for ${
|
|
47218
|
+
`[WaveStudio][HotReloadSafety] Strong warning for ${path6}: code edits introduced unmanaged callback roots. The write is allowed, but preview will show a memory leak warning.`,
|
|
46896
47219
|
"Use Wave-owned callbacks, animation DSL, entity lifecycle/tick/state APIs, or Director/input APIs instead.",
|
|
46897
47220
|
...diagnostics.map((diagnostic) => diagnostic.message)
|
|
46898
47221
|
].join("\n"));
|
|
@@ -47055,8 +47378,8 @@ async function handleVfsTool(input) {
|
|
|
47055
47378
|
return errorToolResult("session_not_found", LOCAL_SESSION_NOT_FOUND_MESSAGE);
|
|
47056
47379
|
}
|
|
47057
47380
|
const pathInput = typeof args?.path === "string" ? args.path : typeof args?.filePath === "string" ? args.filePath : "";
|
|
47058
|
-
const
|
|
47059
|
-
if (!
|
|
47381
|
+
const path6 = normalizeWaveFilePath(pathInput);
|
|
47382
|
+
if (!path6) {
|
|
47060
47383
|
return errorToolResult("invalid_arguments", "`read_wave_file` requires a non-empty string `path` (alias: `filePath`).");
|
|
47061
47384
|
}
|
|
47062
47385
|
const startLine = normalizeOptionalPositiveInteger(args?.startLine);
|
|
@@ -47075,7 +47398,7 @@ async function handleVfsTool(input) {
|
|
|
47075
47398
|
try {
|
|
47076
47399
|
const result = await context.commandBroker.enqueue(session.sessionId, {
|
|
47077
47400
|
type: "readFile",
|
|
47078
|
-
path:
|
|
47401
|
+
path: path6,
|
|
47079
47402
|
...ifMatchHash ? { ifMatchHash } : {},
|
|
47080
47403
|
...typeof effectiveStartLine === "number" ? { startLine: effectiveStartLine } : {},
|
|
47081
47404
|
...typeof endLine === "number" ? { endLine } : {}
|
|
@@ -47097,7 +47420,7 @@ async function handleVfsTool(input) {
|
|
|
47097
47420
|
...result.payload.range ? { editHint: "This was a scoped line read. For edits in this slice, prefer edit_wave_file with the same full-file line numbers. baseHash is optional for simple edits." } : {}
|
|
47098
47421
|
});
|
|
47099
47422
|
} catch (error) {
|
|
47100
|
-
return errorToolResult("command_timeout", error instanceof Error ? error.message : "WaveEngine Agent SDK file read timed out.", { path:
|
|
47423
|
+
return errorToolResult("command_timeout", error instanceof Error ? error.message : "WaveEngine Agent SDK file read timed out.", { path: path6 });
|
|
47101
47424
|
}
|
|
47102
47425
|
}
|
|
47103
47426
|
if (name === "edit_wave_file") {
|
|
@@ -47109,21 +47432,21 @@ async function handleVfsTool(input) {
|
|
|
47109
47432
|
]);
|
|
47110
47433
|
if ("result" in contentResult) return contentResult.result;
|
|
47111
47434
|
if (typeof args?.blockContaining !== "undefined" || typeof args?.replaceBlockContaining !== "undefined" || typeof args?.deleteStatementContaining !== "undefined" || typeof args?.replaceBetween !== "undefined") {
|
|
47112
|
-
const
|
|
47113
|
-
if (!
|
|
47435
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47436
|
+
if (!path6) return errorToolResult("invalid_arguments", "`edit_wave_file` requires a non-empty string `path`.");
|
|
47114
47437
|
const deleteNeedle = normalizeOptionalString(args?.deleteStatementContaining);
|
|
47115
47438
|
const replacementResult = deleteNeedle ? { value: "" } : resolveReplacementAlias("text", args?.text, args?.replacement);
|
|
47116
47439
|
if ("result" in replacementResult) return replacementResult.result;
|
|
47117
47440
|
if (typeof replacementResult.value !== "string") {
|
|
47118
47441
|
return errorToolResult("invalid_arguments", "Block edit requires `text`/`replacement`, or `deleteStatementContaining` for deletion.");
|
|
47119
47442
|
}
|
|
47120
|
-
warnUnsafeCallbackUsage(
|
|
47443
|
+
warnUnsafeCallbackUsage(path6, replacementResult.value);
|
|
47121
47444
|
const sessionResult = resolveWritableSession(args, context);
|
|
47122
47445
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47123
47446
|
const resolvedHash = await resolveEditBaseHash({
|
|
47124
47447
|
context,
|
|
47125
47448
|
session: sessionResult.session,
|
|
47126
|
-
path:
|
|
47449
|
+
path: path6,
|
|
47127
47450
|
baseHash: args?.baseHash
|
|
47128
47451
|
});
|
|
47129
47452
|
if ("result" in resolvedHash) return resolvedHash.result;
|
|
@@ -47145,7 +47468,7 @@ async function handleVfsTool(input) {
|
|
|
47145
47468
|
session: sessionResult.session,
|
|
47146
47469
|
operations: [{
|
|
47147
47470
|
type: "lineEdits",
|
|
47148
|
-
path:
|
|
47471
|
+
path: path6,
|
|
47149
47472
|
baseHash: resolvedHash.baseHash,
|
|
47150
47473
|
edits: [{
|
|
47151
47474
|
startLine: range.startLine,
|
|
@@ -47159,7 +47482,7 @@ async function handleVfsTool(input) {
|
|
|
47159
47482
|
return withEditBaseHashMetadata(result, resolvedHash, {
|
|
47160
47483
|
editMode: deleteNeedle ? "deleteStatementContaining" : "replaceBlockContaining",
|
|
47161
47484
|
editTarget: {
|
|
47162
|
-
path:
|
|
47485
|
+
path: path6,
|
|
47163
47486
|
startLine: range.startLine,
|
|
47164
47487
|
endLine: range.endLine,
|
|
47165
47488
|
targetReason: range.reason,
|
|
@@ -47172,28 +47495,28 @@ async function handleVfsTool(input) {
|
|
|
47172
47495
|
const editArray = parseEditWaveFileEditsArray(args?.edits);
|
|
47173
47496
|
if (editArray) {
|
|
47174
47497
|
if ("result" in editArray) return editArray.result;
|
|
47175
|
-
const
|
|
47176
|
-
if (!
|
|
47498
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47499
|
+
if (!path6) return errorToolResult("invalid_arguments", "`edit_wave_file` requires a non-empty string `path`.");
|
|
47177
47500
|
for (const edit of editArray.edits) {
|
|
47178
|
-
warnUnsafeCallbackUsage(
|
|
47501
|
+
warnUnsafeCallbackUsage(path6, edit.text);
|
|
47179
47502
|
}
|
|
47180
47503
|
const sessionResult = resolveWritableSession(args, context);
|
|
47181
47504
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47182
47505
|
const resolvedHash = await resolveEditBaseHash({
|
|
47183
47506
|
context,
|
|
47184
47507
|
session: sessionResult.session,
|
|
47185
|
-
path:
|
|
47508
|
+
path: path6,
|
|
47186
47509
|
baseHash: args?.baseHash
|
|
47187
47510
|
});
|
|
47188
47511
|
if ("result" in resolvedHash) return resolvedHash.result;
|
|
47189
47512
|
const operations = editArray.type === "lineEdits" ? [{
|
|
47190
47513
|
type: "lineEdits",
|
|
47191
|
-
path:
|
|
47514
|
+
path: path6,
|
|
47192
47515
|
baseHash: resolvedHash.baseHash,
|
|
47193
47516
|
edits: editArray.edits
|
|
47194
47517
|
}] : [{
|
|
47195
47518
|
type: "textEdits",
|
|
47196
|
-
path:
|
|
47519
|
+
path: path6,
|
|
47197
47520
|
baseHash: resolvedHash.baseHash,
|
|
47198
47521
|
edits: editArray.edits
|
|
47199
47522
|
}];
|
|
@@ -47207,17 +47530,17 @@ async function handleVfsTool(input) {
|
|
|
47207
47530
|
return withEditBaseHashMetadata(result, resolvedHash, {
|
|
47208
47531
|
editMode: editArray.type === "lineEdits" ? "multiLineEdits" : "multiTextEdits",
|
|
47209
47532
|
editTarget: {
|
|
47210
|
-
path:
|
|
47533
|
+
path: path6,
|
|
47211
47534
|
editCount: editArray.edits.length
|
|
47212
47535
|
}
|
|
47213
47536
|
});
|
|
47214
47537
|
}
|
|
47215
47538
|
const inferredMode = normalizeEditWaveFileMode(args?.mode) ?? (requireNonEmptyString(args?.oldText) ? "replaceText" : typeof args?.startLine !== "undefined" || typeof args?.endLine !== "undefined" ? "replaceLines" : typeof contentResult.value === "string" ? "replaceWholeFile" : "");
|
|
47216
47539
|
if (inferredMode === "replaceLines") {
|
|
47217
|
-
const
|
|
47540
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47218
47541
|
const startLine = normalizeOptionalPositiveInteger(args?.startLine);
|
|
47219
47542
|
const endLine = normalizeOptionalPositiveInteger(args?.endLine);
|
|
47220
|
-
if (!
|
|
47543
|
+
if (!path6) return errorToolResult("invalid_arguments", "`edit_wave_file` requires a non-empty string `path`.");
|
|
47221
47544
|
if (startLine === null || typeof startLine === "undefined") {
|
|
47222
47545
|
return errorToolResult("invalid_arguments", "`startLine` must be a positive integer.");
|
|
47223
47546
|
}
|
|
@@ -47232,13 +47555,13 @@ async function handleVfsTool(input) {
|
|
|
47232
47555
|
if (typeof replacementResult.value !== "string") {
|
|
47233
47556
|
return errorToolResult("invalid_arguments", "`text` or `replacement` must be a string. Use an empty string to delete the line range.");
|
|
47234
47557
|
}
|
|
47235
|
-
warnUnsafeCallbackUsage(
|
|
47558
|
+
warnUnsafeCallbackUsage(path6, replacementResult.value);
|
|
47236
47559
|
const sessionResult = resolveWritableSession(args, context);
|
|
47237
47560
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47238
47561
|
const resolvedHash = await resolveEditBaseHash({
|
|
47239
47562
|
context,
|
|
47240
47563
|
session: sessionResult.session,
|
|
47241
|
-
path:
|
|
47564
|
+
path: path6,
|
|
47242
47565
|
baseHash: args?.baseHash
|
|
47243
47566
|
});
|
|
47244
47567
|
if ("result" in resolvedHash) return resolvedHash.result;
|
|
@@ -47262,7 +47585,7 @@ async function handleVfsTool(input) {
|
|
|
47262
47585
|
session: sessionResult.session,
|
|
47263
47586
|
operations: [{
|
|
47264
47587
|
type: "lineEdits",
|
|
47265
|
-
path:
|
|
47588
|
+
path: path6,
|
|
47266
47589
|
baseHash: resolvedHash.baseHash,
|
|
47267
47590
|
edits: [{
|
|
47268
47591
|
startLine,
|
|
@@ -47276,7 +47599,7 @@ async function handleVfsTool(input) {
|
|
|
47276
47599
|
return withEditBaseHashMetadata(result, resolvedHash, {
|
|
47277
47600
|
editMode: "replaceLines",
|
|
47278
47601
|
editTarget: {
|
|
47279
|
-
path:
|
|
47602
|
+
path: path6,
|
|
47280
47603
|
startLine,
|
|
47281
47604
|
endLine: finalEndLine,
|
|
47282
47605
|
requestedEndLine,
|
|
@@ -47289,18 +47612,18 @@ async function handleVfsTool(input) {
|
|
|
47289
47612
|
});
|
|
47290
47613
|
}
|
|
47291
47614
|
if (inferredMode === "replaceText") {
|
|
47292
|
-
const
|
|
47615
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47293
47616
|
const oldText = requireNonEmptyString(args?.oldText);
|
|
47294
|
-
if (!
|
|
47617
|
+
if (!path6) return errorToolResult("invalid_arguments", "`edit_wave_file` requires a non-empty string `path`.");
|
|
47295
47618
|
if (!oldText) {
|
|
47296
47619
|
if (typeof contentResult.value === "string") {
|
|
47297
|
-
warnUnsafeCallbackUsage(
|
|
47620
|
+
warnUnsafeCallbackUsage(path6, contentResult.value);
|
|
47298
47621
|
const sessionResult2 = resolveWritableSession(args, context);
|
|
47299
47622
|
if ("result" in sessionResult2) return sessionResult2.result;
|
|
47300
47623
|
const resolvedHash2 = await resolveEditBaseHash({
|
|
47301
47624
|
context,
|
|
47302
47625
|
session: sessionResult2.session,
|
|
47303
|
-
path:
|
|
47626
|
+
path: path6,
|
|
47304
47627
|
baseHash: args?.baseHash
|
|
47305
47628
|
});
|
|
47306
47629
|
if ("result" in resolvedHash2) return resolvedHash2.result;
|
|
@@ -47309,7 +47632,7 @@ async function handleVfsTool(input) {
|
|
|
47309
47632
|
session: sessionResult2.session,
|
|
47310
47633
|
operations: [{
|
|
47311
47634
|
type: "writeFile",
|
|
47312
|
-
path:
|
|
47635
|
+
path: path6,
|
|
47313
47636
|
baseHash: resolvedHash2.baseHash,
|
|
47314
47637
|
content: contentResult.value
|
|
47315
47638
|
}],
|
|
@@ -47325,7 +47648,7 @@ async function handleVfsTool(input) {
|
|
|
47325
47648
|
if (typeof replacementResult.value !== "string") {
|
|
47326
47649
|
return errorToolResult("invalid_arguments", "`newText` or `replacement` must be a string. Use an empty string to delete the matched text.");
|
|
47327
47650
|
}
|
|
47328
|
-
warnUnsafeCallbackUsage(
|
|
47651
|
+
warnUnsafeCallbackUsage(path6, replacementResult.value);
|
|
47329
47652
|
const sessionResult = resolveWritableSession(args, context);
|
|
47330
47653
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47331
47654
|
const hasOccurrenceIndex = typeof args?.occurrenceIndex !== "undefined";
|
|
@@ -47338,7 +47661,7 @@ async function handleVfsTool(input) {
|
|
|
47338
47661
|
const resolvedHash = await resolveEditBaseHash({
|
|
47339
47662
|
context,
|
|
47340
47663
|
session: sessionResult.session,
|
|
47341
|
-
path:
|
|
47664
|
+
path: path6,
|
|
47342
47665
|
baseHash: args?.baseHash
|
|
47343
47666
|
});
|
|
47344
47667
|
if ("result" in resolvedHash) return resolvedHash.result;
|
|
@@ -47347,7 +47670,7 @@ async function handleVfsTool(input) {
|
|
|
47347
47670
|
session: sessionResult.session,
|
|
47348
47671
|
operations: [{
|
|
47349
47672
|
type: "searchReplace",
|
|
47350
|
-
path:
|
|
47673
|
+
path: path6,
|
|
47351
47674
|
baseHash: resolvedHash.baseHash,
|
|
47352
47675
|
oldText,
|
|
47353
47676
|
newText: replacementResult.value,
|
|
@@ -47360,15 +47683,15 @@ async function handleVfsTool(input) {
|
|
|
47360
47683
|
return withEditBaseHashMetadata(result, resolvedHash);
|
|
47361
47684
|
}
|
|
47362
47685
|
if (inferredMode === "replaceWholeFile" && typeof contentResult.value === "string") {
|
|
47363
|
-
const
|
|
47364
|
-
if (!
|
|
47365
|
-
warnUnsafeCallbackUsage(
|
|
47686
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47687
|
+
if (!path6) return errorToolResult("invalid_arguments", "`edit_wave_file` requires a non-empty string `path`.");
|
|
47688
|
+
warnUnsafeCallbackUsage(path6, contentResult.value);
|
|
47366
47689
|
const sessionResult = resolveWritableSession(args, context);
|
|
47367
47690
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47368
47691
|
const resolvedHash = await resolveEditBaseHash({
|
|
47369
47692
|
context,
|
|
47370
47693
|
session: sessionResult.session,
|
|
47371
|
-
path:
|
|
47694
|
+
path: path6,
|
|
47372
47695
|
baseHash: args?.baseHash
|
|
47373
47696
|
});
|
|
47374
47697
|
if ("result" in resolvedHash) return resolvedHash.result;
|
|
@@ -47377,7 +47700,7 @@ async function handleVfsTool(input) {
|
|
|
47377
47700
|
session: sessionResult.session,
|
|
47378
47701
|
operations: [{
|
|
47379
47702
|
type: "writeFile",
|
|
47380
|
-
path:
|
|
47703
|
+
path: path6,
|
|
47381
47704
|
baseHash: resolvedHash.baseHash,
|
|
47382
47705
|
content: contentResult.value
|
|
47383
47706
|
}],
|
|
@@ -47389,12 +47712,12 @@ async function handleVfsTool(input) {
|
|
|
47389
47712
|
return errorToolResult("invalid_arguments", "`edit_wave_file` could not infer the edit shape. Use oldText+newText, startLine+endLine+text, or path+text/content for whole-file replacement.");
|
|
47390
47713
|
}
|
|
47391
47714
|
if (name === "create_wave_file") {
|
|
47392
|
-
const
|
|
47393
|
-
if (!
|
|
47715
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47716
|
+
if (!path6) return errorToolResult("invalid_arguments", "`create_wave_file` requires a non-empty string `path`.");
|
|
47394
47717
|
if (typeof args?.content !== "string") {
|
|
47395
47718
|
return errorToolResult("invalid_arguments", "`content` must be a string.");
|
|
47396
47719
|
}
|
|
47397
|
-
warnUnsafeCallbackUsage(
|
|
47720
|
+
warnUnsafeCallbackUsage(path6, args.content);
|
|
47398
47721
|
const overwrite = args?.overwrite === true || args?.forceOverwrite === true || args?.replaceExisting === true;
|
|
47399
47722
|
const sessionResult = resolveWritableSession(args, context);
|
|
47400
47723
|
if ("result" in sessionResult) return sessionResult.result;
|
|
@@ -47403,7 +47726,7 @@ async function handleVfsTool(input) {
|
|
|
47403
47726
|
session: sessionResult.session,
|
|
47404
47727
|
operations: [{
|
|
47405
47728
|
type: "createFile",
|
|
47406
|
-
path:
|
|
47729
|
+
path: path6,
|
|
47407
47730
|
content: args.content,
|
|
47408
47731
|
runnable: typeof args?.runnable === "boolean" ? args.runnable : true,
|
|
47409
47732
|
...overwrite ? { overwrite: true } : {}
|
|
@@ -47434,14 +47757,14 @@ async function handleVfsTool(input) {
|
|
|
47434
47757
|
if (name === "delete_wave_file") {
|
|
47435
47758
|
const sessionResult = resolveWritableSession(args, context);
|
|
47436
47759
|
if ("result" in sessionResult) return sessionResult.result;
|
|
47437
|
-
const
|
|
47438
|
-
if (!
|
|
47760
|
+
const path6 = typeof args?.path === "string" ? normalizeWaveFilePath(args.path) : "";
|
|
47761
|
+
if (!path6) return errorToolResult("invalid_arguments", "`delete_wave_file` requires a non-empty string `path`.");
|
|
47439
47762
|
return await applyMutationOperations({
|
|
47440
47763
|
context,
|
|
47441
47764
|
session: sessionResult.session,
|
|
47442
47765
|
operations: [{
|
|
47443
47766
|
type: "deleteFile",
|
|
47444
|
-
path:
|
|
47767
|
+
path: path6
|
|
47445
47768
|
}],
|
|
47446
47769
|
...runtimeVerificationArgs(args)
|
|
47447
47770
|
});
|
|
@@ -49497,7 +49820,11 @@ var LOCAL_TOOL_DEFINITION_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
|
49497
49820
|
var LOCAL_EXTRA_TOOL_DEFINITION_NAMES = /* @__PURE__ */ new Set([
|
|
49498
49821
|
"self_check_wave_mcp",
|
|
49499
49822
|
"list_wave_sessions",
|
|
49500
|
-
"get_active_wave_session"
|
|
49823
|
+
"get_active_wave_session",
|
|
49824
|
+
"create_wave_3d_modeling_job"
|
|
49825
|
+
]);
|
|
49826
|
+
var LOCAL_SDK_MODELING_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
49827
|
+
"create_wave_3d_modeling_job"
|
|
49501
49828
|
]);
|
|
49502
49829
|
var LOCAL_OPTIONAL_SESSION_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
49503
49830
|
"get_wave_session",
|
|
@@ -49566,6 +49893,7 @@ var LOCAL_SDK_CONTROL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
49566
49893
|
var LOCAL_SDK_IMPLEMENTED_CANONICAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
49567
49894
|
...LOCAL_SDK_CONTROL_TOOL_NAMES,
|
|
49568
49895
|
...LOCAL_SDK_LOOKUP_TOOL_NAMES,
|
|
49896
|
+
...LOCAL_SDK_MODELING_TOOL_NAMES,
|
|
49569
49897
|
...[...LOCAL_OPTIONAL_SESSION_TOOL_NAMES].map((name) => canonicalizeWaveMcpToolName(name))
|
|
49570
49898
|
]);
|
|
49571
49899
|
function isLocalMcpToolImplemented(name) {
|
|
@@ -49688,6 +50016,60 @@ function getReadToolDefinitions() {
|
|
|
49688
50016
|
additionalProperties: false
|
|
49689
50017
|
}
|
|
49690
50018
|
},
|
|
50019
|
+
{
|
|
50020
|
+
name: "create_wave_3d_modeling_job",
|
|
50021
|
+
description: "[wave.modeling] Create a local SDK 3D-modeling job scaffold from a prompt and optional screenshots/reference image paths. Writes README, CAD prompt, JSON parameter template/schema, Python CadQuery STEP scaffold, and validation checklist into a temp job folder. Does not generate/upload a GLB by itself; use asset upload tools after a real model file exists. Aliases: generate_wave_3d_model, create_wave_3d_model, create_wave_3d_model_from_screenshots.",
|
|
50022
|
+
inputSchema: {
|
|
50023
|
+
type: "object",
|
|
50024
|
+
properties: {
|
|
50025
|
+
sourcePrompt: {
|
|
50026
|
+
type: "string",
|
|
50027
|
+
description: 'Required modeling goal, e.g. "make a low-poly sea lion matching the screenshot". Aliases accepted by handler: prompt, description.'
|
|
50028
|
+
},
|
|
50029
|
+
prompt: {
|
|
50030
|
+
type: "string",
|
|
50031
|
+
description: "Alias for sourcePrompt."
|
|
50032
|
+
},
|
|
50033
|
+
description: {
|
|
50034
|
+
type: "string",
|
|
50035
|
+
description: "Alias for sourcePrompt."
|
|
50036
|
+
},
|
|
50037
|
+
assetName: {
|
|
50038
|
+
type: "string",
|
|
50039
|
+
description: "Optional public Wave asset/model name stem. Handler sanitizes to lowercase snake_case."
|
|
50040
|
+
},
|
|
50041
|
+
screenshotPaths: {
|
|
50042
|
+
type: "array",
|
|
50043
|
+
items: { type: "string" },
|
|
50044
|
+
description: "Optional local screenshot paths, usually returned from capture_wave_screenshot or supplied by user."
|
|
50045
|
+
},
|
|
50046
|
+
referenceImagePaths: {
|
|
50047
|
+
type: "array",
|
|
50048
|
+
items: { type: "string" },
|
|
50049
|
+
description: "Optional local reference image paths."
|
|
50050
|
+
},
|
|
50051
|
+
targetStyle: {
|
|
50052
|
+
type: "string",
|
|
50053
|
+
description: "Optional visual style constraint."
|
|
50054
|
+
},
|
|
50055
|
+
intendedUse: {
|
|
50056
|
+
type: "string",
|
|
50057
|
+
description: "Optional Wave scene usage target such as prop, actor, background, UI model, collision mesh."
|
|
50058
|
+
},
|
|
50059
|
+
requiredParts: {
|
|
50060
|
+
type: "array",
|
|
50061
|
+
items: { type: "string" },
|
|
50062
|
+
description: "Optional named visible parts the generated model should preserve."
|
|
50063
|
+
},
|
|
50064
|
+
knownDimensionsMm: {
|
|
50065
|
+
type: "object",
|
|
50066
|
+
description: 'Optional JSON-safe dimension hints in millimeters, e.g. {"overall_width": 1200, "overall_height": 500}.',
|
|
50067
|
+
additionalProperties: true
|
|
50068
|
+
}
|
|
50069
|
+
},
|
|
50070
|
+
additionalProperties: false
|
|
50071
|
+
}
|
|
50072
|
+
},
|
|
49691
50073
|
{
|
|
49692
50074
|
name: "get_wave_session",
|
|
49693
50075
|
description: "Return the Wave Studio browser session paired through the WaveEngine Agent SDK. Full Studio tools are available when this local endpoint is called with the adopted HTTP Gateway token from Wave Studio.",
|
|
@@ -49792,6 +50174,7 @@ function getLocalToolImplementationSummary() {
|
|
|
49792
50174
|
implementedCanonicalToolCount: implementedCanonicalToolNames.length,
|
|
49793
50175
|
localExtraToolNames: [...LOCAL_EXTRA_TOOL_DEFINITION_NAMES].sort(),
|
|
49794
50176
|
localLookupToolNames: [...LOCAL_SDK_LOOKUP_TOOL_NAMES].sort(),
|
|
50177
|
+
localModelingToolNames: [...LOCAL_SDK_MODELING_TOOL_NAMES].sort(),
|
|
49795
50178
|
omittedCanonicalToolNames: canonicalToolNames.filter((name) => !isLocalMcpToolImplemented(name))
|
|
49796
50179
|
};
|
|
49797
50180
|
}
|
|
@@ -49815,7 +50198,8 @@ function localSdkLookupStatus(waveEngineSdkCache) {
|
|
|
49815
50198
|
corpusCacheReady,
|
|
49816
50199
|
apiAndSkillQueryAvailable: corpusCacheReady,
|
|
49817
50200
|
cacheMissAffectsLiveStudioTransport: false,
|
|
49818
|
-
lookupToolNames: [...LOCAL_SDK_LOOKUP_TOOL_NAMES].sort()
|
|
50201
|
+
lookupToolNames: [...LOCAL_SDK_LOOKUP_TOOL_NAMES].sort(),
|
|
50202
|
+
localModelingToolNames: [...LOCAL_SDK_MODELING_TOOL_NAMES].sort()
|
|
49819
50203
|
};
|
|
49820
50204
|
}
|
|
49821
50205
|
function jsonRpcToolIdempotencyKey(toolName, requestId) {
|
|
@@ -50280,10 +50664,10 @@ function screenshotBasePayload(payload, suggestedFilename) {
|
|
|
50280
50664
|
};
|
|
50281
50665
|
}
|
|
50282
50666
|
async function saveScreenshotPayloadToLocalFile(payload, suggestedFilename) {
|
|
50283
|
-
const directory =
|
|
50284
|
-
await
|
|
50285
|
-
const localPath =
|
|
50286
|
-
await
|
|
50667
|
+
const directory = path3.join(tmpdir2(), "wave3d-agent-sdk", "screenshots");
|
|
50668
|
+
await mkdir2(directory, { recursive: true });
|
|
50669
|
+
const localPath = path3.join(directory, suggestedFilename);
|
|
50670
|
+
await writeFile2(localPath, Buffer.from(payload.dataBase64, "base64"));
|
|
50287
50671
|
return localPath;
|
|
50288
50672
|
}
|
|
50289
50673
|
function normalizeRuntimeMarkerSource(value) {
|
|
@@ -50476,7 +50860,7 @@ async function callReadTool(name, args, context) {
|
|
|
50476
50860
|
if (name === "start_wave_task") {
|
|
50477
50861
|
const routeResult = startWaveMcpTaskRoute({
|
|
50478
50862
|
args,
|
|
50479
|
-
taskRouteId: `wgtask_${
|
|
50863
|
+
taskRouteId: `wgtask_${randomUUID2()}`,
|
|
50480
50864
|
now: nowIso(),
|
|
50481
50865
|
seedEvidence: mergeWaveMcpTaskEvidence(
|
|
50482
50866
|
getLocalPreTaskEvidence(context) ?? createWaveMcpEmptyTaskEvidence(),
|
|
@@ -50665,7 +51049,8 @@ async function callReadTool(name, args, context) {
|
|
|
50665
51049
|
"studio.project: list_wave_project_templates, new_wave_project, list_wave_projects, read_wave_project, rename_wave_project, open_wave_project, save_wave_project, share_wave_project",
|
|
50666
51050
|
"studio.preview: hot_reload_wave_preview (alias: hotreload_wave_preview), run_wave_preview (alias: run_project), pause_wave_preview (alias: pause_wave_engine), resume_wave_preview (alias: resume_wave_engine), get_wave_runtime_diagnostics (alias: get_wave_errors), get_wave_runtime_performance_snapshot (alias: get_wave_runtime_performance)",
|
|
50667
51051
|
"studio.capture: capture_wave_screenshot (alias: get_wave_screenshot), get_wave_runtime_entity_snapshot (alias: get_wave_entity_snapshot), list_wave_runtime_markers (alias: list_wave_markers)",
|
|
50668
|
-
"wave.authoring: list_wave_skill_families, query_wave_skills, get_wave_skill, query_wave_api"
|
|
51052
|
+
"wave.authoring: list_wave_skill_families, query_wave_skills, get_wave_skill, query_wave_api",
|
|
51053
|
+
"wave.modeling: create_wave_3d_modeling_job (aliases: generate_wave_3d_model, create_wave_3d_model, create_wave_3d_model_from_screenshots) creates local SDK support files; upload generated GLB later with asset tools"
|
|
50669
51054
|
],
|
|
50670
51055
|
rules: [
|
|
50671
51056
|
"studio.* labels are categories, not callable tools.",
|
|
@@ -50686,6 +51071,9 @@ async function callReadTool(name, args, context) {
|
|
|
50686
51071
|
args
|
|
50687
51072
|
});
|
|
50688
51073
|
}
|
|
51074
|
+
if (name === "create_wave_3d_modeling_job") {
|
|
51075
|
+
return await createWave3dModelingJobTool(args);
|
|
51076
|
+
}
|
|
50689
51077
|
if (name === "get_wave_command_result") {
|
|
50690
51078
|
const requestId = requireNonEmptyString(args?.requestId);
|
|
50691
51079
|
if (!requestId) {
|
|
@@ -50938,15 +51326,15 @@ async function callReadTool(name, args, context) {
|
|
|
50938
51326
|
if (name === "rename_wave_asset") {
|
|
50939
51327
|
const sessionResult = resolveWritableSession(args, context);
|
|
50940
51328
|
if ("result" in sessionResult) return sessionResult.result;
|
|
50941
|
-
const
|
|
51329
|
+
const path6 = requireNonEmptyString(args?.path);
|
|
50942
51330
|
const nextName = requireNonEmptyString(stringAliasArg(args, ["name", "newName", "displayName"]));
|
|
50943
|
-
if (!
|
|
51331
|
+
if (!path6 || !nextName) return errorToolResult("invalid_arguments", "`rename_wave_asset` requires non-empty `path` and `name`.");
|
|
50944
51332
|
return await enqueueBrowserCommand({
|
|
50945
51333
|
context,
|
|
50946
51334
|
session: sessionResult.session,
|
|
50947
51335
|
command: {
|
|
50948
51336
|
type: "renameAsset",
|
|
50949
|
-
path:
|
|
51337
|
+
path: path6,
|
|
50950
51338
|
name: nextName
|
|
50951
51339
|
}
|
|
50952
51340
|
});
|
|
@@ -50954,14 +51342,14 @@ async function callReadTool(name, args, context) {
|
|
|
50954
51342
|
if (name === "delete_wave_asset") {
|
|
50955
51343
|
const sessionResult = resolveWritableSession(args, context);
|
|
50956
51344
|
if ("result" in sessionResult) return sessionResult.result;
|
|
50957
|
-
const
|
|
50958
|
-
if (!
|
|
51345
|
+
const path6 = requireNonEmptyString(args?.path);
|
|
51346
|
+
if (!path6) return errorToolResult("invalid_arguments", "`delete_wave_asset` requires a non-empty `path`.");
|
|
50959
51347
|
return await enqueueBrowserCommand({
|
|
50960
51348
|
context,
|
|
50961
51349
|
session: sessionResult.session,
|
|
50962
51350
|
command: {
|
|
50963
51351
|
type: "deleteAsset",
|
|
50964
|
-
path:
|
|
51352
|
+
path: path6
|
|
50965
51353
|
}
|
|
50966
51354
|
});
|
|
50967
51355
|
}
|
|
@@ -51415,9 +51803,9 @@ async function handleWaveGenieMcpRequest(request, context) {
|
|
|
51415
51803
|
}
|
|
51416
51804
|
|
|
51417
51805
|
// ../../scripts/wavegenie-sdk/sessionRegistry.ts
|
|
51418
|
-
import { createHash as createHash4, randomUUID as
|
|
51806
|
+
import { createHash as createHash4, randomUUID as randomUUID3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
51419
51807
|
function createSessionId() {
|
|
51420
|
-
return `wgb_${
|
|
51808
|
+
return `wgb_${randomUUID3()}`;
|
|
51421
51809
|
}
|
|
51422
51810
|
function nowIso2() {
|
|
51423
51811
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -51557,18 +51945,18 @@ var WaveGenieBridgeSessionRegistry = class {
|
|
|
51557
51945
|
};
|
|
51558
51946
|
|
|
51559
51947
|
// ../../scripts/wavegenie-sdk/assetStaging.ts
|
|
51560
|
-
import { createHash as createHash5, randomBytes as randomBytes2, randomUUID as
|
|
51948
|
+
import { createHash as createHash5, randomBytes as randomBytes2, randomUUID as randomUUID4 } from "node:crypto";
|
|
51561
51949
|
import { createWriteStream } from "node:fs";
|
|
51562
|
-
import { mkdir as
|
|
51563
|
-
import { tmpdir as
|
|
51564
|
-
import
|
|
51950
|
+
import { mkdir as mkdir3, rm, writeFile as writeFile3 } from "node:fs/promises";
|
|
51951
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
51952
|
+
import path4 from "node:path";
|
|
51565
51953
|
var MAX_LOCAL_ASSET_UPLOAD_BYTES = 300 * 1024 * 1024;
|
|
51566
51954
|
var MAX_LOCAL_ASSET_UPLOAD_CHUNKS = 256;
|
|
51567
|
-
var STAGING_ROOT =
|
|
51955
|
+
var STAGING_ROOT = path4.join(tmpdir3(), "wave3d-agent-sdk-asset-staging");
|
|
51568
51956
|
var LOCAL_DIRECT_UPLOAD_TTL_MS = 60 * 60 * 1e3;
|
|
51569
51957
|
var DEFAULT_LOCAL_ASSET_UPLOAD_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
51570
51958
|
function createUploadId() {
|
|
51571
|
-
return `wgbupload_${
|
|
51959
|
+
return `wgbupload_${randomUUID4()}`;
|
|
51572
51960
|
}
|
|
51573
51961
|
function createSecret() {
|
|
51574
51962
|
return randomBytes2(24).toString("base64url");
|
|
@@ -51628,8 +52016,8 @@ var LocalAssetUploadStagingStore = class {
|
|
|
51628
52016
|
this.uploads = /* @__PURE__ */ new Map();
|
|
51629
52017
|
}
|
|
51630
52018
|
async ensureUploadDir(uploadId) {
|
|
51631
|
-
const uploadDir =
|
|
51632
|
-
await
|
|
52019
|
+
const uploadDir = path4.join(STAGING_ROOT, hashPathPart(uploadId));
|
|
52020
|
+
await mkdir3(uploadDir, { recursive: true });
|
|
51633
52021
|
return uploadDir;
|
|
51634
52022
|
}
|
|
51635
52023
|
createPartUrl(uploadId, chunkIndex, readToken) {
|
|
@@ -51668,8 +52056,8 @@ var LocalAssetUploadStagingStore = class {
|
|
|
51668
52056
|
const currentBytes = [...upload.parts.values()].filter((part2) => part2.chunkIndex !== chunkIndex).reduce((total, part2) => total + part2.sizeBytes, 0);
|
|
51669
52057
|
assertUploadSize(currentBytes + bytes.byteLength);
|
|
51670
52058
|
const uploadDir = await this.ensureUploadDir(upload.uploadId);
|
|
51671
|
-
const filePath =
|
|
51672
|
-
await
|
|
52059
|
+
const filePath = path4.join(uploadDir, `part-${chunkIndex}.bin`);
|
|
52060
|
+
await writeFile3(filePath, bytes);
|
|
51673
52061
|
const part = {
|
|
51674
52062
|
chunkIndex,
|
|
51675
52063
|
sizeBytes: bytes.byteLength,
|
|
@@ -51682,7 +52070,7 @@ var LocalAssetUploadStagingStore = class {
|
|
|
51682
52070
|
async putStreamPart(input) {
|
|
51683
52071
|
const currentBytes = [...input.upload.parts.values()].filter((part2) => part2.chunkIndex !== input.chunkIndex).reduce((total, part2) => total + part2.sizeBytes, 0);
|
|
51684
52072
|
const uploadDir = await this.ensureUploadDir(input.upload.uploadId);
|
|
51685
|
-
const filePath =
|
|
52073
|
+
const filePath = path4.join(uploadDir, `part-${input.chunkIndex}.bin`);
|
|
51686
52074
|
const output = createWriteStream(filePath, { flags: "w" });
|
|
51687
52075
|
let writtenBytes = 0;
|
|
51688
52076
|
try {
|
|
@@ -51836,7 +52224,7 @@ var LocalAssetUploadStagingStore = class {
|
|
|
51836
52224
|
const upload = this.uploads.get(uploadId);
|
|
51837
52225
|
if (!upload) return 0;
|
|
51838
52226
|
this.uploads.delete(uploadId);
|
|
51839
|
-
const uploadDir =
|
|
52227
|
+
const uploadDir = path4.join(STAGING_ROOT, hashPathPart(uploadId));
|
|
51840
52228
|
await rm(uploadDir, { recursive: true, force: true });
|
|
51841
52229
|
return upload.parts.size;
|
|
51842
52230
|
}
|
|
@@ -51861,7 +52249,7 @@ var MAX_DIRECT_UPLOAD_BODY_BYTES = 300 * 1024 * 1024;
|
|
|
51861
52249
|
var LOCAL_ORIGIN_RE = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i;
|
|
51862
52250
|
var TRUSTED_WAVE_STUDIO_ORIGIN_RE = /^https:\/\/(www\.)?wave3d\.ai$/i;
|
|
51863
52251
|
function createBridgeId() {
|
|
51864
|
-
return `wgbridge_${
|
|
52252
|
+
return `wgbridge_${randomUUID5()}`;
|
|
51865
52253
|
}
|
|
51866
52254
|
function isAllowedStudioOrigin(origin) {
|
|
51867
52255
|
if (!origin) return true;
|
|
@@ -52364,10 +52752,10 @@ function readBooleanFlag(options, name) {
|
|
|
52364
52752
|
return options.flags.get(name) === true;
|
|
52365
52753
|
}
|
|
52366
52754
|
function getDefaultCacheRoot() {
|
|
52367
|
-
return
|
|
52755
|
+
return path5.join(homedir2(), ".wave3d", "agent-cache");
|
|
52368
52756
|
}
|
|
52369
52757
|
function getCacheRoot(options) {
|
|
52370
|
-
return
|
|
52758
|
+
return path5.resolve(readStringFlag(options, "cache-dir") ?? process.env[CACHE_DIR_ENV] ?? getDefaultCacheRoot());
|
|
52371
52759
|
}
|
|
52372
52760
|
function getDefaultMcpUrl(options) {
|
|
52373
52761
|
const port = readNumberFlag(options, "port") ?? DEFAULT_PORT2;
|
|
@@ -52386,29 +52774,29 @@ function assertSafeListenHost(host, allowNetwork) {
|
|
|
52386
52774
|
throw new Error(`Refusing to listen on non-loopback host ${host}. Use --allow-network only on a trusted network.`);
|
|
52387
52775
|
}
|
|
52388
52776
|
function assertSafeCacheRoot(cacheRoot) {
|
|
52389
|
-
const resolved =
|
|
52390
|
-
const root =
|
|
52777
|
+
const resolved = path5.resolve(cacheRoot);
|
|
52778
|
+
const root = path5.parse(resolved).root;
|
|
52391
52779
|
const dangerous = /* @__PURE__ */ new Set([
|
|
52392
52780
|
root,
|
|
52393
|
-
|
|
52394
|
-
|
|
52395
|
-
|
|
52781
|
+
path5.resolve(homedir2()),
|
|
52782
|
+
path5.resolve(process.cwd()),
|
|
52783
|
+
path5.resolve(path5.dirname(homedir2()))
|
|
52396
52784
|
]);
|
|
52397
52785
|
if (dangerous.has(resolved)) {
|
|
52398
52786
|
throw new Error(`Refusing to use dangerous cache directory: ${resolved}`);
|
|
52399
52787
|
}
|
|
52400
|
-
const depth =
|
|
52788
|
+
const depth = path5.relative(root, resolved).split(path5.sep).filter(Boolean).length;
|
|
52401
52789
|
if (depth < 2) {
|
|
52402
52790
|
throw new Error(`Refusing to use shallow cache directory: ${resolved}`);
|
|
52403
52791
|
}
|
|
52404
52792
|
}
|
|
52405
52793
|
function isDefaultCacheRoot(cacheRoot) {
|
|
52406
|
-
return
|
|
52794
|
+
return path5.resolve(cacheRoot) === path5.resolve(getDefaultCacheRoot());
|
|
52407
52795
|
}
|
|
52408
52796
|
async function markCacheRoot(cacheRoot) {
|
|
52409
52797
|
assertSafeCacheRoot(cacheRoot);
|
|
52410
|
-
await
|
|
52411
|
-
await
|
|
52798
|
+
await mkdir4(cacheRoot, { recursive: true });
|
|
52799
|
+
await writeFile4(path5.join(cacheRoot, CACHE_MARKER_FILE), "wave3d-agent-cache\n");
|
|
52412
52800
|
}
|
|
52413
52801
|
function isSafePathSegment(value) {
|
|
52414
52802
|
return /^[A-Za-z0-9._-]+$/.test(value);
|
|
@@ -52737,11 +53125,11 @@ function assertCorpusManifestMatches(actual, expected) {
|
|
|
52737
53125
|
}
|
|
52738
53126
|
}
|
|
52739
53127
|
function getBundledSdkDirectory() {
|
|
52740
|
-
return
|
|
53128
|
+
return path5.join(path5.dirname(fileURLToPath(import.meta.url)), BUNDLED_SDK_DIR_NAME);
|
|
52741
53129
|
}
|
|
52742
53130
|
async function readBundledSdkManifest() {
|
|
52743
53131
|
try {
|
|
52744
|
-
const parsed = JSON.parse(await readFile2(
|
|
53132
|
+
const parsed = JSON.parse(await readFile2(path5.join(getBundledSdkDirectory(), "manifest.json"), "utf8"));
|
|
52745
53133
|
return isCorpusManifest(parsed) ? parsed : null;
|
|
52746
53134
|
} catch {
|
|
52747
53135
|
return null;
|
|
@@ -52749,7 +53137,7 @@ async function readBundledSdkManifest() {
|
|
|
52749
53137
|
}
|
|
52750
53138
|
async function readBundledSdkZip(manifest) {
|
|
52751
53139
|
const bundleFileName = requireSafePathSegment(manifest.bundleFileName, "Bundled SDK zip file name");
|
|
52752
|
-
const bytes = await readFile2(
|
|
53140
|
+
const bytes = await readFile2(path5.join(getBundledSdkDirectory(), bundleFileName));
|
|
52753
53141
|
if (bytes.length > MAX_CORPUS_ZIP_BYTES2) {
|
|
52754
53142
|
throw new Error(`Bundled SDK zip is too large: ${bytes.length} bytes.`);
|
|
52755
53143
|
}
|
|
@@ -52760,11 +53148,11 @@ async function writeCorpusCacheFromZip(input) {
|
|
|
52760
53148
|
await markCacheRoot(cacheRoot);
|
|
52761
53149
|
const cacheKey = requireSafePathSegment(input.manifest.cacheKey, "Corpus cacheKey");
|
|
52762
53150
|
const bundleFileName = requireSafePathSegment(input.manifest.bundleFileName, "Corpus bundle file name");
|
|
52763
|
-
const versionDir =
|
|
52764
|
-
const corpusDir =
|
|
52765
|
-
await
|
|
52766
|
-
await
|
|
52767
|
-
await
|
|
53151
|
+
const versionDir = path5.join(cacheRoot, cacheKey);
|
|
53152
|
+
const corpusDir = path5.join(versionDir, "corpus");
|
|
53153
|
+
await mkdir4(versionDir, { recursive: true });
|
|
53154
|
+
await writeFile4(path5.join(versionDir, "corpus-handshake.json"), JSON.stringify(input.handshake, null, 2));
|
|
53155
|
+
await writeFile4(path5.join(versionDir, bundleFileName), input.zipBytes);
|
|
52768
53156
|
await extractZipToDirectory(input.zipBytes, corpusDir, input.manifest);
|
|
52769
53157
|
await buildSearchIndex(corpusDir);
|
|
52770
53158
|
const current = {
|
|
@@ -52776,7 +53164,7 @@ async function writeCorpusCacheFromZip(input) {
|
|
|
52776
53164
|
sourceVersions: input.manifest.sourceVersions,
|
|
52777
53165
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
52778
53166
|
};
|
|
52779
|
-
await
|
|
53167
|
+
await writeFile4(path5.join(cacheRoot, "current.json"), JSON.stringify(current, null, 2));
|
|
52780
53168
|
return current;
|
|
52781
53169
|
}
|
|
52782
53170
|
async function ensureBundledSdkCache(options, input) {
|
|
@@ -52915,16 +53303,16 @@ async function commandDoctor(options) {
|
|
|
52915
53303
|
await commandCacheStatus(options);
|
|
52916
53304
|
}
|
|
52917
53305
|
function normalizeZipEntryPath(entryName) {
|
|
52918
|
-
const normalized =
|
|
52919
|
-
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.includes("\0") ||
|
|
53306
|
+
const normalized = path5.posix.normalize(entryName.replace(/\\/g, "/"));
|
|
53307
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.includes("\0") || path5.posix.isAbsolute(normalized)) {
|
|
52920
53308
|
throw new Error(`Unsafe corpus zip entry: ${entryName}`);
|
|
52921
53309
|
}
|
|
52922
53310
|
return normalized;
|
|
52923
53311
|
}
|
|
52924
53312
|
function resolveZipOutputPath(targetDir, normalizedEntryName) {
|
|
52925
|
-
const outputPath =
|
|
52926
|
-
const relative =
|
|
52927
|
-
if (relative.startsWith("..") ||
|
|
53313
|
+
const outputPath = path5.resolve(targetDir, ...normalizedEntryName.split("/"));
|
|
53314
|
+
const relative = path5.relative(targetDir, outputPath);
|
|
53315
|
+
if (relative.startsWith("..") || path5.isAbsolute(relative)) {
|
|
52928
53316
|
throw new Error(`Unsafe corpus zip output path: ${normalizedEntryName}`);
|
|
52929
53317
|
}
|
|
52930
53318
|
return outputPath;
|
|
@@ -52938,8 +53326,8 @@ function isCorpusCurrentRecord(value) {
|
|
|
52938
53326
|
return isRecord4(value) && typeof value.cacheKey === "string" && typeof value.version === "string" && (typeof value.formatVersion === "undefined" || typeof value.formatVersion === "string") && typeof value.bundleHash === "string" && typeof value.corpusDir === "string" && (typeof value.sourceVersions === "undefined" || isRecord4(value.sourceVersions)) && (typeof value.updatedAt === "undefined" || typeof value.updatedAt === "string");
|
|
52939
53327
|
}
|
|
52940
53328
|
function isPathInsideDirectory2(parentDir, childPath) {
|
|
52941
|
-
const relative =
|
|
52942
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !
|
|
53329
|
+
const relative = path5.relative(path5.resolve(parentDir), path5.resolve(childPath));
|
|
53330
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
52943
53331
|
}
|
|
52944
53332
|
async function readCorpusManifestFromDirectory(corpusDir) {
|
|
52945
53333
|
try {
|
|
@@ -52984,7 +53372,7 @@ async function extractZipToDirectory(zipBytes, targetDir, manifest) {
|
|
|
52984
53372
|
const allowedPaths = /* @__PURE__ */ new Set(["manifest.json", ...expectedFiles.keys()]);
|
|
52985
53373
|
const extractedFilePaths = /* @__PURE__ */ new Set();
|
|
52986
53374
|
await rm2(targetDir, { recursive: true, force: true });
|
|
52987
|
-
await
|
|
53375
|
+
await mkdir4(targetDir, { recursive: true });
|
|
52988
53376
|
const entries = Object.values(zip.files);
|
|
52989
53377
|
if (entries.length > MAX_CORPUS_FILE_COUNT) {
|
|
52990
53378
|
throw new Error(`Corpus zip has too many entries: ${entries.length}.`);
|
|
@@ -53001,7 +53389,7 @@ async function extractZipToDirectory(zipBytes, targetDir, manifest) {
|
|
|
53001
53389
|
throw new Error(`Corpus extracted size is too large: ${extractedBytes + expectedSize} bytes.`);
|
|
53002
53390
|
}
|
|
53003
53391
|
const outputPath = resolveZipOutputPath(targetDir, normalized);
|
|
53004
|
-
await
|
|
53392
|
+
await mkdir4(path5.dirname(outputPath), { recursive: true });
|
|
53005
53393
|
const content = await entry.async("nodebuffer");
|
|
53006
53394
|
extractedBytes += content.length;
|
|
53007
53395
|
if (extractedBytes > MAX_CORPUS_EXTRACTED_BYTES2) {
|
|
@@ -53019,7 +53407,7 @@ async function extractZipToDirectory(zipBytes, targetDir, manifest) {
|
|
|
53019
53407
|
}
|
|
53020
53408
|
extractedFilePaths.add(normalized);
|
|
53021
53409
|
}
|
|
53022
|
-
await
|
|
53410
|
+
await writeFile4(outputPath, content);
|
|
53023
53411
|
}
|
|
53024
53412
|
for (const expectedPath of expectedFiles.keys()) {
|
|
53025
53413
|
if (!extractedFilePaths.has(expectedPath)) {
|
|
@@ -53031,7 +53419,7 @@ function parseJsonLines2(content) {
|
|
|
53031
53419
|
return content.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
53032
53420
|
}
|
|
53033
53421
|
async function buildSearchIndex(corpusDir) {
|
|
53034
|
-
const searchPath =
|
|
53422
|
+
const searchPath = path5.join(corpusDir, "search", "search-documents.jsonl");
|
|
53035
53423
|
const content = await readFile2(searchPath, "utf8");
|
|
53036
53424
|
const documents = parseJsonLines2(content);
|
|
53037
53425
|
const uniqueDocuments = [];
|
|
@@ -53058,7 +53446,7 @@ async function buildSearchIndex(corpusDir) {
|
|
|
53058
53446
|
}
|
|
53059
53447
|
});
|
|
53060
53448
|
miniSearch.addAll(uniqueDocuments);
|
|
53061
|
-
await
|
|
53449
|
+
await writeFile4(path5.join(corpusDir, "search", "minisearch-index.json"), JSON.stringify(miniSearch.toJSON()));
|
|
53062
53450
|
}
|
|
53063
53451
|
async function commandCacheRefresh(options) {
|
|
53064
53452
|
const mcpUrl = readStringFlag(options, "mcp-url");
|
|
@@ -53076,12 +53464,12 @@ async function commandCacheRefresh(options) {
|
|
|
53076
53464
|
console.log(`Cache root: ${getCacheRoot(options)}`);
|
|
53077
53465
|
}
|
|
53078
53466
|
async function readCurrentCache(cacheRoot) {
|
|
53079
|
-
const currentPath =
|
|
53467
|
+
const currentPath = path5.join(cacheRoot, "current.json");
|
|
53080
53468
|
if (!existsSync2(currentPath)) return null;
|
|
53081
53469
|
try {
|
|
53082
53470
|
const parsed = JSON.parse(await readFile2(currentPath, "utf8"));
|
|
53083
53471
|
if (!isCorpusCurrentRecord(parsed)) return null;
|
|
53084
|
-
const corpusDir =
|
|
53472
|
+
const corpusDir = path5.resolve(parsed.corpusDir);
|
|
53085
53473
|
if (!isPathInsideDirectory2(cacheRoot, corpusDir)) return null;
|
|
53086
53474
|
const manifest = await readCorpusManifestFromDirectory(corpusDir);
|
|
53087
53475
|
if (!manifest || !corpusCurrentMatchesManifest(parsed, manifest)) return null;
|
|
@@ -53110,7 +53498,7 @@ async function commandCacheStatus(options) {
|
|
|
53110
53498
|
async function commandCacheClear(options) {
|
|
53111
53499
|
const cacheRoot = getCacheRoot(options);
|
|
53112
53500
|
assertSafeCacheRoot(cacheRoot);
|
|
53113
|
-
if (!isDefaultCacheRoot(cacheRoot) && !existsSync2(
|
|
53501
|
+
if (!isDefaultCacheRoot(cacheRoot) && !existsSync2(path5.join(cacheRoot, CACHE_MARKER_FILE))) {
|
|
53114
53502
|
throw new Error(`Refusing to clear unmarked custom cache directory: ${cacheRoot}`);
|
|
53115
53503
|
}
|
|
53116
53504
|
await rm2(cacheRoot, { recursive: true, force: true });
|
|
@@ -53124,7 +53512,7 @@ async function commandCacheSearch(options) {
|
|
|
53124
53512
|
const current = await readCurrentCache(cacheRoot);
|
|
53125
53513
|
const corpusDir = typeof current?.corpusDir === "string" ? current.corpusDir : null;
|
|
53126
53514
|
if (!corpusDir) throw new Error("No bundled SDK cache is available. Reinstall/update wave3d-agent-sdk.");
|
|
53127
|
-
const indexPath =
|
|
53515
|
+
const indexPath = path5.join(corpusDir, "search", "minisearch-index.json");
|
|
53128
53516
|
const rawIndex = await readFile2(indexPath, "utf8");
|
|
53129
53517
|
const miniSearch = MiniSearch2.loadJSON(rawIndex, {
|
|
53130
53518
|
fields: ["title", "text"],
|