wave3d-agent-sdk 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/cli.js +370 -25
- package/dist/sdk/README.md +3 -3
- package/dist/sdk/manifest.json +19 -19
- package/dist/sdk/wave-engine-sdk-2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf.zip +0 -0
- package/package.json +1 -1
- package/dist/sdk/wave-engine-sdk-2026-06-17.1.dBbXqRbLTZgB2Lhd28PqH8fYLoSLaK0J.zip +0 -0
package/README.md
CHANGED
|
@@ -21,19 +21,19 @@ Vocabulary:
|
|
|
21
21
|
- Project: user workspace content. Reading a project is not opening/replacing live workspace.
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx -y wave3d-agent-sdk@0.2.
|
|
24
|
+
npx -y wave3d-agent-sdk@0.2.9 start
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
After Copy to Agent,
|
|
27
|
+
After Copy to Agent, run the exact start command with MCP_TOKEN preserved as the local pairing secret. Keep it hidden:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
WAVE3D_MCP_TOKEN="$MCP_TOKEN" npx -y wave3d-agent-sdk@0.2.
|
|
30
|
+
WAVE3D_MCP_TOKEN="$MCP_TOKEN" npx -y wave3d-agent-sdk@0.2.9 start
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Check SDK build before trusting a running SDK process:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npx -y wave3d-agent-sdk@0.2.
|
|
36
|
+
npx -y wave3d-agent-sdk@0.2.9 --version
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
The package bundles the WaveEngine SDK lookup corpus. No normal session needs a separate protected zip download.
|
|
@@ -43,12 +43,12 @@ The package bundles the WaveEngine SDK lookup corpus. No normal session needs a
|
|
|
43
43
|
Wave Studio Copy to Agent defaults same-machine live MCP transport to this SDK:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
npx -y wave3d-agent-sdk@0.2.
|
|
46
|
+
npx -y wave3d-agent-sdk@0.2.9 start
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Agents choose topology first:
|
|
50
50
|
|
|
51
|
-
- Same computer as Wave Studio browser: try copied localhost `MCP_URL`, call `self_check_wave_mcp` with copied `expectedVersion` and `expectedBuildHash`, and
|
|
51
|
+
- Same computer as Wave Studio browser: try copied localhost `MCP_URL`, call `self_check_wave_mcp` with copied `expectedVersion` and `expectedBuildHash`, and run the exact SDK command from Copy to Agent if `self_check_wave_mcp` is missing, SDK is stale, tools are missing, or the SDK prints secrets before fallback. Current SDK starts self-heal stale peers on the same localhost port: it reuses a current same-token SDK, replaces verified stale/different-token SDK peers, and refuses to kill non-SDK port owners. If restart happens after Copy to Agent, preserve MCP_TOKEN with `WAVE3D_MCP_TOKEN="$MCP_TOKEN"` so the SDK process can authorize that local pairing secret. HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt; successful Gateway auth or convenience is not a same-machine fallback reason.
|
|
52
52
|
- Different machine, remote container, cloud agent, sandbox without shell/Node/npm, or localhost points to agent machine instead of user browser machine: use `MCP_HTTP_GATEWAY_FALLBACK_URL` directly. Do not start WaveEngine Agent SDK on wrong machine.
|
|
53
53
|
|
|
54
54
|
Call `get_wave_mcp_health` (aliases: `mcp_health`, `mcp_status`) whenever transport, SDK, or session status is unclear. It reports endpoint kind, SDK version/build, tool catalog hash/count, paired sessions, and WaveEngine SDK Cache state.
|
|
@@ -58,13 +58,13 @@ Use `MCP_HTTP_GATEWAY_FALLBACK_URL` only for topology mismatch, remote/sandboxed
|
|
|
58
58
|
For unfamiliar authoring, use the local SDK tools:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
npx -y wave3d-agent-sdk@0.2.
|
|
61
|
+
npx -y wave3d-agent-sdk@0.2.9 cache search "continuous left rotation"
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
`cache refresh` without MCP arguments reinstalls the bundled SDK into `~/.wave3d/agent-cache`:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
npx -y wave3d-agent-sdk@0.2.
|
|
67
|
+
npx -y wave3d-agent-sdk@0.2.9 cache refresh
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Old `cache refresh --mcp-url <url> --token <token>` arguments are ignored. Normal users update the npm package; no separate zip download exists.
|
package/dist/cli.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { mkdir as mkdir3, readFile as readFile2, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
5
5
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6
|
-
import {
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
7
8
|
import { homedir as homedir2 } from "node:os";
|
|
8
9
|
import path4 from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { promisify } from "node:util";
|
|
10
12
|
import JSZip from "jszip";
|
|
11
13
|
import MiniSearch2 from "minisearch";
|
|
12
14
|
|
|
@@ -35,10 +37,13 @@ function createWaveGenieError(code, message, details) {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
// ../../scripts/wavegenie-sdk/auth.ts
|
|
38
|
-
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
40
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
39
41
|
function createWaveGenieBridgeToken() {
|
|
40
42
|
return randomBytes(24).toString("hex");
|
|
41
43
|
}
|
|
44
|
+
function createWaveGenieBridgeTokenFingerprint(token) {
|
|
45
|
+
return createHash("sha256").update("waveengine-agent-sdk-local-pairing-secret:").update(token).digest("base64url").slice(0, 16);
|
|
46
|
+
}
|
|
42
47
|
function extractWaveGenieBearerToken(authorizationHeader) {
|
|
43
48
|
if (!authorizationHeader) return null;
|
|
44
49
|
const [scheme, ...rest] = authorizationHeader.trim().split(/\s+/);
|
|
@@ -42483,8 +42488,8 @@ function normalizeWaveGenieAssetUploadRequest(request) {
|
|
|
42483
42488
|
|
|
42484
42489
|
// ../../src/lib/waveStudio/aiAssist/bridge/localAgentSdkContract.ts
|
|
42485
42490
|
var WAVE3D_AGENT_SDK_PACKAGE_NAME = "wave3d-agent-sdk";
|
|
42486
|
-
var WAVE3D_AGENT_SDK_REQUIRED_VERSION = "0.2.
|
|
42487
|
-
var WAVE3D_AGENT_SDK_REQUIRED_BUILD = "agent-sdk-20260620.
|
|
42491
|
+
var WAVE3D_AGENT_SDK_REQUIRED_VERSION = "0.2.9";
|
|
42492
|
+
var WAVE3D_AGENT_SDK_REQUIRED_BUILD = "agent-sdk-20260620.4";
|
|
42488
42493
|
var WAVE3D_AGENT_SDK_PACKAGE_SPEC = `${WAVE3D_AGENT_SDK_PACKAGE_NAME}@${WAVE3D_AGENT_SDK_REQUIRED_VERSION}`;
|
|
42489
42494
|
var WAVE3D_AGENT_SDK_START_COMMAND = `npx -y ${WAVE3D_AGENT_SDK_PACKAGE_SPEC} start`;
|
|
42490
42495
|
var WAVE3D_AGENT_SDK_TOKEN_ENV_VAR = "WAVE3D_MCP_TOKEN";
|
|
@@ -44390,7 +44395,7 @@ var WAVE_MCP_AGENT_BRIEF = [
|
|
|
44390
44395
|
'0-role. Roleplay lock: you are the Wave Studio authoring agent for this session. Stay in that role, follow Wave authoring strategy, use concrete MCP tools, and briefly confirm "Wave Studio authoring role accepted" after onboarding acceptance.',
|
|
44391
44396
|
"0. Think in skills, act with tools. `studio.*` names are routing families, not callable tool names; call the real MCP tools listed under each family.",
|
|
44392
44397
|
"0a. Do not invent MCP tool names. After onboarding, call `tools/list` (or read the returned tool catalog) and use concrete names. Common aliases and safe high-confidence typos are tolerated; destructive/write fuzzy matches require an exact follow-up call. There is no `get_wave_current_project`; use `get_wave_session` for current project/session/active-file context.",
|
|
44393
|
-
`0b. Transport rule: choose topology first. Same-machine agent + same Wave Studio browser computer -> use copied \`MCP_URL\` WaveEngine Agent SDK (\`http://127.0.0.1:47231/mcp\`). On localhost, MCP_TOKEN is a local pairing secret, not account/cloud auth. If localhost is refused/unreachable, start exact SDK \`${WAVE3D_AGENT_SDK_START_COMMAND}\` when your runtime allows shell/Node/npm. If SDK is stale, missing tools, printing secrets, or local 401 follows an SDK restart,
|
|
44398
|
+
`0b. Transport rule: choose topology first. Same-machine agent + same Wave Studio browser computer -> use copied \`MCP_URL\` WaveEngine Agent SDK (\`http://127.0.0.1:47231/mcp\`). On localhost, MCP_TOKEN is a local pairing secret, not account/cloud auth. If localhost is refused/unreachable, start exact SDK \`${WAVE3D_AGENT_SDK_START_COMMAND}\` when your runtime allows shell/Node/npm. If SDK is stale, missing tools, printing secrets, or local 401 follows an SDK restart, run the exact token-preserving command \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\`; use the current MCP_TOKEN value without printing it. Current SDK start self-heals verified stale/different-token SDK peers on localhost and refuses to kill non-SDK port owners. Then recheck /health and tools/list. Same-machine stale/missing tools/401-with-Gateway-success means repair local SDK first, do not use HTTP Gateway first. Same-machine HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt; convenience, habit, or successful Gateway auth is not a valid fallback reason. Different machine, remote container, cloud agent, sandbox without shell/Node/npm, or localhost points to the agent machine -> use \`MCP_HTTP_GATEWAY_FALLBACK_URL\` directly, where MCP_TOKEN is an HTTP Gateway bearer. Do not start WaveEngine Agent SDK on the wrong machine.`,
|
|
44394
44399
|
"0b-recovery. If connection/session/onboarding/cache/mirror/timeout state is unclear, call `get_wave_mcp_health` (aliases `mcp_health`, `mcp_status`) first for status, then call `recover_wave_connection({ lastErrorCode?, failedTool?, symptom? })` and follow its `nextAction` exactly. Use recovery after `agent_onboarding_required`, `session_not_found`, `command_timeout`, `command_lease_expired`, `workspace_mirror_not_ready`, `hosted_mcp_proxy_failed`, local/HTTP Gateway fallback confusion, or sleeping-tab symptoms. If local HTTP returns 401 but HTTP Gateway accepts the same MCP_TOKEN, update/restart WaveEngine Agent SDK first. If every endpoint returns 401/bridge_auth_invalid before any tool result, ask user for fresh Copy to Agent.",
|
|
44395
44400
|
`0c. Cache priorities: Priority 1 is active role/context cache. Keep the role, coding guardrails, authoring workflow, full intent taxonomy (${WAVE_MCP_TASK_INTENT_KIND_VALUES.join(", ")}), Studio tool policy, assets/VFS/hot-reload rules, prompt version, and prompt hash in active LLM context as system-prompt-like rules. Priority 2 is WaveEngine SDK Cache. Production HTTP Gateway/local-SDK onboarding must not claim normal cached success until SDK cache is prepared or explicit accept proof passes. If cache cannot work because sandbox/no filesystem/no Node/npm/user refusal/refresh failure, accept degraded fallback explicitly with \`waveEngineSdkFallbackMode:"studio_ops_only"\` plus \`waveEngineSdkUnavailableReason\` plus \`studioOpsOnlyFallbackAccepted:true\`, and warn user HTTP Gateway is Studio-ops-only and skill/API lookup requires local SDK/cache repair. Keep live transport and retrieval cache separate: \`wave3d-agent-sdk start\` is long-running MCP transport; \`wave3d-agent-sdk cache refresh\` reinstalls the package-bundled SDK locally. Never use cache refresh to switch MCP transport, and never treat cache failure as local transport failure. Use SDK Cache for unfamiliar authoring skill/API lookup when Node/npm/local cache are allowed.`,
|
|
44396
44401
|
'0d. Local parity rule: WaveEngine Agent SDK `tools/list` mirrors the HTTP Gateway Studio-operation catalog, then adds local SDK lookup/session-discovery extras. Live-session tools run through the paired browser. Local calls wait directly for browser results. Only call `get_wave_command_result` when a tool response actually includes `status:"pending"` and `requestId`; on local it is normally catalog parity only.',
|
|
@@ -44416,7 +44421,7 @@ var WAVE_MCP_AGENT_BRIEF = [
|
|
|
44416
44421
|
"VFS hashes: simple edit tools auto-resolve the latest mirror `contentHash`; pass `baseHash` only when you already have it or when using advanced `apply_wave_patch`. Always inspect `changedFiles`, `changedPaths`, `partial`, and `skippedOperations` after edits.",
|
|
44417
44422
|
"VFS modules: scene-root modules and their owned helpers run inside the Studio authoring context, but helpers should stay pure or parameterized. Put live object ownership in scene-root/feature files, not shared helpers.",
|
|
44418
44423
|
'VFS HTML sketches: for diagram/chart/doc-sketch requests, create or edit an exact `.html` file with Studio VFS tools. Mermaid renders from `<div class="mermaid">flowchart TD ...</div>` when that HTML file is active in the editor/coding pane; HTML sketches must never replace the live engine preview iframe. Do not put Mermaid in markdown fences unless user specifically wants markdown text.',
|
|
44419
|
-
'VFS hot reload ownership: Studio hot reload targets the active/open edited file when safe. Editing `/main.ts` reloads the main graph; editing a scene-root module reloads that root; bootstrap/scene registry/assets/network/ambiguous helper surfaces escalate. In multi-file projects, standalone `hot_reload_wave_preview` follows the active Studio editor file, so do not expect it to reload an arbitrary unopened module by path. Put heavy world loading in bootstrap or a coarse world module. For intentional cross-module live entities, the owner module calls `waveStudio.exportEntity("Name", entity)` and importer modules call `waveStudio.importEntity<T>("Name")`; Studio restores the exported baseline and replays active importer overlays during source hot reload. Source execution order is dependency-driven: exporter roots run before importer roots; an exporter root runs before `main.ts` when main imports it; importer overlays run after exporter/main baseline. Entity names must be static string literals, unique, and acyclic or run/hot reload stops before execution. Keep shared helpers pure or parameterized.',
|
|
44424
|
+
'VFS hot reload ownership: Studio hot reload targets the active/open edited file when safe. Editing `/main.ts` reloads the main graph; editing a scene-root module reloads that root; bootstrap/scene registry/assets/network/ambiguous helper surfaces escalate. In multi-file projects, standalone `hot_reload_wave_preview` follows the active Studio editor file, so do not expect it to reload an arbitrary unopened module by path. Put heavy world loading in bootstrap or a coarse world module. If authored UI in `main.ts` changes bootstrap/world-provider state, store the state and call `waveStudio.reloadPreview({ reason })`; do not call `myScene.hotReload(...)` from Studio-authored code because it bypasses Studio VFS/package/source-patch orchestration. For intentional cross-module live entities, the owner module calls `waveStudio.exportEntity("Name", entity)` and importer modules call `waveStudio.importEntity<T>("Name")`; Studio restores the exported baseline and replays active importer overlays during source hot reload. Source execution order is dependency-driven: exporter roots run before importer roots; an exporter root runs before `main.ts` when main imports it; importer overlays run after exporter/main baseline. Entity names must be static string literals, unique, and acyclic or run/hot reload stops before execution. Keep shared helpers pure or parameterized.',
|
|
44420
44425
|
"Assets: when quoting or passing an existing asset, use typed bare refs in main files: `models.X`, `textures.X`, `materials.X`, `audios.X`, `hdr.X`, `cubeMaps.X`, `animations.X`, etc. Do not paste raw paths or edit Studio-managed generated files just to register assets. If the exact ref is not already visible, call category-first asset discovery (`find_wave_assets_by_category`, alias `list_wave_assets_by_category`, alias `search_wave_assets_by_category`, or tolerant `list_wave_assets`) with required `category` from user intent, semantic `query`, and small `limit` before editing code; category-filtered results can still be capped/truncated, so refine query before concluding absence. Use `list_wave_explorer_assets` only for uploaded library metadata, rename/delete, URL, path, size, or display/ref-name management.",
|
|
44421
44426
|
"Asset authoring facade split: typed refs quote existing assets; `waveMaterial` authors material handles; `assetManager` is only for exact asset-pipeline/generated-asset APIs that a local SDK skill or API lookup names, such as SDF surfaces or surface ribbons. Do not treat `assetManager` as the default creative facade for materials or normal asset arguments.",
|
|
44422
44427
|
"Material authoring: when creating, configuring, forking, or intentionally editing material handles, route to `wave.material.authoring` and start from `waveMaterial` (`createWater`, `createGrid`, `createSplatMaterial`, `createFromAsset`, `editAsset`). If a material handle later needs runtime sync, `assetManager` may be passed as the sync target; it is still not the authoring root.",
|
|
@@ -44441,7 +44446,7 @@ var WAVE_MCP_AUTHORING_GLOBALS_GUIDE = [
|
|
|
44441
44446
|
"- Colors/materials/fx helpers: `COLOR`, `PALETTE`, `waveCOLOR`, `waveMaterial`, `shaderUniform`, `waveFx`, `waveFxPresets`, `FxAnchor`, `FxCondition`.",
|
|
44442
44447
|
"- Controller movement helpers: `waveKinematicActor`, `netKinematicActor`, `KinematicHumanoidMovementState`, `KinematicVehicleMovementState`, and `KinematicFlightMovementState`. Use actor-level `.asHumanoid(...)`, `.asVehicle(...)`, or `.asFlight(...)` for player/controller movement.",
|
|
44443
44448
|
"- Asset maps: use category-first refs returned by `find_wave_assets_by_category`, commonly `models`, `gaussianSplats`, `animations`, `materials`, `guis`, `textures`, `audios`, `instruments`, `videos`, `hdr`, `fonts`, `serializedData`, `terrains`, `fx`, and `particles`.",
|
|
44444
|
-
"- Scene/runtime handles: prefer `myScene` for scene composition and `waveStudio` for Studio operations. `assetManager` is available, but use it only when an exact skill/API names an asset-pipeline or generated-asset factory; do not use it as the default facade for existing asset refs or material authoring. Use lower-level `ctx`, `engine`, or `scene` only when existing code or an exact API requires them.",
|
|
44449
|
+
"- Scene/runtime handles: prefer `myScene` for scene composition and `waveStudio` for Studio operations. If authored UI changes bootstrap/world-provider state and needs the Studio preview to rebuild, call `waveStudio.reloadPreview({ reason })`, not `myScene.hotReload(...)`. `assetManager` is available, but use it only when an exact skill/API names an asset-pipeline or generated-asset factory; do not use it as the default facade for existing asset refs or material authoring. Use lower-level `ctx`, `engine`, or `scene` only when existing code or an exact API requires them.",
|
|
44445
44450
|
"- Authoring systems/presets: `waveEventBus`, `waveRig`, `waveValueCurve`, `waveValueCurvePresets`, `waveMotionSignal`, `waveParam`, `WaveParam`, `WaveChoice`, `prefabModels`, `effectPrefabs`, and prefab helpers such as `rocketPrefab`.",
|
|
44446
44451
|
"- Lowercase constructors: `prop`, `marker`, `sphere`, `point`, `cube`, `box`, `cylinder`, `capsule`, `cone`, `torus`, `plane`, `ground`, `line`, `arc`, `path`, and related shape helpers.",
|
|
44447
44452
|
"- Type names are not import paths. If the handbook shows a type like `TransformVerbMode` or `WaveRateUnit`, look for the matching bare runtime value such as `Animate` or `DegreesPerSecond` before writing code. Transform `Snapshot` calls return a snapshot builder; call `.take()` before passing the pose to `transitionTo(...)`.",
|
|
@@ -44633,7 +44638,7 @@ var WAVE_MCP_STUDIO_TOOL_GUIDE = [
|
|
|
44633
44638
|
"- MCP names: HTTP Gateway = Next/Vercel endpoint for remote/sandbox Studio operations; WaveEngine Agent SDK = same-machine live MCP transport from long-running `wave3d-agent-sdk start`; WaveEngine SDK Cache = package-bundled local skill/API docs retrieval; VFS Mirror = HTTP Gateway workspace snapshot. Do not confuse these modules. On same machine, HTTP Gateway is degraded fallback only after SDK cannot run/update/adopt; do not choose it because it is already reachable or easier.",
|
|
44634
44639
|
"- MCP recovery: when connection/session/onboarding/cache/mirror/timeout state is unclear, call `recover_wave_connection({ lastErrorCode?, failedTool?, symptom? })` first and follow its returned `nextAction`. This is the explicit recovery path for sleeping tabs, local/HTTP Gateway confusion, mirror-not-ready, onboarding-required, command timeout/lease expiry, and hosted proxy/cache failures.",
|
|
44635
44640
|
'- Local parity rule: WaveEngine Agent SDK `tools/list` mirrors the HTTP Gateway Studio-operation catalog, then adds local SDK lookup/session-discovery extras. Direct local live-session tools include VFS edits, assets/uploads, project save/open/new/read/share, preview run/hot reload/pause/resume, diagnostics, performance, screenshots, entity snapshots, and runtime markers. Local browser-backed calls wait directly for browser results. Only call `get_wave_command_result` if a tool returns `status:"pending"` plus `requestId`; on local it normally exists only for catalog parity and reports no local pending-command queue.',
|
|
44636
|
-
`- MCP topology: same-machine agents should run \`${WAVE3D_AGENT_SDK_START_COMMAND}\`, check /health, and update/restart SDK if version is stale, tools are missing, or SDK prints secrets; after restart recheck /health and tools/list. If restart happens after Copy-to-Agent, preserve MCP_TOKEN with \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value without printing it, otherwise the
|
|
44641
|
+
`- MCP topology: same-machine agents should run \`${WAVE3D_AGENT_SDK_START_COMMAND}\`, check /health, and update/restart SDK if version is stale, tools are missing, or SDK prints secrets; after restart recheck /health and tools/list. If restart happens after Copy-to-Agent, preserve MCP_TOKEN with \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value without printing it, otherwise the SDK may be healthy but unable to authorize the copied local pairing secret. Current SDK start reuses a current same-token peer, replaces verified stale/different-token SDK peers on localhost, and refuses to kill non-SDK port owners. Same-machine stale/missing tools/401-with-Gateway-success means repair local SDK first, not HTTP Gateway first. Same-machine HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt. Different-machine/cloud/sandbox agents should use \`MCP_HTTP_GATEWAY_FALLBACK_URL\` directly; starting WaveEngine Agent SDK there binds localhost beside the agent, not beside the user browser. Stale SDK/cache are not reasons to use HTTP Gateway fallback; wrong-machine localhost is a topology mismatch.`,
|
|
44637
44642
|
`- MCP cache priorities: Priority 1 is active role/context cache: keep role, guardrails, authoring workflow, asset/VFS/hot-reload rules, prompt version, and prompt hash in active LLM context. Priority 2 is WaveEngine SDK Cache: production HTTP Gateway/local-SDK onboarding must not claim normal cached success until SDK cache is prepared or accept proof passes. If accept returns \`wave_engine_sdk_cache_not_ready\`, update/restart \`wave3d-agent-sdk\` or run \`${WAVE_MCP_CORPUS_CACHE_COMMAND_TEMPLATE}\`, then retry accept. If local cache cannot work because sandbox/no filesystem/no Node/npm/user refusal/refresh failure, explicitly accept degraded fallback with \`waveEngineSdkFallbackMode:"studio_ops_only"\`, a valid \`waveEngineSdkUnavailableReason\`, and \`studioOpsOnlyFallbackAccepted:true\`; warn user HTTP Gateway is Studio-ops-only and skill/API lookup requires local SDK/cache repair. This cache command reinstalls the package-bundled SDK locally; it is not WaveEngine Agent SDK transport. Never run it to switch MCP transport; never treat cache failure as local transport failure.`,
|
|
44638
44643
|
"- Main-file globals include `myScene`, `assetManager`, `waveEngine`, `waveStudio`, and direct asset maps such as `models`, `gaussianSplats`, `textures`, `materials`, `animations`, `audios`, `hdr`, `cubeMaps`, `fonts`, `serializedData`, `terrains`, `fx`, `particles`. Availability is not endorsement: choose the intent-owned facade/skill before using a global.",
|
|
44639
44644
|
`- Bare authoring globals: common examples include ${WAVE_AUTHORING_COMMON_GLOBAL_EXAMPLES_TEXT}, \`waveMaterial\`, \`waveFx\`, \`waveValueCurve\`, and \`waveParam\`. Use them directly in \`main.ts\`; do not import or namespace-qualify them. If the task is unfamiliar, retrieve the relevant local-SDK \`wave.*\` skill first; for the full generated exact-global surface, call local-SDK \`query_wave_api\`.`,
|
|
@@ -44659,6 +44664,7 @@ var WAVE_MCP_STUDIO_TOOL_GUIDE = [
|
|
|
44659
44664
|
"- `waveStudio.saveFor3dPrinting(target, options?)`: save export-friendly model.",
|
|
44660
44665
|
"- `waveStudio.saveMaterial(request)`: save authored material asset.",
|
|
44661
44666
|
"- `waveStudio.bakeTerrainMaterial(request)`: bake terrain material textures.",
|
|
44667
|
+
"- `waveStudio.reloadPreview({ reason? })`: request the Wave Studio toolbar-style reload path from authored code. Use this when a UI authored in `main.ts` changes Studio/bootstrap-backed state such as active city/world provider and must re-run the Studio preview package. Do not call `myScene.hotReload(...)` from Studio-authored code for this; that bypasses Studio VFS/package/source-patch orchestration.",
|
|
44662
44668
|
"- `waveStudio.exportEntity(name, entity)`: declare a live entity owned by the current source module for intentional modular reuse. `name` must be a static string literal and unique across runnable roots.",
|
|
44663
44669
|
"- `waveStudio.importEntity<T>(name)`: import an exported live entity into the current source module; Studio restores the exporter baseline and prunes/replays importer callbacks/overlays during source hot reload. Studio orders source roots by export/import dependencies: exporter before importer; exporter before `main.ts` if main imports it; overlay importers after exporter/main baseline. Missing, duplicate, dynamic, or cyclic entity names stop run/hot reload before execution.",
|
|
44664
44670
|
'- MCP command result rule: poll `get_wave_command_result({ requestId })` only when the previous tool response includes `status:"pending"` plus `requestId`. HTTP Gateway browser-backed tools may return pending and need that poll. WaveEngine Agent SDK browser-backed tools wait directly and return final results; its `get_wave_command_result` is a catalog-parity no-op unless a pending result was explicitly returned.',
|
|
@@ -44676,7 +44682,7 @@ var WAVE_MCP_TOOL_CATALOG_GUIDE = [
|
|
|
44676
44682
|
"- `get_wave_coding_guardrails`: call after onboarding acceptance; returns policy, file context, tool catalog, and recipes.",
|
|
44677
44683
|
'- `get_wave_mcp_health` (aliases `mcp_health`, `mcp_status`): read-only self diagnostic for endpoint kind, SDK version/build, tool catalog hash/count, onboarding, session/VFS, and WaveEngine SDK Cache state. Use when the agent asks "am I local/cloud/current/cached?" or any MCP state is unclear.',
|
|
44678
44684
|
"- `get_wave_tool_map`: stable complete concrete MCP tool menu grouped by family plus policy flags. Use only when `tools/list` feels truncated or unclear.",
|
|
44679
|
-
`- Transport topology rule: same-machine agents should run \`${WAVE3D_AGENT_SDK_START_COMMAND}\`, check /health, and update/restart SDK if version is stale, tools are missing, or SDK prints secrets; after Copy-to-Agent, restart with \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value without printing it so the copied local pairing secret still works. After restart recheck /health and tools/list. Same-machine stale/missing tools/401-with-Gateway-success means repair local SDK first, not HTTP Gateway first. Same-machine HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt; never use it because it is familiar, already authenticated, or avoids SDK repair. Different-machine/cloud/sandbox agents should use \`MCP_HTTP_GATEWAY_FALLBACK_URL\` directly. Stale SDK/cache are local setup issues, not fallback triggers; wrong-machine localhost is a topology mismatch.`,
|
|
44685
|
+
`- Transport topology rule: same-machine agents should run \`${WAVE3D_AGENT_SDK_START_COMMAND}\`, check /health, and update/restart SDK if version is stale, tools are missing, or SDK prints secrets; after Copy-to-Agent, restart with \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value without printing it so the copied local pairing secret still works. Current SDK start self-heals localhost SDK peers: reuse current same-token, replace verified stale/different-token SDK, refuse non-SDK port owners. After restart recheck /health and tools/list. Same-machine stale/missing tools/401-with-Gateway-success means repair local SDK first, not HTTP Gateway first. Same-machine HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt; never use it because it is familiar, already authenticated, or avoids SDK repair. Different-machine/cloud/sandbox agents should use \`MCP_HTTP_GATEWAY_FALLBACK_URL\` directly. Stale SDK/cache are local setup issues, not fallback triggers; wrong-machine localhost is a topology mismatch.`,
|
|
44680
44686
|
"- `recover_wave_connection`: direct Observe recovery tool. Call after onboarding required, session missing, command timeout/lease expired, mirror not ready, hosted proxy/cache failure, local/HTTP Gateway confusion, or sleeping-tab symptoms. It returns exact next action: re-onboard, get session, wake tab, update/restart SDK, wait mirror, retry, use HTTP Gateway fallback, or ask fresh Copy to Agent.",
|
|
44681
44687
|
'- WaveEngine SDK Cache: normal skill/API lookup lives inside the local `wave3d-agent-sdk` SDK package. Update/restart SDK when SDK is stale or missing. HTTP Gateway is Studio-ops-only and does not provide cloud skill/API lookup. If local SDK cannot work because sandbox/no filesystem/no Node/npm/user refusal/refresh failure, explicitly accept degraded fallback with `waveEngineSdkFallbackMode:"studio_ops_only"`, a valid `waveEngineSdkUnavailableReason`, and `studioOpsOnlyFallbackAccepted:true`; warn user skill/API lookup requires local SDK repair. This is retrieval cache, not WaveEngine Agent SDK transport. Never treat SDK cache failure as local transport failure. Keep Priority 1 role, guardrails, workflow, assets/VFS/hot-reload rules, prompt version, and prompt hash in active LLM context.',
|
|
44682
44688
|
"- Work-mode rule: Observe reads directly; Operate direct Studio commands directly; Author/Diagnose routes through `start_wave_task`; General avoids Wave tools. Operate tools include run/hot reload, save/share, VFS create/rename/delete exact files, asset upload/rename/delete exact assets, new project, project rename, and open project by exact projectId.",
|
|
@@ -44848,7 +44854,7 @@ var LOCAL_MCP_AGENT_ONBOARDING_PROMPT = [
|
|
|
44848
44854
|
`The WaveEngine Agent SDK answers skill/API/guardrail tools from the bundled WaveEngine SDK Cache. The SDK ships inside \`wave3d-agent-sdk\`; \`start\`, \`cache status\`, and \`cache search\` auto-prepare it locally. During \`accept_wave_agent_onboarding\`, production/HTTP-Gateway pairings must verify this local SDK cache before normal success. If missing, stale, or unverified, accept returns \`wave_engine_sdk_cache_not_ready\`; update/restart the exact SDK package or run the local bundled repair command \`${WAVE_MCP_CORPUS_CACHE_COMMAND_TEMPLATE}\`, then retry accept. If the agent is sandboxed, has no filesystem/Node/npm, or user refuses local SDK/cache, explicitly accept degraded Studio-ops-only fallback with \`waveEngineSdkFallbackMode:"studio_ops_only"\`, a valid \`waveEngineSdkUnavailableReason\`, and \`studioOpsOnlyFallbackAccepted:true\`; warn user HTTP Gateway can still do VFS/assets/project/preview/capture, but exact API lookup is unavailable until local SDK works. Keep the exact SDK command in active context as \`waveEngineSdkCacheCommandCached\`. Use local skill onion retrieval: \`list_wave_skill_families\` -> choose family ids -> \`query_wave_skills\` with \`families\`/\`familyId\` -> \`get_wave_skill\` -> \`query_wave_api\` only for exact signature/global/value uncertainty. HTTP Gateway is not an API lookup fallback.`,
|
|
44849
44855
|
"",
|
|
44850
44856
|
"Mandatory phased handshake:",
|
|
44851
|
-
`Phase 0 WaveEngine Agent SDK preflight is not optional. Do these phases in order before user work: 1 initialize endpoint; 2 call \`self_check_wave_mcp\` with the exact \`expectedVersion\` and \`expectedBuildHash\` from Copy-to-Agent; hard-stop if stale/hardStop is true or tool count/hash looks missing, then
|
|
44857
|
+
`Phase 0 WaveEngine Agent SDK preflight is not optional. Do these phases in order before user work: 1 initialize endpoint; 2 call \`self_check_wave_mcp\` with the exact \`expectedVersion\` and \`expectedBuildHash\` from Copy-to-Agent; hard-stop if stale/hardStop is true or tool count/hash looks missing, then run the SDK while preserving the copied MCP_TOKEN/local pairing secret with \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value hidden; current SDK start reuses a current same-token peer, replaces verified stale/different-token SDK peers on localhost, and refuses to kill non-SDK port owners; same-machine HTTP Gateway is degraded fallback only after SDK cannot run, update, or adopt, never because Gateway is familiar/easier; 2a when status is unclear, call \`get_wave_mcp_health\`/\`mcp_status\` to read endpoint/SDK/tool/SDK/session state; 3 call \`get_wave_agent_onboarding\` and cache Priority 1 rules in active context; 4 call \`accept_wave_agent_onboarding\` with acceptedRole true, current promptVersion/promptHash, all required cachedSections, full intentKindsCached list, exact waveEngineSdkCacheCommandCached template, and every context-cache confirmation true; if it returns \`wave_engine_sdk_cache_not_ready\`, update/restart SDK or run the bundled SDK repair command and retry accept; if local SDK cannot work, explicitly accept Studio-ops-only fallback and do not do unfamiliar API authoring; 5 call \`tools/list\` and prefer concrete tool names from the returned catalog; 6 call \`get_wave_session\`; 7 call \`list_wave_files\`; 8 if the tool menu is unclear, call \`get_wave_tool_map\`. Observe tools such as file reads, assets, diagnostics, screenshots, snapshots, performance, and markers stay open. Operate tools such as run preview, hot reload, save/share/rename/open/new project, VFS create/rename/delete, and asset upload/rename/delete are direct Studio commands when the user asks for that operation. Author/Diagnose tools such as \`query_wave_api\`, \`edit_wave_file\`, \`apply_wave_patch\`, and code behavior changes use \`start_wave_task\`. After startup, report phase status briefly. If a required phase fails or any later MCP state is unclear, call \`get_wave_mcp_health\` for state and \`recover_wave_connection({ lastErrorCode?, failedTool?, symptom? })\` for nextAction before asking user for a fresh copy; do not skip ahead.`,
|
|
44852
44858
|
"Tool-name tolerance: concrete names from tools/list remain preferred. Common aliases and safe high-confidence typos are tolerated. Destructive/write fuzzy matches return did-you-mean instead of executing.",
|
|
44853
44859
|
"Glossary: WaveEngine Agent SDK = same-machine live MCP transport plus bundled SDK lookup; Local Pairing Secret = MCP_TOKEN on localhost, not account/cloud auth; HTTP Gateway Bearer = MCP_TOKEN on HTTP Gateway; WaveEngine SDK Cache = package-bundled public docs/API/skill retrieval; MCP Session = browser pairing state; VFS Mirror = HTTP Gateway read/edit hash snapshot; Project = user workspace content. Do not merge these concepts.",
|
|
44854
44860
|
"",
|
|
@@ -44876,7 +44882,7 @@ var LOCAL_MCP_AGENT_ONBOARDING_PROMPT = [
|
|
|
44876
44882
|
].join("\n");
|
|
44877
44883
|
|
|
44878
44884
|
// ../../scripts/wavegenie-sdk/mcp/referenceTools.ts
|
|
44879
|
-
import { createHash } from "node:crypto";
|
|
44885
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
44880
44886
|
import { existsSync } from "node:fs";
|
|
44881
44887
|
import { readFile } from "node:fs/promises";
|
|
44882
44888
|
import { homedir } from "node:os";
|
|
@@ -45261,7 +45267,7 @@ function isLocalCorpusManifest(value) {
|
|
|
45261
45267
|
return isRecord2(value) && typeof value.version === "string" && (typeof value.formatVersion === "undefined" || typeof value.formatVersion === "string") && typeof value.cacheKey === "string" && typeof value.bundleFileName === "string" && typeof value.bundleHash === "string" && (typeof value.generatedAt === "undefined" || typeof value.generatedAt === "string") && (typeof value.sourceVersions === "undefined" || isRecord2(value.sourceVersions)) && Array.isArray(value.files) && value.files.every(isLocalCorpusFileManifestEntry);
|
|
45262
45268
|
}
|
|
45263
45269
|
function corpusFileHash(content) {
|
|
45264
|
-
return
|
|
45270
|
+
return createHash2("sha256").update(JSON.stringify(content)).digest("base64url").slice(0, 32);
|
|
45265
45271
|
}
|
|
45266
45272
|
function stableJson(value) {
|
|
45267
45273
|
if (Array.isArray(value)) return `[${value.map((item) => stableJson(item)).join(",")}]`;
|
|
@@ -46192,6 +46198,9 @@ var HOT_RELOAD_UNSAFE_SCENE_EVENT_CALLBACKS = /* @__PURE__ */ new Map([
|
|
|
46192
46198
|
["on", "myScene.on(...) is scene-scoped, but not authored-epoch scoped. It is cleared on scene transition, not on every preserve-scene hotreload. Use Wave-owned input/entity callbacks, or wait for a scoped Wave scene-event authoring API."],
|
|
46193
46199
|
["once", "myScene.once(...) is scene-scoped, but an unfired one-shot handler can survive preserve-scene hotreload. Use Wave-owned input/entity callbacks, or wait for a scoped Wave scene-event authoring API."]
|
|
46194
46200
|
]);
|
|
46201
|
+
var HOT_RELOAD_FORBIDDEN_STUDIO_SCENE_METHODS = /* @__PURE__ */ new Map([
|
|
46202
|
+
["hotReload", 'Studio-authored code must not call myScene.hotReload(...). Use waveStudio.reloadPreview({ reason: "..." }) so Wave Studio can flush VFS, rebuild the project package/bootstrap, sync assets, choose the correct reload tier, and return diagnostics.']
|
|
46203
|
+
]);
|
|
46195
46204
|
var HOT_RELOAD_UNSAFE_CONSTRUCTORS = /* @__PURE__ */ new Map([
|
|
46196
46205
|
["Promise", "new Promise(...) is not hotreload-safe because it creates unmanaged callback closures. Use awaitable Wave APIs instead."],
|
|
46197
46206
|
["MutationObserver", "DOM observers are not hotreload-safe. Use an engine-owned wrapper that can be disposed by hotreload."],
|
|
@@ -46279,6 +46288,7 @@ function readPropertyReceiverPath(code, tokenStart) {
|
|
|
46279
46288
|
while (index >= 0 && /\s/.test(code[index] ?? "")) index -= 1;
|
|
46280
46289
|
if (code[index] !== ".") return "";
|
|
46281
46290
|
index -= 1;
|
|
46291
|
+
if (code[index] === "?") index -= 1;
|
|
46282
46292
|
while (index >= 0 && /\s/.test(code[index] ?? "")) index -= 1;
|
|
46283
46293
|
const endIndex = index + 1;
|
|
46284
46294
|
while (index >= 0 && (isIdentifierPartChar(code[index]) || code[index] === "." || /\s/.test(code[index] ?? ""))) {
|
|
@@ -46328,6 +46338,9 @@ function skipQuotedRuntimeLiteral(code, startIndex, quote) {
|
|
|
46328
46338
|
return index;
|
|
46329
46339
|
}
|
|
46330
46340
|
function buildHotReloadUnsafeMessage(params) {
|
|
46341
|
+
if (params.severity === "error") {
|
|
46342
|
+
return `${RUNTIME_HOT_RELOAD_SAFETY_WARNING_PREFIX} ERROR: forbidden Studio reload API '${params.token}' at ${params.path}:${params.line}:${params.column}. ${params.guidance}`;
|
|
46343
|
+
}
|
|
46331
46344
|
return `${RUNTIME_HOT_RELOAD_SAFETY_WARNING_PREFIX} WARNING: unsafe unmanaged callback API '${params.token}' at ${params.path}:${params.line}:${params.column}. Code will still run, but this callback is registered outside Wave's authored callback registry, so hotreload cannot unregister it and the closure can retain old scene/entity/model graphs. Memory leak risk. Recommendation: ${params.guidance}`;
|
|
46332
46345
|
}
|
|
46333
46346
|
function reportHotReloadUnsafeUsage(params) {
|
|
@@ -46336,6 +46349,7 @@ function reportHotReloadUnsafeUsage(params) {
|
|
|
46336
46349
|
const key = `${params.path}:${line}:${column}:${params.token}`;
|
|
46337
46350
|
if (params.reported.has(key)) return;
|
|
46338
46351
|
params.reported.add(key);
|
|
46352
|
+
const severity = params.severity ?? "warning";
|
|
46339
46353
|
params.diagnostics.push({
|
|
46340
46354
|
path: params.path,
|
|
46341
46355
|
token: params.token,
|
|
@@ -46343,12 +46357,14 @@ function reportHotReloadUnsafeUsage(params) {
|
|
|
46343
46357
|
length: params.token.length,
|
|
46344
46358
|
line,
|
|
46345
46359
|
column,
|
|
46360
|
+
severity,
|
|
46346
46361
|
guidance: params.guidance,
|
|
46347
46362
|
message: buildHotReloadUnsafeMessage({
|
|
46348
46363
|
path: params.path,
|
|
46349
46364
|
token: params.token,
|
|
46350
46365
|
line,
|
|
46351
46366
|
column,
|
|
46367
|
+
severity,
|
|
46352
46368
|
guidance: params.guidance
|
|
46353
46369
|
})
|
|
46354
46370
|
});
|
|
@@ -46393,7 +46409,8 @@ function collectRuntimeHotReloadSafetyDiagnostics(path5, code, limit = HOT_RELOA
|
|
|
46393
46409
|
const nextNonWhitespace = code[nextNonWhitespaceIndex] ?? "";
|
|
46394
46410
|
const previousChar = readPreviousNonWhitespaceChar(code, tokenStart);
|
|
46395
46411
|
const previousIdentifier = readPreviousIdentifier(code, tokenStart);
|
|
46396
|
-
const
|
|
46412
|
+
const optionalCallParenIndex = nextNonWhitespace === "?" && code[nextNonWhitespaceIndex + 1] === "." ? readNextNonWhitespaceIndex(code, nextNonWhitespaceIndex + 2) : -1;
|
|
46413
|
+
const isCall = nextNonWhitespace === "(" || optionalCallParenIndex >= 0 && code[optionalCallParenIndex] === "(";
|
|
46397
46414
|
const receiverPath = previousChar === "." ? readPropertyReceiverPath(code, tokenStart) : "";
|
|
46398
46415
|
const globalGuidance = HOT_RELOAD_UNSAFE_GLOBAL_CALLBACKS.get(token);
|
|
46399
46416
|
if (globalGuidance) {
|
|
@@ -46420,6 +46437,20 @@ function collectRuntimeHotReloadSafetyDiagnostics(path5, code, limit = HOT_RELOA
|
|
|
46420
46437
|
reportHotReloadUnsafeUsage({ code, path: path5, token, offset: tokenStart, guidance: sceneEventGuidance, diagnostics, reported });
|
|
46421
46438
|
continue;
|
|
46422
46439
|
}
|
|
46440
|
+
const forbiddenStudioSceneMethodGuidance = HOT_RELOAD_FORBIDDEN_STUDIO_SCENE_METHODS.get(token);
|
|
46441
|
+
if (forbiddenStudioSceneMethodGuidance && previousChar === "." && isCall && isWaveSceneEventReceiverPath(receiverPath)) {
|
|
46442
|
+
reportHotReloadUnsafeUsage({
|
|
46443
|
+
code,
|
|
46444
|
+
path: path5,
|
|
46445
|
+
token,
|
|
46446
|
+
offset: tokenStart,
|
|
46447
|
+
severity: "error",
|
|
46448
|
+
guidance: forbiddenStudioSceneMethodGuidance,
|
|
46449
|
+
diagnostics,
|
|
46450
|
+
reported
|
|
46451
|
+
});
|
|
46452
|
+
continue;
|
|
46453
|
+
}
|
|
46423
46454
|
const constructorGuidance = HOT_RELOAD_UNSAFE_CONSTRUCTORS.get(token);
|
|
46424
46455
|
if (constructorGuidance && (previousIdentifier === "new" || previousChar === "." && isCall)) {
|
|
46425
46456
|
reportHotReloadUnsafeUsage({ code, path: path5, token, offset: tokenStart, guidance: constructorGuidance, diagnostics, reported });
|
|
@@ -47403,7 +47434,7 @@ async function handleVfsTool(input) {
|
|
|
47403
47434
|
}
|
|
47404
47435
|
|
|
47405
47436
|
// ../../scripts/wavegenie-sdk/mcp/toolDefinitions.ts
|
|
47406
|
-
import { createHash as
|
|
47437
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
47407
47438
|
|
|
47408
47439
|
// ../../src/lib/waveStudio/authoring/workspace/vfs/vfsImportAnalysis.ts
|
|
47409
47440
|
var VFS_CODE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
@@ -49738,7 +49769,7 @@ function getLocalToolCatalogHash() {
|
|
|
49738
49769
|
description: definition.description,
|
|
49739
49770
|
inputSchema: definition.inputSchema
|
|
49740
49771
|
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
49741
|
-
return
|
|
49772
|
+
return createHash3("sha256").update(JSON.stringify(tools)).digest("base64url").slice(0, 24);
|
|
49742
49773
|
}
|
|
49743
49774
|
|
|
49744
49775
|
// ../../scripts/wavegenie-sdk/mcp.ts
|
|
@@ -49937,7 +49968,7 @@ function localConnectionRecoveryResult(args, context) {
|
|
|
49937
49968
|
nextAction = "Wave Studio tab is paired but not actively polling commands, or browser-backed command timed out. Ask user to focus/wake the tab and verify MCP is live; retry the same tool once on the same local endpoint and MCP_TOKEN pairing secret.";
|
|
49938
49969
|
nextTool = failedTool || "get_wave_session";
|
|
49939
49970
|
requiresUserAction = true;
|
|
49940
|
-
steps.push(`If SDK /health is down before pairing, start \`${WAVE3D_AGENT_SDK_START_COMMAND}\`; after Copy-to-Agent,
|
|
49971
|
+
steps.push(`If SDK /health is down before pairing, start \`${WAVE3D_AGENT_SDK_START_COMMAND}\`; after Copy-to-Agent, run \`${WAVE3D_AGENT_SDK_TOKEN_PRESERVING_START_COMMAND}\` using the current MCP_TOKEN value hidden. Current SDK start self-heals verified stale/different-token SDK peers and refuses non-SDK port owners. If /health is OK and version/token match Copy to Agent, do not restart SDK first.`);
|
|
49941
49972
|
} else if (lastErrorCode === "session_write_not_allowed" || selectedSession?.accessMode !== "readwrite") {
|
|
49942
49973
|
state = "write_access_not_ready";
|
|
49943
49974
|
nextAction = "Read tools can continue. For write/run/hot reload, user must make the Studio MCP session read-write/adopt WaveEngine Agent SDK, then retry same local endpoint and MCP_TOKEN pairing secret or paste fresh Copy to Agent if the secret changed.";
|
|
@@ -49997,7 +50028,7 @@ function localConnectionRecoveryResult(args, context) {
|
|
|
49997
50028
|
steps,
|
|
49998
50029
|
hardStopRules: [
|
|
49999
50030
|
"Local HTTP refused/unreachable before MCP response: start WaveEngine Agent SDK, retry localhost for up to 10 seconds.",
|
|
50000
|
-
"Local HTTP 401/bridge_auth_invalid: if HTTP Gateway accepts the same MCP_TOKEN, SDK is stale/not adopted:
|
|
50031
|
+
"Local HTTP 401/bridge_auth_invalid: if HTTP Gateway accepts the same MCP_TOKEN, SDK is stale/not adopted: run the exact token-preserving WaveEngine Agent SDK command so it adopts MCP_TOKEN as the local pairing secret, then retry local. If every endpoint rejects MCP_TOKEN, ask user for fresh Copy to Agent.",
|
|
50001
50032
|
"No paired session or stale command poll: wake/reopen Wave Studio tab; keep same local endpoint and MCP_TOKEN pairing secret first.",
|
|
50002
50033
|
"WaveEngine SDK Cache failure is not live transport failure."
|
|
50003
50034
|
]
|
|
@@ -51352,7 +51383,7 @@ async function handleWaveGenieMcpRequest(request, context) {
|
|
|
51352
51383
|
}
|
|
51353
51384
|
|
|
51354
51385
|
// ../../scripts/wavegenie-sdk/sessionRegistry.ts
|
|
51355
|
-
import { createHash as
|
|
51386
|
+
import { createHash as createHash4, randomUUID as randomUUID2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
51356
51387
|
function createSessionId() {
|
|
51357
51388
|
return `wgb_${randomUUID2()}`;
|
|
51358
51389
|
}
|
|
@@ -51360,7 +51391,7 @@ function nowIso2() {
|
|
|
51360
51391
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
51361
51392
|
}
|
|
51362
51393
|
function hashAgentToken(token) {
|
|
51363
|
-
return
|
|
51394
|
+
return createHash4("sha256").update(token).digest("hex");
|
|
51364
51395
|
}
|
|
51365
51396
|
function tokenHashMatches(left, right) {
|
|
51366
51397
|
const leftBuffer = Buffer.from(left, "hex");
|
|
@@ -51494,7 +51525,7 @@ var WaveGenieBridgeSessionRegistry = class {
|
|
|
51494
51525
|
};
|
|
51495
51526
|
|
|
51496
51527
|
// ../../scripts/wavegenie-sdk/assetStaging.ts
|
|
51497
|
-
import { createHash as
|
|
51528
|
+
import { createHash as createHash5, randomBytes as randomBytes2, randomUUID as randomUUID3 } from "node:crypto";
|
|
51498
51529
|
import { createWriteStream } from "node:fs";
|
|
51499
51530
|
import { mkdir as mkdir2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
51500
51531
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
@@ -51511,7 +51542,7 @@ function createSecret() {
|
|
|
51511
51542
|
return randomBytes2(24).toString("base64url");
|
|
51512
51543
|
}
|
|
51513
51544
|
function hashPathPart(value) {
|
|
51514
|
-
return
|
|
51545
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 20);
|
|
51515
51546
|
}
|
|
51516
51547
|
function decodeBase64(dataBase64) {
|
|
51517
51548
|
const normalized = dataBase64.trim();
|
|
@@ -51971,8 +52002,10 @@ async function startWaveEngineAgentSdkServer(options = {}) {
|
|
|
51971
52002
|
bridgeId,
|
|
51972
52003
|
version: MCP_SERVER_VERSION,
|
|
51973
52004
|
packageVersion: MCP_SERVER_VERSION,
|
|
52005
|
+
processId: process.pid,
|
|
51974
52006
|
protocolVersion: "waveengine-agent-sdk-http-v1",
|
|
51975
52007
|
buildHash: WAVE3D_AGENT_SDK_REQUIRED_BUILD,
|
|
52008
|
+
tokenFingerprint: createWaveGenieBridgeTokenFingerprint(token),
|
|
51976
52009
|
requiredPackage: WAVE3D_AGENT_SDK_PACKAGE_SPEC,
|
|
51977
52010
|
requiredVersion: WAVE3D_AGENT_SDK_REQUIRED_VERSION,
|
|
51978
52011
|
requiredBuildHash: WAVE3D_AGENT_SDK_REQUIRED_BUILD,
|
|
@@ -52259,6 +52292,7 @@ var BUNDLED_SDK_DIR_NAME = "sdk";
|
|
|
52259
52292
|
var MAX_CORPUS_ZIP_BYTES2 = 50 * 1024 * 1024;
|
|
52260
52293
|
var MAX_CORPUS_EXTRACTED_BYTES2 = 100 * 1024 * 1024;
|
|
52261
52294
|
var MAX_CORPUS_FILE_COUNT = 5e3;
|
|
52295
|
+
var execFileAsync = promisify(execFile);
|
|
52262
52296
|
function parseArgs(argv) {
|
|
52263
52297
|
const flags = /* @__PURE__ */ new Map();
|
|
52264
52298
|
const positionals = [];
|
|
@@ -52356,6 +52390,307 @@ function requireSafePathSegment(value, label) {
|
|
|
52356
52390
|
function isRecord4(value) {
|
|
52357
52391
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
52358
52392
|
}
|
|
52393
|
+
function formatLoopbackHttpUrl(host, port, pathname) {
|
|
52394
|
+
const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
52395
|
+
return `http://${urlHost}:${port}${pathname}`;
|
|
52396
|
+
}
|
|
52397
|
+
function readNumberProperty(record, name) {
|
|
52398
|
+
const value = record[name];
|
|
52399
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
52400
|
+
}
|
|
52401
|
+
function readStringProperty(record, name) {
|
|
52402
|
+
const value = record[name];
|
|
52403
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
52404
|
+
}
|
|
52405
|
+
function readLocalSdkPeerHealth(value) {
|
|
52406
|
+
if (!isRecord4(value)) return null;
|
|
52407
|
+
const data = isRecord4(value.data) ? value.data : value;
|
|
52408
|
+
const bridgeId = readStringProperty(data, "bridgeId");
|
|
52409
|
+
const protocolVersion = readStringProperty(data, "protocolVersion");
|
|
52410
|
+
const requiredPackage = readStringProperty(data, "requiredPackage");
|
|
52411
|
+
const isCurrentSdkProtocol = protocolVersion === "waveengine-agent-sdk-http-v1";
|
|
52412
|
+
const isLegacyHelperProtocol = protocolVersion === "local-agent-helper-http-v1" && typeof requiredPackage === "string" && requiredPackage.startsWith("wave3d-agent-bridge@");
|
|
52413
|
+
if (!bridgeId || !bridgeId.startsWith("wgbridge_") || !isCurrentSdkProtocol && !isLegacyHelperProtocol) {
|
|
52414
|
+
return null;
|
|
52415
|
+
}
|
|
52416
|
+
return {
|
|
52417
|
+
bridgeId,
|
|
52418
|
+
packageVersion: readStringProperty(data, "packageVersion"),
|
|
52419
|
+
protocolVersion,
|
|
52420
|
+
buildHash: readStringProperty(data, "buildHash"),
|
|
52421
|
+
processId: readNumberProperty(data, "processId"),
|
|
52422
|
+
tokenFingerprint: readStringProperty(data, "tokenFingerprint")
|
|
52423
|
+
};
|
|
52424
|
+
}
|
|
52425
|
+
function parseProcessIds(output) {
|
|
52426
|
+
const ids = /* @__PURE__ */ new Set();
|
|
52427
|
+
for (const match of output.matchAll(/\b\d+\b/g)) {
|
|
52428
|
+
const pid = Number(match[0]);
|
|
52429
|
+
if (Number.isInteger(pid) && pid > 0 && pid !== process.pid) ids.add(pid);
|
|
52430
|
+
}
|
|
52431
|
+
return [...ids];
|
|
52432
|
+
}
|
|
52433
|
+
async function execProcessIdCommand(command, args) {
|
|
52434
|
+
try {
|
|
52435
|
+
const { stdout } = await execFileAsync(command, args, {
|
|
52436
|
+
timeout: 2500,
|
|
52437
|
+
maxBuffer: 128 * 1024,
|
|
52438
|
+
windowsHide: true
|
|
52439
|
+
});
|
|
52440
|
+
return parseProcessIds(stdout);
|
|
52441
|
+
} catch {
|
|
52442
|
+
return [];
|
|
52443
|
+
}
|
|
52444
|
+
}
|
|
52445
|
+
async function findListenerProcessIds(port) {
|
|
52446
|
+
if (process.platform === "win32") {
|
|
52447
|
+
return execProcessIdCommand("powershell.exe", [
|
|
52448
|
+
"-NoProfile",
|
|
52449
|
+
"-Command",
|
|
52450
|
+
`(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | Sort-Object -Unique) -join "
|
|
52451
|
+
"`
|
|
52452
|
+
]);
|
|
52453
|
+
}
|
|
52454
|
+
return execProcessIdCommand("lsof", ["-nP", `-tiTCP:${port}`, "-sTCP:LISTEN"]);
|
|
52455
|
+
}
|
|
52456
|
+
async function readProcessInfo(pid) {
|
|
52457
|
+
if (pid === process.pid) return null;
|
|
52458
|
+
try {
|
|
52459
|
+
if (process.platform === "win32") {
|
|
52460
|
+
const { stdout: stdout2 } = await execFileAsync("powershell.exe", [
|
|
52461
|
+
"-NoProfile",
|
|
52462
|
+
"-Command",
|
|
52463
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress)`
|
|
52464
|
+
], {
|
|
52465
|
+
timeout: 2500,
|
|
52466
|
+
maxBuffer: 128 * 1024,
|
|
52467
|
+
windowsHide: true
|
|
52468
|
+
});
|
|
52469
|
+
const parsed = JSON.parse(stdout2);
|
|
52470
|
+
if (!isRecord4(parsed)) return null;
|
|
52471
|
+
const parsedPid = readNumberProperty(parsed, "ProcessId");
|
|
52472
|
+
const parentPid = readNumberProperty(parsed, "ParentProcessId");
|
|
52473
|
+
const command = readStringProperty(parsed, "CommandLine");
|
|
52474
|
+
if (!parsedPid || !parentPid || !command) return null;
|
|
52475
|
+
return { pid: parsedPid, parentPid, command };
|
|
52476
|
+
}
|
|
52477
|
+
const { stdout } = await execFileAsync("ps", [
|
|
52478
|
+
"-o",
|
|
52479
|
+
"pid=",
|
|
52480
|
+
"-o",
|
|
52481
|
+
"ppid=",
|
|
52482
|
+
"-o",
|
|
52483
|
+
"command=",
|
|
52484
|
+
"-p",
|
|
52485
|
+
String(pid)
|
|
52486
|
+
], {
|
|
52487
|
+
timeout: 2500,
|
|
52488
|
+
maxBuffer: 128 * 1024,
|
|
52489
|
+
windowsHide: true
|
|
52490
|
+
});
|
|
52491
|
+
const match = stdout.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
52492
|
+
if (!match) return null;
|
|
52493
|
+
return {
|
|
52494
|
+
pid: Number(match[1]),
|
|
52495
|
+
parentPid: Number(match[2]),
|
|
52496
|
+
command: match[3]
|
|
52497
|
+
};
|
|
52498
|
+
} catch {
|
|
52499
|
+
return null;
|
|
52500
|
+
}
|
|
52501
|
+
}
|
|
52502
|
+
async function findChildProcessIds(parentPid) {
|
|
52503
|
+
if (parentPid <= 0 || parentPid === process.pid) return [];
|
|
52504
|
+
if (process.platform === "win32") {
|
|
52505
|
+
return execProcessIdCommand("powershell.exe", [
|
|
52506
|
+
"-NoProfile",
|
|
52507
|
+
"-Command",
|
|
52508
|
+
`(Get-CimInstance Win32_Process -Filter "ParentProcessId = ${parentPid}" | Select-Object -ExpandProperty ProcessId | Sort-Object -Unique) -join "
|
|
52509
|
+
"`
|
|
52510
|
+
]);
|
|
52511
|
+
}
|
|
52512
|
+
try {
|
|
52513
|
+
const { stdout } = await execFileAsync("ps", ["-axo", "pid=", "-o", "ppid="], {
|
|
52514
|
+
timeout: 2500,
|
|
52515
|
+
maxBuffer: 512 * 1024,
|
|
52516
|
+
windowsHide: true
|
|
52517
|
+
});
|
|
52518
|
+
const ids = [];
|
|
52519
|
+
for (const line of stdout.split("\n")) {
|
|
52520
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
52521
|
+
if (!match) continue;
|
|
52522
|
+
const pid = Number(match[1]);
|
|
52523
|
+
const ppid = Number(match[2]);
|
|
52524
|
+
if (ppid === parentPid && pid !== process.pid) ids.push(pid);
|
|
52525
|
+
}
|
|
52526
|
+
return ids;
|
|
52527
|
+
} catch {
|
|
52528
|
+
return [];
|
|
52529
|
+
}
|
|
52530
|
+
}
|
|
52531
|
+
function isWaveEngineAgentProcessCommand(command) {
|
|
52532
|
+
return /\bwave3d-agent-(sdk|bridge)\b/.test(command);
|
|
52533
|
+
}
|
|
52534
|
+
function isLegacyBridgePeer(health) {
|
|
52535
|
+
return health.protocolVersion === "local-agent-helper-http-v1";
|
|
52536
|
+
}
|
|
52537
|
+
async function stopLegacyBridgeLaunchAgent(health) {
|
|
52538
|
+
if (!isLegacyBridgePeer(health) || process.platform !== "darwin") return;
|
|
52539
|
+
const getuid = process.getuid;
|
|
52540
|
+
if (typeof getuid !== "function") return;
|
|
52541
|
+
const uid = getuid();
|
|
52542
|
+
const launchTarget = `gui/${uid}/com.wave3d.agent-bridge`;
|
|
52543
|
+
console.log("[WaveEngine Agent SDK] Stopping legacy wave3d-agent-bridge launch agent before replacement.");
|
|
52544
|
+
try {
|
|
52545
|
+
await execFileAsync("launchctl", ["bootout", launchTarget], {
|
|
52546
|
+
timeout: 2500,
|
|
52547
|
+
maxBuffer: 128 * 1024,
|
|
52548
|
+
windowsHide: true
|
|
52549
|
+
});
|
|
52550
|
+
} catch {
|
|
52551
|
+
}
|
|
52552
|
+
try {
|
|
52553
|
+
await execFileAsync("launchctl", ["remove", "com.wave3d.agent-bridge"], {
|
|
52554
|
+
timeout: 2500,
|
|
52555
|
+
maxBuffer: 128 * 1024,
|
|
52556
|
+
windowsHide: true
|
|
52557
|
+
});
|
|
52558
|
+
} catch {
|
|
52559
|
+
}
|
|
52560
|
+
}
|
|
52561
|
+
async function collectVerifiedSdkPeerProcessIds(seedPids) {
|
|
52562
|
+
const targetPids = /* @__PURE__ */ new Set();
|
|
52563
|
+
const parentPids = /* @__PURE__ */ new Set();
|
|
52564
|
+
for (const seedPid of seedPids) {
|
|
52565
|
+
if (seedPid <= 0 || seedPid === process.pid) continue;
|
|
52566
|
+
targetPids.add(seedPid);
|
|
52567
|
+
const processInfo = await readProcessInfo(seedPid);
|
|
52568
|
+
if (!processInfo) continue;
|
|
52569
|
+
if (isWaveEngineAgentProcessCommand(processInfo.command)) {
|
|
52570
|
+
for (const childPid of await findChildProcessIds(processInfo.pid)) targetPids.add(childPid);
|
|
52571
|
+
}
|
|
52572
|
+
if (processInfo.parentPid > 1 && processInfo.parentPid !== process.pid) {
|
|
52573
|
+
const parentInfo = await readProcessInfo(processInfo.parentPid);
|
|
52574
|
+
if (parentInfo && isWaveEngineAgentProcessCommand(parentInfo.command)) {
|
|
52575
|
+
parentPids.add(parentInfo.pid);
|
|
52576
|
+
for (const childPid of await findChildProcessIds(parentInfo.pid)) targetPids.add(childPid);
|
|
52577
|
+
}
|
|
52578
|
+
}
|
|
52579
|
+
}
|
|
52580
|
+
for (const parentPid of parentPids) targetPids.delete(parentPid);
|
|
52581
|
+
return [...parentPids, ...targetPids].filter((pid) => pid !== process.pid);
|
|
52582
|
+
}
|
|
52583
|
+
async function readLocalSdkPeerProbe(host, port) {
|
|
52584
|
+
const healthUrl = formatLoopbackHttpUrl(host, port, "/health");
|
|
52585
|
+
let response;
|
|
52586
|
+
try {
|
|
52587
|
+
response = await fetch(healthUrl, { signal: AbortSignal.timeout(1500) });
|
|
52588
|
+
} catch {
|
|
52589
|
+
return { kind: "none" };
|
|
52590
|
+
}
|
|
52591
|
+
let parsed;
|
|
52592
|
+
try {
|
|
52593
|
+
parsed = await response.json();
|
|
52594
|
+
} catch {
|
|
52595
|
+
return {
|
|
52596
|
+
kind: "non-sdk",
|
|
52597
|
+
message: `Port ${port} is occupied by an HTTP server, but /health did not return JSON.`
|
|
52598
|
+
};
|
|
52599
|
+
}
|
|
52600
|
+
const health = readLocalSdkPeerHealth(parsed);
|
|
52601
|
+
if (!health) {
|
|
52602
|
+
return {
|
|
52603
|
+
kind: "non-sdk",
|
|
52604
|
+
message: `Port ${port} is occupied, but /health is not a WaveEngine Agent SDK health response.`
|
|
52605
|
+
};
|
|
52606
|
+
}
|
|
52607
|
+
return { kind: "sdk", health };
|
|
52608
|
+
}
|
|
52609
|
+
function isCurrentSdkPeer(health) {
|
|
52610
|
+
return health.packageVersion === WAVE3D_AGENT_SDK_REQUIRED_VERSION && health.buildHash === WAVE3D_AGENT_SDK_REQUIRED_BUILD;
|
|
52611
|
+
}
|
|
52612
|
+
function shouldReplaceCurrentSdkPeer(health, token) {
|
|
52613
|
+
if (!isCurrentSdkPeer(health)) return true;
|
|
52614
|
+
if (!token) return false;
|
|
52615
|
+
const expectedFingerprint = createWaveGenieBridgeTokenFingerprint(token);
|
|
52616
|
+
return health.tokenFingerprint !== expectedFingerprint;
|
|
52617
|
+
}
|
|
52618
|
+
async function waitForSdkPeerExit(pids, deadlineMs) {
|
|
52619
|
+
const deadline = Date.now() + deadlineMs;
|
|
52620
|
+
while (Date.now() < deadline) {
|
|
52621
|
+
const alive = pids.some((pid) => {
|
|
52622
|
+
try {
|
|
52623
|
+
process.kill(pid, 0);
|
|
52624
|
+
return true;
|
|
52625
|
+
} catch {
|
|
52626
|
+
return false;
|
|
52627
|
+
}
|
|
52628
|
+
});
|
|
52629
|
+
if (!alive) return true;
|
|
52630
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
52631
|
+
}
|
|
52632
|
+
return false;
|
|
52633
|
+
}
|
|
52634
|
+
async function waitForSdkPeerPortRelease(port, deadlineMs) {
|
|
52635
|
+
const deadline = Date.now() + deadlineMs;
|
|
52636
|
+
while (Date.now() < deadline) {
|
|
52637
|
+
const listenerPids = await findListenerProcessIds(port);
|
|
52638
|
+
if (listenerPids.every((pid) => pid === process.pid)) return true;
|
|
52639
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
52640
|
+
}
|
|
52641
|
+
return false;
|
|
52642
|
+
}
|
|
52643
|
+
async function terminateVerifiedSdkPeer(health, port) {
|
|
52644
|
+
await stopLegacyBridgeLaunchAgent(health);
|
|
52645
|
+
const seedPids = /* @__PURE__ */ new Set();
|
|
52646
|
+
if (health.processId && health.processId !== process.pid) seedPids.add(health.processId);
|
|
52647
|
+
for (const pid of await findListenerProcessIds(port)) seedPids.add(pid);
|
|
52648
|
+
if (seedPids.size === 0) {
|
|
52649
|
+
if (await waitForSdkPeerPortRelease(port, 250)) return;
|
|
52650
|
+
throw new Error(`WaveEngine Agent SDK peer on port ${port} is stale, but no listener PID could be found.`);
|
|
52651
|
+
}
|
|
52652
|
+
let targetPids = await collectVerifiedSdkPeerProcessIds([...seedPids]);
|
|
52653
|
+
if (targetPids.length === 0) {
|
|
52654
|
+
throw new Error("Refusing to terminate current SDK process.");
|
|
52655
|
+
}
|
|
52656
|
+
for (const pid of targetPids) {
|
|
52657
|
+
try {
|
|
52658
|
+
process.kill(pid, "SIGTERM");
|
|
52659
|
+
} catch {
|
|
52660
|
+
}
|
|
52661
|
+
}
|
|
52662
|
+
const exitedAfterTerm = await waitForSdkPeerExit(targetPids, 2500);
|
|
52663
|
+
if (exitedAfterTerm && await waitForSdkPeerPortRelease(port, 1500)) return;
|
|
52664
|
+
targetPids = await collectVerifiedSdkPeerProcessIds(await findListenerProcessIds(port));
|
|
52665
|
+
for (const pid of targetPids) {
|
|
52666
|
+
try {
|
|
52667
|
+
process.kill(pid, "SIGKILL");
|
|
52668
|
+
} catch {
|
|
52669
|
+
}
|
|
52670
|
+
}
|
|
52671
|
+
const exitedAfterKill = await waitForSdkPeerExit(targetPids, 2500);
|
|
52672
|
+
if (!exitedAfterKill || !await waitForSdkPeerPortRelease(port, 1500)) {
|
|
52673
|
+
throw new Error(`WaveEngine Agent SDK peer on port ${port} did not exit after termination.`);
|
|
52674
|
+
}
|
|
52675
|
+
}
|
|
52676
|
+
async function prepareLocalSdkPeerForStart(input) {
|
|
52677
|
+
if (!isLoopbackHost(input.host)) return false;
|
|
52678
|
+
if (input.allowNetwork) return false;
|
|
52679
|
+
const probe = await readLocalSdkPeerProbe(input.host, input.port);
|
|
52680
|
+
if (probe.kind === "none") return false;
|
|
52681
|
+
if (probe.kind === "non-sdk") {
|
|
52682
|
+
throw new Error(`${probe.message} Refusing to kill a non-SDK process.`);
|
|
52683
|
+
}
|
|
52684
|
+
if (!shouldReplaceCurrentSdkPeer(probe.health, input.token)) {
|
|
52685
|
+
console.log(`[WaveEngine Agent SDK] Existing SDK on ${formatLoopbackHttpUrl(input.host, input.port, "")} is current; reusing it.`);
|
|
52686
|
+
console.log("[WaveEngine Agent SDK] If a new Copy-to-Agent token fails later, restart with WAVE3D_MCP_TOKEN from that copy.");
|
|
52687
|
+
return true;
|
|
52688
|
+
}
|
|
52689
|
+
const reason = isCurrentSdkPeer(probe.health) ? "local pairing secret does not match this start request" : `stale version/build (${probe.health.packageVersion ?? "unknown"} ${probe.health.buildHash ?? "unknown"})`;
|
|
52690
|
+
console.log(`[WaveEngine Agent SDK] Replacing existing SDK on port ${input.port}: ${reason}.`);
|
|
52691
|
+
await terminateVerifiedSdkPeer(probe.health, input.port);
|
|
52692
|
+
return false;
|
|
52693
|
+
}
|
|
52359
52694
|
function isCorpusFileManifestEntry(value) {
|
|
52360
52695
|
if (!isRecord4(value)) return false;
|
|
52361
52696
|
return typeof value.path === "string" && typeof value.mediaType === "string" && typeof value.role === "string" && typeof value.bytes === "number" && Number.isFinite(value.bytes) && Number.isInteger(value.bytes) && value.bytes >= 0 && typeof value.hash === "string";
|
|
@@ -52365,7 +52700,7 @@ function isCorpusManifest(value) {
|
|
|
52365
52700
|
return typeof value.version === "string" && (typeof value.formatVersion === "undefined" || typeof value.formatVersion === "string") && typeof value.cacheKey === "string" && typeof value.bundleFileName === "string" && typeof value.bundleHash === "string" && (typeof value.generatedAt === "undefined" || typeof value.generatedAt === "string") && (typeof value.sourceVersions === "undefined" || isRecord4(value.sourceVersions)) && Array.isArray(value.files) && value.files.every(isCorpusFileManifestEntry);
|
|
52366
52701
|
}
|
|
52367
52702
|
function corpusFileHash2(content) {
|
|
52368
|
-
return
|
|
52703
|
+
return createHash6("sha256").update(JSON.stringify(content)).digest("base64url").slice(0, 32);
|
|
52369
52704
|
}
|
|
52370
52705
|
function stableJson2(value) {
|
|
52371
52706
|
if (Array.isArray(value)) return `[${value.map((item) => stableJson2(item)).join(",")}]`;
|
|
@@ -52515,14 +52850,24 @@ function printUsage() {
|
|
|
52515
52850
|
}
|
|
52516
52851
|
async function commandStart(options) {
|
|
52517
52852
|
const host = readStringFlag(options, "host") ?? DEFAULT_HOST2;
|
|
52518
|
-
|
|
52853
|
+
const port = readNumberFlag(options, "port") ?? DEFAULT_PORT2;
|
|
52854
|
+
const token = readStringFlag(options, "token") ?? process.env.WAVE3D_MCP_TOKEN ?? process.env.WAVESTUDIO_MCP_TOKEN;
|
|
52855
|
+
const allowNetwork = readBooleanFlag(options, "allow-network");
|
|
52856
|
+
assertSafeListenHost(host, allowNetwork);
|
|
52519
52857
|
await ensureBundledSdkCache(options).catch((error) => {
|
|
52520
52858
|
console.warn(`[WaveEngine Agent SDK] Bundled WaveEngine SDK cache prepare failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
52521
52859
|
});
|
|
52860
|
+
const reusedExistingPeer = await prepareLocalSdkPeerForStart({
|
|
52861
|
+
host,
|
|
52862
|
+
port,
|
|
52863
|
+
token,
|
|
52864
|
+
allowNetwork
|
|
52865
|
+
});
|
|
52866
|
+
if (reusedExistingPeer) return;
|
|
52522
52867
|
const handle = await startWaveEngineAgentSdkServer({
|
|
52523
52868
|
host,
|
|
52524
|
-
port
|
|
52525
|
-
token
|
|
52869
|
+
port,
|
|
52870
|
+
token,
|
|
52526
52871
|
sessionTtlMs: readNumberFlag(options, "session-ttl-ms")
|
|
52527
52872
|
});
|
|
52528
52873
|
console.log(`[WaveEngine Agent SDK] sdkId=${handle.bridgeId}`);
|
package/dist/sdk/README.md
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
Bundled public-safe WaveEngine SDK lookup cache for wave3d-agent-sdk.
|
|
4
4
|
Generated from sanitized public API/skill corpus during package build.
|
|
5
5
|
|
|
6
|
-
cacheKey: 2026-06-17.1.
|
|
7
|
-
bundleHash:
|
|
8
|
-
zip: wave-engine-sdk-2026-06-17.1.
|
|
6
|
+
cacheKey: 2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf
|
|
7
|
+
bundleHash: TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf
|
|
8
|
+
zip: wave-engine-sdk-2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf.zip
|
package/dist/sdk/manifest.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2026-06-17.1.
|
|
2
|
+
"version": "2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf",
|
|
3
3
|
"formatVersion": "2026-06-17.1",
|
|
4
|
-
"cacheKey": "2026-06-17.1.
|
|
5
|
-
"bundleFileName": "wave-engine-sdk-2026-06-17.1.
|
|
6
|
-
"bundleHash": "
|
|
4
|
+
"cacheKey": "2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf",
|
|
5
|
+
"bundleFileName": "wave-engine-sdk-2026-06-17.1.TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf.zip",
|
|
6
|
+
"bundleHash": "TzPhd-nl78ioHZ5HHdOLeV95c3YCuOJf",
|
|
7
7
|
"generatedAt": "2026-06-06T00:00:00.000Z",
|
|
8
8
|
"sourceVersions": {
|
|
9
9
|
"onboarding": {
|
|
10
10
|
"promptVersion": "2026-06-15.2",
|
|
11
|
-
"promptHash": "
|
|
11
|
+
"promptHash": "YyLcFG8-h779ReTOG7GOz4mjgutRtKZw"
|
|
12
12
|
},
|
|
13
13
|
"apiHandbook": {
|
|
14
14
|
"version": "1",
|
|
15
|
-
"contentHash": "
|
|
15
|
+
"contentHash": "edf117cbd2b09857"
|
|
16
16
|
},
|
|
17
|
-
"waveSkillCorpusHash": "
|
|
18
|
-
"sdkLookupCorpusHash": "
|
|
17
|
+
"waveSkillCorpusHash": "nhg-WfsotIdN1pRO41g97bff6RrKujrN",
|
|
18
|
+
"sdkLookupCorpusHash": "1DUg8-FDFbzXLOMm12TB3-S_y4c6Z74H"
|
|
19
19
|
},
|
|
20
20
|
"cacheContract": {
|
|
21
21
|
"llmContextCacheOnly": [
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"searchDocuments"
|
|
36
36
|
],
|
|
37
37
|
"rule": "Priority 1 always-on sections must stay in LLM chat/system context. The WaveEngine SDK local cache is public-safe retrieval data, never the only memory for core policy.",
|
|
38
|
-
"defaultRefreshCommand": "npx -y wave3d-agent-sdk@0.2.
|
|
38
|
+
"defaultRefreshCommand": "npx -y wave3d-agent-sdk@0.2.9 cache refresh",
|
|
39
39
|
"refreshRule": "When using WaveEngine SDK local cache, compare cacheKey, bundleHash, and sourceVersions before trusting local lookup. If any value differs, update the SDK package or reinstall its bundled SDK cache and rebuild local indexes. Do not fetch a remote SDK zip.",
|
|
40
40
|
"onlineFallback": "HTTP Gateway does not provide cloud API lookup. If local Node/npm or cache is refused/unavailable, use only concrete Studio operations and already-known code; repair the local WaveEngine SDK before unfamiliar API authoring."
|
|
41
41
|
},
|
|
42
42
|
"corpus": {
|
|
43
43
|
"skillCount": 67,
|
|
44
|
-
"apiEntryCount":
|
|
45
|
-
"apiMethodGroupCount":
|
|
44
|
+
"apiEntryCount": 4201,
|
|
45
|
+
"apiMethodGroupCount": 4480,
|
|
46
46
|
"apiInheritanceCount": 735
|
|
47
47
|
},
|
|
48
48
|
"files": [
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"mediaType": "application/json",
|
|
52
52
|
"role": "lookup-guide",
|
|
53
53
|
"bytes": 1310,
|
|
54
|
-
"hash": "
|
|
54
|
+
"hash": "PCkIsr75VpI_nQ4ufjVs-j13jCuh_YUL"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"path": "skills/wave-skills.jsonl",
|
|
@@ -78,15 +78,15 @@
|
|
|
78
78
|
"path": "api/entries.jsonl",
|
|
79
79
|
"mediaType": "application/jsonl",
|
|
80
80
|
"role": "api-corpus",
|
|
81
|
-
"bytes":
|
|
82
|
-
"hash": "
|
|
81
|
+
"bytes": 4169568,
|
|
82
|
+
"hash": "jyGCEHeyJe5VSmLHEBzGc-ehK02T4Im_"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"path": "api/method-groups.jsonl",
|
|
86
86
|
"mediaType": "application/jsonl",
|
|
87
87
|
"role": "api-corpus",
|
|
88
|
-
"bytes":
|
|
89
|
-
"hash": "
|
|
88
|
+
"bytes": 1400372,
|
|
89
|
+
"hash": "ddN0Bqv0ue3BIPuzp4fh3zBtTlUzSfjJ"
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"path": "api/inheritance.jsonl",
|
|
@@ -100,14 +100,14 @@
|
|
|
100
100
|
"mediaType": "application/json",
|
|
101
101
|
"role": "api-corpus",
|
|
102
102
|
"bytes": 219,
|
|
103
|
-
"hash": "
|
|
103
|
+
"hash": "63XcQbDuL3ZswbTi4kAzD_Jt04FXIONX"
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
"path": "search/search-documents.jsonl",
|
|
107
107
|
"mediaType": "application/jsonl",
|
|
108
108
|
"role": "search-index",
|
|
109
|
-
"bytes":
|
|
110
|
-
"hash": "
|
|
109
|
+
"bytes": 3917846,
|
|
110
|
+
"hash": "AnlOOjUCNs-BOUdLrYzW5ugcAEnTA7JF"
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
"path": "README.md",
|
|
Binary file
|
package/package.json
CHANGED