Spaces:
Running
Running
Commit
·
ec75a88
1
Parent(s):
eb1a39a
langgraph.js migration
Browse files- CLAUDE.md +1 -1
- bun.lock +60 -1
- layers/structure.md +2 -4
- package.json +4 -1
- src/lib/components/chat/ReasoningBlock.svelte +2 -5
- src/lib/server/agent-config.ts +0 -19
- src/lib/server/agent-runner.test.ts +0 -100
- src/lib/server/agent-runner.ts +0 -176
- src/lib/server/api.ts +38 -16
- src/lib/server/context.md +22 -18
- src/lib/server/hint.test.ts +0 -31
- src/lib/server/langgraph-agent.ts +333 -0
- src/lib/server/prompts.ts +0 -46
- src/lib/server/reasoning-extractor.test.ts +0 -114
- src/lib/server/reasoning-extractor.ts +0 -131
- src/lib/server/tools.ts +62 -0
- src/lib/stores/agent.ts +47 -1
- src/lib/stores/loading.ts +0 -3
- src/lib/tools/context.md +0 -37
- src/lib/tools/executor.ts +0 -36
- src/lib/tools/index.ts +0 -17
- src/lib/tools/parser.ts +0 -41
- src/lib/tools/read-console-output.ts +0 -47
- src/lib/tools/read-game-code.ts +0 -18
- src/lib/tools/registry.ts +0 -51
- src/lib/tools/tools-integration.test.ts +0 -110
- src/lib/tools/write-game-code.ts +0 -38
- vite.config.ts +1 -4
CLAUDE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# AI Context - Working Agreement
|
| 2 |
|
| 3 |
<project-description>
|
| 4 |
-
AI-assisted game development environment using the VibeGame engine. Iterative development with Svelte UI, Monaco editor, and
|
| 5 |
</project-description>
|
| 6 |
|
| 7 |
**Required**: Read [layers/structure.md](layers/structure.md) before proceeding with any task
|
|
|
|
| 1 |
# AI Context - Working Agreement
|
| 2 |
|
| 3 |
<project-description>
|
| 4 |
+
AI-assisted game development environment using the VibeGame engine. Iterative development with Svelte UI, Monaco editor, and LangGraph.js agent for AI-driven game modifications.
|
| 5 |
</project-description>
|
| 6 |
|
| 7 |
**Required**: Read [layers/structure.md](layers/structure.md) before proceeding with any task
|
bun.lock
CHANGED
|
@@ -6,6 +6,8 @@
|
|
| 6 |
"dependencies": {
|
| 7 |
"@huggingface/hub": "^2.6.3",
|
| 8 |
"@huggingface/inference": "^4.8.0",
|
|
|
|
|
|
|
| 9 |
"@types/marked": "^6.0.0",
|
| 10 |
"@types/node": "^24.3.3",
|
| 11 |
"gsap": "^3.13.0",
|
|
@@ -13,6 +15,7 @@
|
|
| 13 |
"monaco-editor": "^0.50.0",
|
| 14 |
"svelte-splitpanes": "^8.0.5",
|
| 15 |
"vibegame": "^0.1.1",
|
|
|
|
| 16 |
},
|
| 17 |
"devDependencies": {
|
| 18 |
"@eslint/js": "^9.33.0",
|
|
@@ -37,6 +40,8 @@
|
|
| 37 |
"packages": {
|
| 38 |
"@ampproject/remapping": ["@ampproject/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
| 39 |
|
|
|
|
|
|
|
| 40 |
"@dimforge/rapier3d-compat": ["@dimforge/[email protected]", "", {}, "sha512-DTXrOsn3ra9ZonB+VyqJc16xnRXWsHVa5FK230Z+R1PJ7q8oSRmWQ+AU6e+IJYBHxkM0a5QVDqd729DyeEhHPA=="],
|
| 41 |
|
| 42 |
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
|
@@ -127,6 +132,14 @@
|
|
| 127 |
|
| 128 |
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
"@nodelib/fs.scandir": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
| 131 |
|
| 132 |
"@nodelib/fs.stat": ["@nodelib/[email protected]", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
|
@@ -199,6 +212,10 @@
|
|
| 199 |
|
| 200 |
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
| 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
"@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
| 203 |
|
| 204 |
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="],
|
|
@@ -229,7 +246,7 @@
|
|
| 229 |
|
| 230 |
"ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 231 |
|
| 232 |
-
"ansi-styles": ["ansi-styles@
|
| 233 |
|
| 234 |
"anymatch": ["[email protected]", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
| 235 |
|
|
@@ -257,6 +274,8 @@
|
|
| 257 |
|
| 258 |
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
| 259 |
|
|
|
|
|
|
|
| 260 |
"binary-extensions": ["[email protected]", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
| 261 |
|
| 262 |
"bitecs": ["[email protected]", "", {}, "sha512-wAylY4pNfX8IeIH5phtwt1lUNtHKrkoSNrArI7Ris2Y4nEQWFIVvXdgAuqprEg9bq8Wolmlj0gVfeG6MFmtI2Q=="],
|
|
@@ -277,6 +296,8 @@
|
|
| 277 |
|
| 278 |
"callsites": ["[email protected]", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
| 279 |
|
|
|
|
|
|
|
| 280 |
"chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
| 281 |
|
| 282 |
"chokidar": ["[email protected]", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
|
@@ -291,6 +312,8 @@
|
|
| 291 |
|
| 292 |
"concat-map": ["[email protected]", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
| 293 |
|
|
|
|
|
|
|
| 294 |
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
| 295 |
|
| 296 |
"css-tree": ["[email protected]", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
|
|
@@ -305,6 +328,8 @@
|
|
| 305 |
|
| 306 |
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
| 307 |
|
|
|
|
|
|
|
| 308 |
"deep-is": ["[email protected]", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
| 309 |
|
| 310 |
"deepmerge": ["[email protected]", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
|
@@ -373,6 +398,8 @@
|
|
| 373 |
|
| 374 |
"esutils": ["[email protected]", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
| 375 |
|
|
|
|
|
|
|
| 376 |
"fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
| 377 |
|
| 378 |
"fast-diff": ["[email protected]", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
|
|
@@ -515,6 +542,8 @@
|
|
| 515 |
|
| 516 |
"isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
| 517 |
|
|
|
|
|
|
|
| 518 |
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
| 519 |
|
| 520 |
"json-buffer": ["[email protected]", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
|
@@ -529,6 +558,8 @@
|
|
| 529 |
|
| 530 |
"kleur": ["[email protected]", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
| 531 |
|
|
|
|
|
|
|
| 532 |
"levn": ["[email protected]", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 533 |
|
| 534 |
"locate-character": ["[email protected]", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
|
@@ -563,6 +594,8 @@
|
|
| 563 |
|
| 564 |
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
| 565 |
|
|
|
|
|
|
|
| 566 |
"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
| 567 |
|
| 568 |
"natural-compare": ["[email protected]", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
|
@@ -587,10 +620,18 @@
|
|
| 587 |
|
| 588 |
"own-keys": ["[email protected]", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
| 589 |
|
|
|
|
|
|
|
| 590 |
"p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
| 591 |
|
| 592 |
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
| 593 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
"parent-module": ["[email protected]", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
| 595 |
|
| 596 |
"path-exists": ["[email protected]", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
|
@@ -631,6 +672,8 @@
|
|
| 631 |
|
| 632 |
"resolve-from": ["[email protected]", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
| 633 |
|
|
|
|
|
|
|
| 634 |
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
| 635 |
|
| 636 |
"rimraf": ["[email protected]", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
|
|
@@ -669,6 +712,8 @@
|
|
| 669 |
|
| 670 |
"side-channel-weakmap": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
| 671 |
|
|
|
|
|
|
|
| 672 |
"sorcery": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", "buffer-crc32": "^1.0.0", "minimist": "^1.2.0", "sander": "^0.5.0" }, "bin": { "sorcery": "bin/sorcery" } }, "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ=="],
|
| 673 |
|
| 674 |
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
@@ -733,6 +778,8 @@
|
|
| 733 |
|
| 734 |
"uri-js": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
| 735 |
|
|
|
|
|
|
|
| 736 |
"vibegame": ["[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "^0.18.2", "gsap": "^3.13.0", "zod": "^4.1.5" }, "peerDependencies": { "bitecs": ">=0.3.40", "three": ">=0.170.0" } }, "sha512-HjWMlO4qUus9ChEBsD+abS9UGle/qiDZWukIqkve811N2oliKjWE7WYCDhqaDhWS18FaaYaY1a/Vc8yF0HaS1Q=="],
|
| 737 |
|
| 738 |
"vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="],
|
|
@@ -759,14 +806,24 @@
|
|
| 759 |
|
| 760 |
"zod": ["[email protected]", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
| 761 |
|
|
|
|
|
|
|
| 762 |
"@eslint-community/eslint-utils/eslint-visitor-keys": ["[email protected]", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
| 763 |
|
| 764 |
"@eslint/eslintrc/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 765 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 766 |
"@typescript-eslint/typescript-estree/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
| 767 |
|
| 768 |
"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
| 769 |
|
|
|
|
|
|
|
| 770 |
"chokidar/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
| 771 |
|
| 772 |
"eslint/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
|
@@ -779,6 +836,8 @@
|
|
| 779 |
|
| 780 |
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
| 781 |
|
|
|
|
|
|
|
| 782 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
| 783 |
}
|
| 784 |
}
|
|
|
|
| 6 |
"dependencies": {
|
| 7 |
"@huggingface/hub": "^2.6.3",
|
| 8 |
"@huggingface/inference": "^4.8.0",
|
| 9 |
+
"@langchain/core": "^0.3.75",
|
| 10 |
+
"@langchain/langgraph": "^0.4.9",
|
| 11 |
"@types/marked": "^6.0.0",
|
| 12 |
"@types/node": "^24.3.3",
|
| 13 |
"gsap": "^3.13.0",
|
|
|
|
| 15 |
"monaco-editor": "^0.50.0",
|
| 16 |
"svelte-splitpanes": "^8.0.5",
|
| 17 |
"vibegame": "^0.1.1",
|
| 18 |
+
"zod": "^4.1.8",
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"@eslint/js": "^9.33.0",
|
|
|
|
| 40 |
"packages": {
|
| 41 |
"@ampproject/remapping": ["@ampproject/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
| 42 |
|
| 43 |
+
"@cfworker/json-schema": ["@cfworker/[email protected]", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
| 44 |
+
|
| 45 |
"@dimforge/rapier3d-compat": ["@dimforge/[email protected]", "", {}, "sha512-DTXrOsn3ra9ZonB+VyqJc16xnRXWsHVa5FK230Z+R1PJ7q8oSRmWQ+AU6e+IJYBHxkM0a5QVDqd729DyeEhHPA=="],
|
| 46 |
|
| 47 |
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
|
|
|
| 132 |
|
| 133 |
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 134 |
|
| 135 |
+
"@langchain/core": ["@langchain/[email protected]", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-kTyBS0DTeD0JYa9YH5lg6UdDbHmvplk3t9PCjP5jDQZCK5kPe2aDFToqdiCaLzZg8RzzM+clXLVyJtPTE8bZ2Q=="],
|
| 136 |
+
|
| 137 |
+
"@langchain/langgraph": ["@langchain/[email protected]", "", { "dependencies": { "@langchain/langgraph-checkpoint": "^0.1.1", "@langchain/langgraph-sdk": "~0.1.0", "uuid": "^10.0.0", "zod": "^3.25.32" }, "peerDependencies": { "@langchain/core": ">=0.3.58 < 0.4.0", "zod-to-json-schema": "^3.x" }, "optionalPeers": ["zod-to-json-schema"] }, "sha512-+rcdTGi4Ium4X/VtIX3Zw4RhxEkYWpwUyz806V6rffjHOAMamg6/WZDxpJbrP33RV/wJG1GH12Z29oX3Pqq3Aw=="],
|
| 138 |
+
|
| 139 |
+
"@langchain/langgraph-checkpoint": ["@langchain/[email protected]", "", { "dependencies": { "uuid": "^10.0.0" }, "peerDependencies": { "@langchain/core": ">=0.2.31 <0.4.0 || ^1.0.0-alpha" } }, "sha512-h2bP0RUikQZu0Um1ZUPErQLXyhzroJqKRbRcxYRTAh49oNlsfeq4A3K4YEDRbGGuyPZI/Jiqwhks1wZwY73AZw=="],
|
| 140 |
+
|
| 141 |
+
"@langchain/langgraph-sdk": ["@langchain/[email protected]", "", { "dependencies": { "@types/json-schema": "^7.0.15", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^9.0.0" }, "peerDependencies": { "@langchain/core": ">=0.2.31 <0.4.0", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "optionalPeers": ["@langchain/core", "react", "react-dom"] }, "sha512-muZJ+D9A7gL0/QSvsjL6LMwgvN58euO2+MwSXq5Nb40ZgnjVWWtUYmjdVLno4IpgAXWXOQi34i2XtLbTwbyXvg=="],
|
| 142 |
+
|
| 143 |
"@nodelib/fs.scandir": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
| 144 |
|
| 145 |
"@nodelib/fs.stat": ["@nodelib/[email protected]", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
|
|
|
| 212 |
|
| 213 |
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
| 214 |
|
| 215 |
+
"@types/retry": ["@types/[email protected]", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
| 216 |
+
|
| 217 |
+
"@types/uuid": ["@types/[email protected]", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="],
|
| 218 |
+
|
| 219 |
"@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
| 220 |
|
| 221 |
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="],
|
|
|
|
| 246 |
|
| 247 |
"ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
| 248 |
|
| 249 |
+
"ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
| 250 |
|
| 251 |
"anymatch": ["[email protected]", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
| 252 |
|
|
|
|
| 274 |
|
| 275 |
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
| 276 |
|
| 277 |
+
"base64-js": ["[email protected]", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
| 278 |
+
|
| 279 |
"binary-extensions": ["[email protected]", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
| 280 |
|
| 281 |
"bitecs": ["[email protected]", "", {}, "sha512-wAylY4pNfX8IeIH5phtwt1lUNtHKrkoSNrArI7Ris2Y4nEQWFIVvXdgAuqprEg9bq8Wolmlj0gVfeG6MFmtI2Q=="],
|
|
|
|
| 296 |
|
| 297 |
"callsites": ["[email protected]", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
| 298 |
|
| 299 |
+
"camelcase": ["[email protected]", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
|
| 300 |
+
|
| 301 |
"chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
| 302 |
|
| 303 |
"chokidar": ["[email protected]", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
|
|
|
| 312 |
|
| 313 |
"concat-map": ["[email protected]", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
| 314 |
|
| 315 |
+
"console-table-printer": ["[email protected]", "", { "dependencies": { "simple-wcswidth": "^1.0.1" } }, "sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw=="],
|
| 316 |
+
|
| 317 |
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
| 318 |
|
| 319 |
"css-tree": ["[email protected]", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
|
|
|
|
| 328 |
|
| 329 |
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
| 330 |
|
| 331 |
+
"decamelize": ["[email protected]", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
|
| 332 |
+
|
| 333 |
"deep-is": ["[email protected]", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
| 334 |
|
| 335 |
"deepmerge": ["[email protected]", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
|
|
|
| 398 |
|
| 399 |
"esutils": ["[email protected]", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
| 400 |
|
| 401 |
+
"eventemitter3": ["[email protected]", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
|
| 402 |
+
|
| 403 |
"fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
| 404 |
|
| 405 |
"fast-diff": ["[email protected]", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
|
|
|
|
| 542 |
|
| 543 |
"isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
| 544 |
|
| 545 |
+
"js-tiktoken": ["[email protected]", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
|
| 546 |
+
|
| 547 |
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
| 548 |
|
| 549 |
"json-buffer": ["[email protected]", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
|
|
|
| 558 |
|
| 559 |
"kleur": ["[email protected]", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
| 560 |
|
| 561 |
+
"langsmith": ["[email protected]", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-Yx4fnyTjrPKtqH2ax9nb6Ua6XAMYkafKkOLMcTzbJ/w+Yu3V6JjE+vabl/Q600oC53bo3hg6ourI4/KdZVXr4A=="],
|
| 562 |
+
|
| 563 |
"levn": ["[email protected]", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 564 |
|
| 565 |
"locate-character": ["[email protected]", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
|
|
|
| 594 |
|
| 595 |
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
| 596 |
|
| 597 |
+
"mustache": ["[email protected]", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
| 598 |
+
|
| 599 |
"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
| 600 |
|
| 601 |
"natural-compare": ["[email protected]", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
|
|
|
| 620 |
|
| 621 |
"own-keys": ["[email protected]", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
| 622 |
|
| 623 |
+
"p-finally": ["[email protected]", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
|
| 624 |
+
|
| 625 |
"p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
| 626 |
|
| 627 |
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
| 628 |
|
| 629 |
+
"p-queue": ["[email protected]", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
|
| 630 |
+
|
| 631 |
+
"p-retry": ["[email protected]", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
|
| 632 |
+
|
| 633 |
+
"p-timeout": ["[email protected]", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
|
| 634 |
+
|
| 635 |
"parent-module": ["[email protected]", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
| 636 |
|
| 637 |
"path-exists": ["[email protected]", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
|
|
|
| 672 |
|
| 673 |
"resolve-from": ["[email protected]", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
| 674 |
|
| 675 |
+
"retry": ["[email protected]", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
| 676 |
+
|
| 677 |
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
| 678 |
|
| 679 |
"rimraf": ["[email protected]", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
|
|
|
|
| 712 |
|
| 713 |
"side-channel-weakmap": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
| 714 |
|
| 715 |
+
"simple-wcswidth": ["[email protected]", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="],
|
| 716 |
+
|
| 717 |
"sorcery": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", "buffer-crc32": "^1.0.0", "minimist": "^1.2.0", "sander": "^0.5.0" }, "bin": { "sorcery": "bin/sorcery" } }, "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ=="],
|
| 718 |
|
| 719 |
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
|
|
| 778 |
|
| 779 |
"uri-js": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
| 780 |
|
| 781 |
+
"uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
| 782 |
+
|
| 783 |
"vibegame": ["[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "^0.18.2", "gsap": "^3.13.0", "zod": "^4.1.5" }, "peerDependencies": { "bitecs": ">=0.3.40", "three": ">=0.170.0" } }, "sha512-HjWMlO4qUus9ChEBsD+abS9UGle/qiDZWukIqkve811N2oliKjWE7WYCDhqaDhWS18FaaYaY1a/Vc8yF0HaS1Q=="],
|
| 784 |
|
| 785 |
"vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="],
|
|
|
|
| 806 |
|
| 807 |
"zod": ["[email protected]", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
| 808 |
|
| 809 |
+
"zod-to-json-schema": ["[email protected]", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="],
|
| 810 |
+
|
| 811 |
"@eslint-community/eslint-utils/eslint-visitor-keys": ["[email protected]", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
| 812 |
|
| 813 |
"@eslint/eslintrc/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 814 |
|
| 815 |
+
"@langchain/core/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 816 |
+
|
| 817 |
+
"@langchain/langgraph/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
| 818 |
+
|
| 819 |
+
"@langchain/langgraph-sdk/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
| 820 |
+
|
| 821 |
"@typescript-eslint/typescript-estree/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
| 822 |
|
| 823 |
"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
| 824 |
|
| 825 |
+
"chalk/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
| 826 |
+
|
| 827 |
"chokidar/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
| 828 |
|
| 829 |
"eslint/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
|
|
|
| 836 |
|
| 837 |
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
| 838 |
|
| 839 |
+
"langsmith/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
| 840 |
+
|
| 841 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
| 842 |
}
|
| 843 |
}
|
layers/structure.md
CHANGED
|
@@ -9,7 +9,7 @@ AI-assisted iterative game development environment with real-time feedback
|
|
| 9 |
- UI Framework: Svelte
|
| 10 |
- Game Engine: VibeGame (3D physics, ECS)
|
| 11 |
- Code Editor: Monaco Editor
|
| 12 |
-
- AI Agent: Hugging Face Inference
|
| 13 |
- WebSocket: ws (for real-time communication)
|
| 14 |
- Build: Vite
|
| 15 |
- Animation: GSAP (bundled with VibeGame)
|
|
@@ -64,9 +64,8 @@ vibegame/
|
|
| 64 |
Three-layer architecture: UI (Svelte) → Agent (Hugging Face) → Game (VibeGame)
|
| 65 |
|
| 66 |
- **UI Layer**: Svelte components for chat, code editing, console display
|
| 67 |
-
- **Agent Layer**:
|
| 68 |
- **Game Layer**: VibeGame ECS with declarative XML scene definition
|
| 69 |
-
- **Feedback Loop**: Console output → Agent parsing → Self-correction (planned)
|
| 70 |
|
| 71 |
## Entry points
|
| 72 |
|
|
@@ -99,7 +98,6 @@ TypeScript/Web standards with Svelte and ECS conventions
|
|
| 99 |
- State management → `src/lib/stores/[store].ts`
|
| 100 |
- Business logic → `src/lib/services/[service].ts`
|
| 101 |
- Server/Agent logic → `src/lib/server/[module].ts`
|
| 102 |
-
- MCP tools → `src/lib/tools/[tool].ts`
|
| 103 |
- Configuration → `src/lib/config/[config].ts`
|
| 104 |
- Game entities → XML in Monaco editor
|
| 105 |
- Custom ECS components → `src/lib/game/components/[Component].ts`
|
|
|
|
| 9 |
- UI Framework: Svelte
|
| 10 |
- Game Engine: VibeGame (3D physics, ECS)
|
| 11 |
- Code Editor: Monaco Editor
|
| 12 |
+
- AI Agent: LangGraph.js with Hugging Face Inference
|
| 13 |
- WebSocket: ws (for real-time communication)
|
| 14 |
- Build: Vite
|
| 15 |
- Animation: GSAP (bundled with VibeGame)
|
|
|
|
| 64 |
Three-layer architecture: UI (Svelte) → Agent (Hugging Face) → Game (VibeGame)
|
| 65 |
|
| 66 |
- **UI Layer**: Svelte components for chat, code editing, console display
|
| 67 |
+
- **Agent Layer**: LangGraph.js agent via WebSocket with Read/Write tools
|
| 68 |
- **Game Layer**: VibeGame ECS with declarative XML scene definition
|
|
|
|
| 69 |
|
| 70 |
## Entry points
|
| 71 |
|
|
|
|
| 98 |
- State management → `src/lib/stores/[store].ts`
|
| 99 |
- Business logic → `src/lib/services/[service].ts`
|
| 100 |
- Server/Agent logic → `src/lib/server/[module].ts`
|
|
|
|
| 101 |
- Configuration → `src/lib/config/[config].ts`
|
| 102 |
- Game entities → XML in Monaco editor
|
| 103 |
- Custom ECS components → `src/lib/game/components/[Component].ts`
|
package.json
CHANGED
|
@@ -38,12 +38,15 @@
|
|
| 38 |
"dependencies": {
|
| 39 |
"@huggingface/hub": "^2.6.3",
|
| 40 |
"@huggingface/inference": "^4.8.0",
|
|
|
|
|
|
|
| 41 |
"@types/marked": "^6.0.0",
|
| 42 |
"@types/node": "^24.3.3",
|
| 43 |
"gsap": "^3.13.0",
|
| 44 |
"marked": "^16.2.1",
|
| 45 |
"monaco-editor": "^0.50.0",
|
| 46 |
"svelte-splitpanes": "^8.0.5",
|
| 47 |
-
"vibegame": "^0.1.1"
|
|
|
|
| 48 |
}
|
| 49 |
}
|
|
|
|
| 38 |
"dependencies": {
|
| 39 |
"@huggingface/hub": "^2.6.3",
|
| 40 |
"@huggingface/inference": "^4.8.0",
|
| 41 |
+
"@langchain/core": "^0.3.75",
|
| 42 |
+
"@langchain/langgraph": "^0.4.9",
|
| 43 |
"@types/marked": "^6.0.0",
|
| 44 |
"@types/node": "^24.3.3",
|
| 45 |
"gsap": "^3.13.0",
|
| 46 |
"marked": "^16.2.1",
|
| 47 |
"monaco-editor": "^0.50.0",
|
| 48 |
"svelte-splitpanes": "^8.0.5",
|
| 49 |
+
"vibegame": "^0.1.1",
|
| 50 |
+
"zod": "^4.1.8"
|
| 51 |
}
|
| 52 |
}
|
src/lib/components/chat/ReasoningBlock.svelte
CHANGED
|
@@ -14,14 +14,12 @@
|
|
| 14 |
if (!iconElement || !contentElement) return;
|
| 15 |
|
| 16 |
if (isExpanded) {
|
| 17 |
-
// Expand - quick but smooth
|
| 18 |
gsap.to(iconElement, {
|
| 19 |
rotation: 180,
|
| 20 |
duration: 0.15,
|
| 21 |
ease: "power2.out"
|
| 22 |
});
|
| 23 |
|
| 24 |
-
// Show content
|
| 25 |
gsap.set(contentElement, {
|
| 26 |
display: 'block'
|
| 27 |
});
|
|
@@ -32,14 +30,13 @@
|
|
| 32 |
y: -10
|
| 33 |
}, {
|
| 34 |
opacity: 1,
|
| 35 |
-
maxHeight: 500,
|
| 36 |
y: 0,
|
| 37 |
duration: 0.2,
|
| 38 |
ease: "power2.out"
|
| 39 |
});
|
| 40 |
|
| 41 |
} else {
|
| 42 |
-
// Collapse - nearly instant
|
| 43 |
gsap.to(iconElement, {
|
| 44 |
rotation: 0,
|
| 45 |
duration: 0.1,
|
|
@@ -234,4 +231,4 @@
|
|
| 234 |
.reasoning-text::-webkit-scrollbar-thumb:hover {
|
| 235 |
background: rgba(120, 120, 130, 0.5);
|
| 236 |
}
|
| 237 |
-
</style>
|
|
|
|
| 14 |
if (!iconElement || !contentElement) return;
|
| 15 |
|
| 16 |
if (isExpanded) {
|
|
|
|
| 17 |
gsap.to(iconElement, {
|
| 18 |
rotation: 180,
|
| 19 |
duration: 0.15,
|
| 20 |
ease: "power2.out"
|
| 21 |
});
|
| 22 |
|
|
|
|
| 23 |
gsap.set(contentElement, {
|
| 24 |
display: 'block'
|
| 25 |
});
|
|
|
|
| 30 |
y: -10
|
| 31 |
}, {
|
| 32 |
opacity: 1,
|
| 33 |
+
maxHeight: 500,
|
| 34 |
y: 0,
|
| 35 |
duration: 0.2,
|
| 36 |
ease: "power2.out"
|
| 37 |
});
|
| 38 |
|
| 39 |
} else {
|
|
|
|
| 40 |
gsap.to(iconElement, {
|
| 41 |
rotation: 0,
|
| 42 |
duration: 0.1,
|
|
|
|
| 231 |
.reasoning-text::-webkit-scrollbar-thumb:hover {
|
| 232 |
background: rgba(120, 120, 130, 0.5);
|
| 233 |
}
|
| 234 |
+
</style>
|
src/lib/server/agent-config.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
| 1 |
-
export interface AgentConfig {
|
| 2 |
-
model?: string;
|
| 3 |
-
maxIterations?: number;
|
| 4 |
-
temperature?: number;
|
| 5 |
-
maxTokens?: number;
|
| 6 |
-
}
|
| 7 |
-
|
| 8 |
-
export const DEFAULT_CONFIG: Required<AgentConfig> = {
|
| 9 |
-
model: "Qwen/Qwen3-Next-80B-A3B-Instruct",
|
| 10 |
-
maxIterations: 10,
|
| 11 |
-
temperature: 0.5,
|
| 12 |
-
maxTokens: 1200,
|
| 13 |
-
};
|
| 14 |
-
|
| 15 |
-
export const mergeConfig = (
|
| 16 |
-
config: AgentConfig = {},
|
| 17 |
-
): Required<AgentConfig> => {
|
| 18 |
-
return { ...DEFAULT_CONFIG, ...config };
|
| 19 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/agent-runner.test.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
| 1 |
-
import { describe, expect, test, beforeAll } from "bun:test";
|
| 2 |
-
import { AgentRunner } from "./agent-runner";
|
| 3 |
-
import "../tools"; // Import to register tools
|
| 4 |
-
|
| 5 |
-
describe("AgentRunner Tool Execution", () => {
|
| 6 |
-
beforeAll(() => {
|
| 7 |
-
// Create runner instance for test context
|
| 8 |
-
new AgentRunner({
|
| 9 |
-
model: "test-model",
|
| 10 |
-
temperature: 0.7,
|
| 11 |
-
maxTokens: 1000,
|
| 12 |
-
});
|
| 13 |
-
});
|
| 14 |
-
|
| 15 |
-
describe("Tool Detection", () => {
|
| 16 |
-
test("should detect tool calls in response", async () => {
|
| 17 |
-
const { parseToolCalls } = await import("../tools");
|
| 18 |
-
|
| 19 |
-
// Test various agent responses
|
| 20 |
-
const responses = [
|
| 21 |
-
{
|
| 22 |
-
text: "I'll read the game code for you.\n\n[TOOL: read_game_code]\n\nAnalyzing...",
|
| 23 |
-
expected: ["read_game_code"],
|
| 24 |
-
},
|
| 25 |
-
{
|
| 26 |
-
text: "Let me check that. [TOOL: read_game_code] Now I can see the code.",
|
| 27 |
-
expected: ["read_game_code"],
|
| 28 |
-
},
|
| 29 |
-
{
|
| 30 |
-
text: "No tools needed for this response.",
|
| 31 |
-
expected: [],
|
| 32 |
-
},
|
| 33 |
-
];
|
| 34 |
-
|
| 35 |
-
for (const { text, expected } of responses) {
|
| 36 |
-
const calls = parseToolCalls(text);
|
| 37 |
-
expect(calls.map((c) => c.tool)).toEqual(expected);
|
| 38 |
-
}
|
| 39 |
-
});
|
| 40 |
-
|
| 41 |
-
test("should format tool results correctly", async () => {
|
| 42 |
-
const { formatToolResult } = await import("../tools");
|
| 43 |
-
|
| 44 |
-
const successResult = {
|
| 45 |
-
success: true,
|
| 46 |
-
data: { content: "<world>test</world>", language: "html" },
|
| 47 |
-
};
|
| 48 |
-
|
| 49 |
-
const formatted = formatToolResult("read_game_code", successResult);
|
| 50 |
-
expect(formatted).toContain("[TOOL_RESULT: read_game_code]");
|
| 51 |
-
expect(formatted).toContain("<world>test</world>");
|
| 52 |
-
expect(formatted).toContain("[/TOOL_RESULT]");
|
| 53 |
-
});
|
| 54 |
-
});
|
| 55 |
-
|
| 56 |
-
describe("System Prompt", () => {
|
| 57 |
-
test("should include tool instructions", async () => {
|
| 58 |
-
const { toolRegistry } = await import("../tools");
|
| 59 |
-
const descriptions = toolRegistry.getToolDescriptions();
|
| 60 |
-
|
| 61 |
-
// System prompt should mention the tool
|
| 62 |
-
expect(descriptions).toContain("read_game_code");
|
| 63 |
-
expect(descriptions).toContain(
|
| 64 |
-
"Get the current game code from the editor",
|
| 65 |
-
);
|
| 66 |
-
});
|
| 67 |
-
});
|
| 68 |
-
});
|
| 69 |
-
|
| 70 |
-
// Integration test that would require mocking HuggingFace API
|
| 71 |
-
describe("Full Tool Flow (Mock)", () => {
|
| 72 |
-
test("should execute tool when detected in response", async () => {
|
| 73 |
-
// This test demonstrates the expected flow
|
| 74 |
-
// In production, this would make actual API calls
|
| 75 |
-
|
| 76 |
-
const mockResponse = "I'll check the game code.\n\n[TOOL: read_game_code]";
|
| 77 |
-
const { parseToolCalls, toolRegistry } = await import("../tools");
|
| 78 |
-
|
| 79 |
-
// 1. Parse tool calls from response
|
| 80 |
-
const calls = parseToolCalls(mockResponse);
|
| 81 |
-
expect(calls).toHaveLength(1);
|
| 82 |
-
expect(calls[0].tool).toBe("read_game_code");
|
| 83 |
-
|
| 84 |
-
// 2. Execute the tool
|
| 85 |
-
const result = await toolRegistry.execute(
|
| 86 |
-
calls[0].tool,
|
| 87 |
-
calls[0].parameters,
|
| 88 |
-
);
|
| 89 |
-
expect(result.success).toBe(true);
|
| 90 |
-
expect(result.data).toBeDefined();
|
| 91 |
-
|
| 92 |
-
// 3. Format result for context
|
| 93 |
-
const { formatToolResult } = await import("../tools");
|
| 94 |
-
const formatted = formatToolResult(calls[0].tool, result);
|
| 95 |
-
expect(formatted).toContain("[TOOL_RESULT:");
|
| 96 |
-
|
| 97 |
-
// 4. In the real flow, this formatted result would be added to message history
|
| 98 |
-
// and a follow-up API call would be made to process it
|
| 99 |
-
});
|
| 100 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/agent-runner.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
| 1 |
-
import { HfInference } from "@huggingface/inference";
|
| 2 |
-
import { toolRegistry, toolExecutor } from "../tools";
|
| 3 |
-
import { documentationService } from "./documentation";
|
| 4 |
-
import {
|
| 5 |
-
buildSystemPrompt,
|
| 6 |
-
buildToolFollowUpPrompt,
|
| 7 |
-
formatDocumentation,
|
| 8 |
-
} from "./prompts";
|
| 9 |
-
import { mergeConfig, type AgentConfig } from "./agent-config";
|
| 10 |
-
import { UniversalReasoningExtractor } from "./reasoning-extractor";
|
| 11 |
-
|
| 12 |
-
export interface AgentMessage {
|
| 13 |
-
id: string;
|
| 14 |
-
role: "user" | "assistant" | "system";
|
| 15 |
-
content: string;
|
| 16 |
-
timestamp: number;
|
| 17 |
-
reasoning?: string;
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
export class AgentRunner {
|
| 21 |
-
private client: HfInference | null = null;
|
| 22 |
-
private config: Required<AgentConfig>;
|
| 23 |
-
private messageHistory: AgentMessage[] = [];
|
| 24 |
-
private reasoningExtractor = new UniversalReasoningExtractor();
|
| 25 |
-
|
| 26 |
-
constructor(config: AgentConfig = {}) {
|
| 27 |
-
this.config = mergeConfig(config);
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
async initialize(hfToken?: string) {
|
| 31 |
-
if (!hfToken) {
|
| 32 |
-
throw new Error(
|
| 33 |
-
"Hugging Face authentication required. Please sign in to continue.",
|
| 34 |
-
);
|
| 35 |
-
}
|
| 36 |
-
this.client = new HfInference(hfToken);
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
async processMessage(
|
| 40 |
-
message: string,
|
| 41 |
-
onStream?: (chunk: string, reasoning?: string) => void,
|
| 42 |
-
): Promise<string> {
|
| 43 |
-
if (!this.client) {
|
| 44 |
-
throw new Error("AgentRunner not initialized. Call initialize() first.");
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
this.addUserMessage(message);
|
| 48 |
-
|
| 49 |
-
const documentation = await this.loadDocumentation();
|
| 50 |
-
const systemPrompt = this.buildPrompt(documentation);
|
| 51 |
-
|
| 52 |
-
this.reasoningExtractor.reset();
|
| 53 |
-
const response = await this.streamCompletion(systemPrompt, onStream);
|
| 54 |
-
|
| 55 |
-
const extracted = this.reasoningExtractor.extract(response);
|
| 56 |
-
this.addAssistantMessage(extracted.content, extracted.reasoning);
|
| 57 |
-
|
| 58 |
-
const toolResults = await this.executeToolsIfNeeded(extracted.content);
|
| 59 |
-
|
| 60 |
-
if (toolResults.length > 0) {
|
| 61 |
-
this.addToolResults(toolResults);
|
| 62 |
-
return await this.handleToolFollowUp(documentation, onStream);
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
return extracted.content;
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
clearHistory() {
|
| 69 |
-
this.messageHistory = [];
|
| 70 |
-
}
|
| 71 |
-
|
| 72 |
-
getHistory(): AgentMessage[] {
|
| 73 |
-
return [...this.messageHistory];
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
private async loadDocumentation(): Promise<string> {
|
| 77 |
-
const fullDocs = await documentationService.load();
|
| 78 |
-
return formatDocumentation(fullDocs || "");
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
private buildPrompt(documentation: string): string {
|
| 82 |
-
const tools = toolRegistry.getToolDescriptions();
|
| 83 |
-
return buildSystemPrompt(tools, documentation);
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
private async streamCompletion(
|
| 87 |
-
systemPrompt: string,
|
| 88 |
-
onStream?: (chunk: string, reasoning?: string) => void,
|
| 89 |
-
): Promise<string> {
|
| 90 |
-
const messages = this.buildMessages(systemPrompt);
|
| 91 |
-
|
| 92 |
-
const stream = await this.client!.chatCompletionStream({
|
| 93 |
-
model: this.config.model,
|
| 94 |
-
messages: messages as Array<{ role: string; content: string }>,
|
| 95 |
-
temperature: this.config.temperature,
|
| 96 |
-
max_tokens: this.config.maxTokens,
|
| 97 |
-
});
|
| 98 |
-
|
| 99 |
-
let fullResponse = "";
|
| 100 |
-
for await (const chunk of stream) {
|
| 101 |
-
const delta = chunk.choices?.[0]?.delta?.content;
|
| 102 |
-
if (delta) {
|
| 103 |
-
fullResponse += delta;
|
| 104 |
-
|
| 105 |
-
const result = this.reasoningExtractor.stream(delta);
|
| 106 |
-
if (result.partial) {
|
| 107 |
-
onStream?.(result.partial, result.reasoning);
|
| 108 |
-
} else if (result.reasoning) {
|
| 109 |
-
onStream?.("", result.reasoning);
|
| 110 |
-
}
|
| 111 |
-
}
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
-
return fullResponse;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
private buildMessages(systemPrompt: string) {
|
| 118 |
-
return [
|
| 119 |
-
{ role: "system", content: systemPrompt },
|
| 120 |
-
...this.messageHistory.map((msg) => ({
|
| 121 |
-
role: msg.role,
|
| 122 |
-
content: msg.content,
|
| 123 |
-
})),
|
| 124 |
-
];
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
private async executeToolsIfNeeded(response: string) {
|
| 128 |
-
return toolExecutor.executeFromResponse(response);
|
| 129 |
-
}
|
| 130 |
-
|
| 131 |
-
private addToolResults(results: { formatted: string }[]) {
|
| 132 |
-
for (const result of results) {
|
| 133 |
-
this.messageHistory.push({
|
| 134 |
-
id: this.generateId(),
|
| 135 |
-
role: "system",
|
| 136 |
-
content: result.formatted,
|
| 137 |
-
timestamp: Date.now(),
|
| 138 |
-
});
|
| 139 |
-
}
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
private async handleToolFollowUp(
|
| 143 |
-
documentation: string,
|
| 144 |
-
onStream?: (chunk: string, reasoning?: string) => void,
|
| 145 |
-
): Promise<string> {
|
| 146 |
-
const followUpPrompt = buildToolFollowUpPrompt(documentation);
|
| 147 |
-
this.reasoningExtractor.reset();
|
| 148 |
-
const response = await this.streamCompletion(followUpPrompt, onStream);
|
| 149 |
-
const extracted = this.reasoningExtractor.extract(response);
|
| 150 |
-
this.addAssistantMessage(extracted.content, extracted.reasoning);
|
| 151 |
-
return extracted.content;
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
private addUserMessage(content: string) {
|
| 155 |
-
this.messageHistory.push({
|
| 156 |
-
id: this.generateId(),
|
| 157 |
-
role: "user",
|
| 158 |
-
content,
|
| 159 |
-
timestamp: Date.now(),
|
| 160 |
-
});
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
private addAssistantMessage(content: string, reasoning?: string) {
|
| 164 |
-
this.messageHistory.push({
|
| 165 |
-
id: this.generateId(),
|
| 166 |
-
role: "assistant",
|
| 167 |
-
content,
|
| 168 |
-
timestamp: Date.now(),
|
| 169 |
-
reasoning,
|
| 170 |
-
});
|
| 171 |
-
}
|
| 172 |
-
|
| 173 |
-
private generateId(): string {
|
| 174 |
-
return `msg-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
| 175 |
-
}
|
| 176 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/api.ts
CHANGED
|
@@ -1,26 +1,40 @@
|
|
| 1 |
import type { IncomingMessage } from "http";
|
| 2 |
import type { WebSocket } from "ws";
|
| 3 |
-
import
|
|
|
|
|
|
|
| 4 |
|
| 5 |
export interface WebSocketMessage {
|
| 6 |
-
type:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
payload: {
|
| 8 |
content?: string;
|
| 9 |
role?: string;
|
| 10 |
chunk?: string;
|
| 11 |
-
reasoning?: string;
|
| 12 |
error?: string;
|
| 13 |
processing?: boolean;
|
| 14 |
connected?: boolean;
|
| 15 |
message?: string;
|
| 16 |
token?: string;
|
|
|
|
|
|
|
|
|
|
| 17 |
};
|
| 18 |
timestamp: number;
|
| 19 |
}
|
| 20 |
|
| 21 |
class WebSocketManager {
|
| 22 |
-
private connections: Map<
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
|
| 25 |
handleConnection(ws: WebSocket, _request: IncomingMessage) {
|
| 26 |
this.connections.set(ws, {});
|
|
@@ -60,10 +74,9 @@ class WebSocketManager {
|
|
| 60 |
connectionData.token = message.payload.token;
|
| 61 |
|
| 62 |
try {
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
connectionData.
|
| 66 |
-
await connectionData.agent.initialize(message.payload.token);
|
| 67 |
|
| 68 |
this.sendMessage(ws, {
|
| 69 |
type: "status",
|
|
@@ -82,6 +95,12 @@ class WebSocketManager {
|
|
| 82 |
}
|
| 83 |
break;
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
case "chat":
|
| 86 |
try {
|
| 87 |
if (!connectionData?.agent) {
|
|
@@ -101,17 +120,25 @@ class WebSocketManager {
|
|
| 101 |
timestamp: Date.now(),
|
| 102 |
});
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
const response = await connectionData.agent.processMessage(
|
| 105 |
userMessage,
|
| 106 |
-
(
|
|
|
|
| 107 |
this.sendMessage(ws, {
|
| 108 |
type: "stream",
|
| 109 |
-
payload: { chunk
|
| 110 |
timestamp: Date.now(),
|
| 111 |
});
|
| 112 |
},
|
| 113 |
);
|
| 114 |
|
|
|
|
|
|
|
| 115 |
this.sendMessage(ws, {
|
| 116 |
type: "chat",
|
| 117 |
payload: {
|
|
@@ -162,8 +189,3 @@ class WebSocketManager {
|
|
| 162 |
}
|
| 163 |
|
| 164 |
export const wsManager = new WebSocketManager();
|
| 165 |
-
|
| 166 |
-
export async function initializeAgent() {
|
| 167 |
-
// Initialize tools when server starts
|
| 168 |
-
await import("../tools");
|
| 169 |
-
}
|
|
|
|
| 1 |
import type { IncomingMessage } from "http";
|
| 2 |
import type { WebSocket } from "ws";
|
| 3 |
+
import { LangGraphAgent } from "./langgraph-agent";
|
| 4 |
+
import { HumanMessage, AIMessage, BaseMessage } from "@langchain/core/messages";
|
| 5 |
+
import { updateEditorContent } from "./tools";
|
| 6 |
|
| 7 |
export interface WebSocketMessage {
|
| 8 |
+
type:
|
| 9 |
+
| "chat"
|
| 10 |
+
| "error"
|
| 11 |
+
| "status"
|
| 12 |
+
| "stream"
|
| 13 |
+
| "auth"
|
| 14 |
+
| "editor_update"
|
| 15 |
+
| "editor_sync"
|
| 16 |
+
| "tool_execution";
|
| 17 |
payload: {
|
| 18 |
content?: string;
|
| 19 |
role?: string;
|
| 20 |
chunk?: string;
|
|
|
|
| 21 |
error?: string;
|
| 22 |
processing?: boolean;
|
| 23 |
connected?: boolean;
|
| 24 |
message?: string;
|
| 25 |
token?: string;
|
| 26 |
+
toolName?: string;
|
| 27 |
+
toolArgs?: Record<string, unknown>;
|
| 28 |
+
toolResult?: string;
|
| 29 |
};
|
| 30 |
timestamp: number;
|
| 31 |
}
|
| 32 |
|
| 33 |
class WebSocketManager {
|
| 34 |
+
private connections: Map<
|
| 35 |
+
WebSocket,
|
| 36 |
+
{ token?: string; agent?: LangGraphAgent; messages?: BaseMessage[] }
|
| 37 |
+
> = new Map();
|
| 38 |
|
| 39 |
handleConnection(ws: WebSocket, _request: IncomingMessage) {
|
| 40 |
this.connections.set(ws, {});
|
|
|
|
| 74 |
connectionData.token = message.payload.token;
|
| 75 |
|
| 76 |
try {
|
| 77 |
+
connectionData.agent = new LangGraphAgent();
|
| 78 |
+
await connectionData.agent.initialize(message.payload.token, ws);
|
| 79 |
+
connectionData.messages = [];
|
|
|
|
| 80 |
|
| 81 |
this.sendMessage(ws, {
|
| 82 |
type: "status",
|
|
|
|
| 95 |
}
|
| 96 |
break;
|
| 97 |
|
| 98 |
+
case "editor_sync":
|
| 99 |
+
if (message.payload.content) {
|
| 100 |
+
updateEditorContent(message.payload.content);
|
| 101 |
+
}
|
| 102 |
+
break;
|
| 103 |
+
|
| 104 |
case "chat":
|
| 105 |
try {
|
| 106 |
if (!connectionData?.agent) {
|
|
|
|
| 120 |
timestamp: Date.now(),
|
| 121 |
});
|
| 122 |
|
| 123 |
+
if (!connectionData.messages) {
|
| 124 |
+
connectionData.messages = [];
|
| 125 |
+
}
|
| 126 |
+
connectionData.messages.push(new HumanMessage(userMessage));
|
| 127 |
+
|
| 128 |
const response = await connectionData.agent.processMessage(
|
| 129 |
userMessage,
|
| 130 |
+
connectionData.messages.slice(0, -1),
|
| 131 |
+
(chunk: string) => {
|
| 132 |
this.sendMessage(ws, {
|
| 133 |
type: "stream",
|
| 134 |
+
payload: { chunk },
|
| 135 |
timestamp: Date.now(),
|
| 136 |
});
|
| 137 |
},
|
| 138 |
);
|
| 139 |
|
| 140 |
+
connectionData.messages.push(new AIMessage(response));
|
| 141 |
+
|
| 142 |
this.sendMessage(ws, {
|
| 143 |
type: "chat",
|
| 144 |
payload: {
|
|
|
|
| 189 |
}
|
| 190 |
|
| 191 |
export const wsManager = new WebSocketManager();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/context.md
CHANGED
|
@@ -1,25 +1,29 @@
|
|
| 1 |
# Server Context
|
| 2 |
|
| 3 |
-
WebSocket server
|
| 4 |
|
| 5 |
-
## Components
|
| 6 |
|
| 7 |
-
-
|
| 8 |
-
-
|
| 9 |
-
-
|
| 10 |
-
-
|
| 11 |
-
-
|
| 12 |
-
- `reasoning-extractor.ts` - Filters thinking tags from model output
|
| 13 |
|
| 14 |
## Architecture
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
-
|
| 19 |
-
-
|
| 20 |
-
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Server Context
|
| 2 |
|
| 3 |
+
WebSocket server with React Agent pattern for AI-assisted game development.
|
| 4 |
|
| 5 |
+
## Key Components
|
| 6 |
|
| 7 |
+
- **api.ts** - WebSocket routing with editor sync and tool feedback
|
| 8 |
+
- **langgraph-agent.ts** - React Agent with conditional loops for tool execution
|
| 9 |
+
- **tools.ts** - Editor read/write with WebSocket-based state sync
|
| 10 |
+
- **documentation.ts** - VibeGame documentation loader
|
| 11 |
+
- **prompts.ts** - Documentation formatting utilities
|
|
|
|
| 12 |
|
| 13 |
## Architecture
|
| 14 |
|
| 15 |
+
React Agent pattern with conditional execution flow:
|
| 16 |
+
- Agent node processes messages and executes tools
|
| 17 |
+
- Conditional edges loop back after tool execution
|
| 18 |
+
- Tool results feed back into agent for final response
|
| 19 |
+
- Real-time tool execution feedback via WebSocket
|
| 20 |
+
|
| 21 |
+
## Message Types
|
| 22 |
+
|
| 23 |
+
- `auth` - HF token authentication
|
| 24 |
+
- `chat` - User messages
|
| 25 |
+
- `editor_sync` - Sync editor content with server
|
| 26 |
+
- `editor_update` - Server updates client editor
|
| 27 |
+
- `tool_execution` - Tool execution notifications
|
| 28 |
+
- `stream` - Response streaming
|
| 29 |
+
- `status` - Connection and processing status
|
src/lib/server/hint.test.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
import { describe, expect, test } from "bun:test";
|
| 2 |
-
|
| 3 |
-
describe("Message Enhancement for Tool Usage", () => {
|
| 4 |
-
test("should detect requests to read game code", () => {
|
| 5 |
-
const testCases = [
|
| 6 |
-
{ input: "read the game code", shouldTrigger: true },
|
| 7 |
-
{ input: "show me the game", shouldTrigger: true },
|
| 8 |
-
{ input: "what's in the game?", shouldTrigger: true },
|
| 9 |
-
{ input: "check the code", shouldTrigger: true },
|
| 10 |
-
{ input: "view the editor", shouldTrigger: true },
|
| 11 |
-
{ input: "see the scene", shouldTrigger: true },
|
| 12 |
-
{ input: "display the game code", shouldTrigger: true },
|
| 13 |
-
{ input: "look at the code", shouldTrigger: true },
|
| 14 |
-
{ input: "get the game", shouldTrigger: true },
|
| 15 |
-
{ input: "game code please", shouldTrigger: true },
|
| 16 |
-
{ input: "hello there", shouldTrigger: false },
|
| 17 |
-
{ input: "add a box", shouldTrigger: false },
|
| 18 |
-
{ input: "change the color", shouldTrigger: false },
|
| 19 |
-
];
|
| 20 |
-
|
| 21 |
-
const pattern =
|
| 22 |
-
/\b(read|show|view|check|see|what's in|what is in|get|display|look at)\b.*\b(game|code|editor|scene)\b/i;
|
| 23 |
-
const reversePattern =
|
| 24 |
-
/\b(game|code|editor|scene)\b.*\b(read|show|view|check|see|please)\b/i;
|
| 25 |
-
|
| 26 |
-
for (const { input, shouldTrigger } of testCases) {
|
| 27 |
-
const matches = pattern.test(input) || reversePattern.test(input);
|
| 28 |
-
expect(matches).toBe(shouldTrigger);
|
| 29 |
-
}
|
| 30 |
-
});
|
| 31 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/langgraph-agent.ts
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { InferenceClient } from "@huggingface/inference";
|
| 2 |
+
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
|
| 3 |
+
import {
|
| 4 |
+
HumanMessage,
|
| 5 |
+
AIMessage,
|
| 6 |
+
BaseMessage,
|
| 7 |
+
ToolMessage,
|
| 8 |
+
} from "@langchain/core/messages";
|
| 9 |
+
import {
|
| 10 |
+
readEditorTool,
|
| 11 |
+
writeEditorTool,
|
| 12 |
+
setWebSocketConnection,
|
| 13 |
+
} from "./tools";
|
| 14 |
+
import { documentationService } from "./documentation";
|
| 15 |
+
import type { WebSocket } from "ws";
|
| 16 |
+
|
| 17 |
+
const AgentState = Annotation.Root({
|
| 18 |
+
messages: Annotation<BaseMessage[]>({
|
| 19 |
+
reducer: (x, y) => x.concat(y),
|
| 20 |
+
}),
|
| 21 |
+
hasToolCalls: Annotation<boolean>({
|
| 22 |
+
reducer: (_, y) => y,
|
| 23 |
+
default: () => false,
|
| 24 |
+
}),
|
| 25 |
+
});
|
| 26 |
+
|
| 27 |
+
export class LangGraphAgent {
|
| 28 |
+
private client: InferenceClient | null = null;
|
| 29 |
+
private graph!: ReturnType<typeof StateGraph.prototype.compile>;
|
| 30 |
+
private model: string = "Qwen/Qwen2.5-Coder-32B-Instruct";
|
| 31 |
+
private documentation: string = "";
|
| 32 |
+
private ws: WebSocket | null = null;
|
| 33 |
+
|
| 34 |
+
constructor() {
|
| 35 |
+
this.setupGraph();
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
async initialize(hfToken: string, ws?: WebSocket) {
|
| 39 |
+
if (!hfToken) {
|
| 40 |
+
throw new Error("Hugging Face authentication required");
|
| 41 |
+
}
|
| 42 |
+
this.client = new InferenceClient(hfToken);
|
| 43 |
+
|
| 44 |
+
if (ws) {
|
| 45 |
+
this.ws = ws;
|
| 46 |
+
setWebSocketConnection({
|
| 47 |
+
send: (message: { type: string; payload: Record<string, unknown> }) => {
|
| 48 |
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
| 49 |
+
this.ws.send(JSON.stringify(message));
|
| 50 |
+
}
|
| 51 |
+
},
|
| 52 |
+
});
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const docs = await documentationService.load();
|
| 56 |
+
this.documentation = docs || "";
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
private setupGraph() {
|
| 60 |
+
const graph = new StateGraph(AgentState);
|
| 61 |
+
|
| 62 |
+
graph.addNode("agent", async (state) => {
|
| 63 |
+
const systemPrompt = this.buildSystemPrompt();
|
| 64 |
+
const messages = this.formatMessages(state.messages, systemPrompt);
|
| 65 |
+
|
| 66 |
+
const response = await this.callModel(messages);
|
| 67 |
+
const toolCalls = this.parseToolCalls(response);
|
| 68 |
+
|
| 69 |
+
if (toolCalls.length > 0) {
|
| 70 |
+
const toolResults = await this.executeTools(toolCalls);
|
| 71 |
+
const toolMessage = new AIMessage({
|
| 72 |
+
content: response,
|
| 73 |
+
additional_kwargs: { has_tool_calls: true },
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
return {
|
| 77 |
+
messages: [toolMessage, ...toolResults],
|
| 78 |
+
hasToolCalls: true,
|
| 79 |
+
};
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
return {
|
| 83 |
+
messages: [new AIMessage(response)],
|
| 84 |
+
hasToolCalls: false,
|
| 85 |
+
};
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
// @ts-expect-error - LangGraph type mismatch with START constant
|
| 89 |
+
graph.addEdge(START, "agent");
|
| 90 |
+
|
| 91 |
+
// @ts-expect-error - LangGraph type mismatch with conditional edges
|
| 92 |
+
graph.addConditionalEdges("agent", (state) => this.shouldContinue(state), {
|
| 93 |
+
continue: "agent",
|
| 94 |
+
end: END,
|
| 95 |
+
});
|
| 96 |
+
|
| 97 |
+
this.graph = graph.compile();
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
private shouldContinue(state: typeof AgentState.State): string {
|
| 101 |
+
const lastMessage = state.messages[state.messages.length - 1];
|
| 102 |
+
|
| 103 |
+
if (lastMessage instanceof ToolMessage) {
|
| 104 |
+
return "continue";
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
if (
|
| 108 |
+
lastMessage instanceof AIMessage &&
|
| 109 |
+
lastMessage.additional_kwargs?.has_tool_calls
|
| 110 |
+
) {
|
| 111 |
+
return "continue";
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
return "end";
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
private buildSystemPrompt(): string {
|
| 118 |
+
return `You are an expert VibeGame developer assistant. VibeGame is a declarative 3D game engine using XML-like syntax.
|
| 119 |
+
|
| 120 |
+
VIBEGAME DOCUMENTATION:
|
| 121 |
+
${this.documentation}
|
| 122 |
+
|
| 123 |
+
AVAILABLE TOOLS:
|
| 124 |
+
- read_editor: Read the current code in the editor
|
| 125 |
+
- write_editor: Write new code to the editor
|
| 126 |
+
|
| 127 |
+
TOOL USAGE FORMAT:
|
| 128 |
+
To use a tool, format your response with the tool call in this exact format:
|
| 129 |
+
[TOOL: tool_name]
|
| 130 |
+
or with parameters:
|
| 131 |
+
[TOOL: tool_name {"param": "value"}]
|
| 132 |
+
|
| 133 |
+
Example:
|
| 134 |
+
To read the editor: [TOOL: read_editor]
|
| 135 |
+
To write code: [TOOL: write_editor {"content": "<world>...</world>"}]
|
| 136 |
+
|
| 137 |
+
IMPORTANT WORKFLOW:
|
| 138 |
+
1. When asked to modify code, FIRST use read_editor to see the current code
|
| 139 |
+
2. After receiving tool results, CONTINUE with your analysis and next steps
|
| 140 |
+
3. Use write_editor to make the requested changes
|
| 141 |
+
4. After writing, provide a clear explanation of what was changed
|
| 142 |
+
|
| 143 |
+
When you receive tool results, use them to inform your next action. Do not stop after using a tool - continue until the task is complete.
|
| 144 |
+
|
| 145 |
+
Be concise, accurate, and focus on practical solutions.`;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
private formatMessages(
|
| 149 |
+
messages: BaseMessage[],
|
| 150 |
+
systemPrompt: string,
|
| 151 |
+
): Array<{ role: string; content: string }> {
|
| 152 |
+
const formatted = [
|
| 153 |
+
{ role: "system", content: systemPrompt },
|
| 154 |
+
...messages.map((msg) => {
|
| 155 |
+
let role = "assistant";
|
| 156 |
+
if (msg instanceof HumanMessage) {
|
| 157 |
+
role = "user";
|
| 158 |
+
} else if (msg instanceof ToolMessage) {
|
| 159 |
+
const content = `Tool result for ${msg.name}: ${msg.content}`;
|
| 160 |
+
return { role: "assistant", content };
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
const content =
|
| 164 |
+
typeof msg.content === "string"
|
| 165 |
+
? msg.content
|
| 166 |
+
: JSON.stringify(msg.content);
|
| 167 |
+
return { role, content };
|
| 168 |
+
}),
|
| 169 |
+
];
|
| 170 |
+
return formatted;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
private async callModel(
|
| 174 |
+
messages: Array<{ role: string; content: string }>,
|
| 175 |
+
): Promise<string> {
|
| 176 |
+
if (!this.client) {
|
| 177 |
+
throw new Error("Agent not initialized");
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
const response = await this.client.chatCompletion({
|
| 181 |
+
model: this.model,
|
| 182 |
+
messages,
|
| 183 |
+
temperature: 0.3,
|
| 184 |
+
max_tokens: 2048,
|
| 185 |
+
});
|
| 186 |
+
|
| 187 |
+
return response.choices[0].message.content || "";
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
private parseToolCalls(
|
| 191 |
+
response: string,
|
| 192 |
+
): Array<{ name: string; args: Record<string, unknown> }> {
|
| 193 |
+
const toolCalls = [];
|
| 194 |
+
const toolRegex = /\[TOOL:\s*(\w+)(?:\s+({[^}]+}))?\]/g;
|
| 195 |
+
let match;
|
| 196 |
+
|
| 197 |
+
while ((match = toolRegex.exec(response)) !== null) {
|
| 198 |
+
const toolName = match[1];
|
| 199 |
+
const params = match[2] ? JSON.parse(match[2]) : {};
|
| 200 |
+
|
| 201 |
+
toolCalls.push({
|
| 202 |
+
name: toolName,
|
| 203 |
+
args: params,
|
| 204 |
+
});
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
return toolCalls;
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
private async executeTools(
|
| 211 |
+
toolCalls: Array<{ name: string; args: Record<string, unknown> }>,
|
| 212 |
+
): Promise<BaseMessage[]> {
|
| 213 |
+
const results = [];
|
| 214 |
+
|
| 215 |
+
for (const call of toolCalls) {
|
| 216 |
+
try {
|
| 217 |
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
| 218 |
+
this.ws.send(
|
| 219 |
+
JSON.stringify({
|
| 220 |
+
type: "tool_execution",
|
| 221 |
+
payload: {
|
| 222 |
+
toolName: call.name,
|
| 223 |
+
toolArgs: call.args,
|
| 224 |
+
message: `Executing ${call.name}...`,
|
| 225 |
+
},
|
| 226 |
+
timestamp: Date.now(),
|
| 227 |
+
}),
|
| 228 |
+
);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
let result;
|
| 232 |
+
if (call.name === "read_editor") {
|
| 233 |
+
result = await readEditorTool.func("");
|
| 234 |
+
} else if (call.name === "write_editor") {
|
| 235 |
+
result = await writeEditorTool.func(call.args as { content: string });
|
| 236 |
+
} else {
|
| 237 |
+
result = `Unknown tool: ${call.name}`;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
| 241 |
+
this.ws.send(
|
| 242 |
+
JSON.stringify({
|
| 243 |
+
type: "tool_execution",
|
| 244 |
+
payload: {
|
| 245 |
+
toolName: call.name,
|
| 246 |
+
toolResult: result,
|
| 247 |
+
message: `${call.name} completed`,
|
| 248 |
+
},
|
| 249 |
+
timestamp: Date.now(),
|
| 250 |
+
}),
|
| 251 |
+
);
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
results.push(
|
| 255 |
+
new ToolMessage({
|
| 256 |
+
content: result,
|
| 257 |
+
tool_call_id: `${call.name}_${Date.now()}`,
|
| 258 |
+
name: call.name,
|
| 259 |
+
}),
|
| 260 |
+
);
|
| 261 |
+
} catch (error) {
|
| 262 |
+
results.push(
|
| 263 |
+
new ToolMessage({
|
| 264 |
+
content: `Error executing ${call.name}: ${error}`,
|
| 265 |
+
tool_call_id: `${call.name}_${Date.now()}`,
|
| 266 |
+
name: call.name,
|
| 267 |
+
}),
|
| 268 |
+
);
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
return results;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
async processMessage(
|
| 276 |
+
message: string,
|
| 277 |
+
messageHistory: BaseMessage[] = [],
|
| 278 |
+
onStream?: (chunk: string) => void,
|
| 279 |
+
): Promise<string> {
|
| 280 |
+
if (!this.client) {
|
| 281 |
+
throw new Error("Agent not initialized");
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
const messages = [...messageHistory, new HumanMessage(message)];
|
| 285 |
+
|
| 286 |
+
const stream = await this.graph.stream({
|
| 287 |
+
messages,
|
| 288 |
+
hasToolCalls: false,
|
| 289 |
+
});
|
| 290 |
+
|
| 291 |
+
let fullResponse = "";
|
| 292 |
+
let isStreaming = false;
|
| 293 |
+
let lastAIResponse = "";
|
| 294 |
+
let hasExecutedTools = false;
|
| 295 |
+
|
| 296 |
+
for await (const chunk of stream) {
|
| 297 |
+
if (chunk.agent?.messages) {
|
| 298 |
+
for (const msg of chunk.agent.messages) {
|
| 299 |
+
if (msg instanceof AIMessage) {
|
| 300 |
+
const content = msg.content as string;
|
| 301 |
+
|
| 302 |
+
if (msg.additional_kwargs?.has_tool_calls) {
|
| 303 |
+
hasExecutedTools = true;
|
| 304 |
+
lastAIResponse = content;
|
| 305 |
+
} else {
|
| 306 |
+
if (!isStreaming) {
|
| 307 |
+
isStreaming = true;
|
| 308 |
+
for (const char of content) {
|
| 309 |
+
fullResponse += char;
|
| 310 |
+
onStream?.(char);
|
| 311 |
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
| 312 |
+
}
|
| 313 |
+
} else {
|
| 314 |
+
fullResponse = content;
|
| 315 |
+
}
|
| 316 |
+
lastAIResponse = content;
|
| 317 |
+
}
|
| 318 |
+
} else if (msg instanceof ToolMessage) {
|
| 319 |
+
if (onStream && !hasExecutedTools) {
|
| 320 |
+
const toolNotification = `\n🔧 ${msg.name}\n`;
|
| 321 |
+
for (const char of toolNotification) {
|
| 322 |
+
onStream(char);
|
| 323 |
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
| 324 |
+
}
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
return fullResponse || lastAIResponse;
|
| 332 |
+
}
|
| 333 |
+
}
|
src/lib/server/prompts.ts
CHANGED
|
@@ -1,50 +1,4 @@
|
|
| 1 |
-
export const SYSTEM_PROMPTS = {
|
| 2 |
-
main: (
|
| 3 |
-
tools: string,
|
| 4 |
-
documentation: string,
|
| 5 |
-
) => `You are an expert VibeGame developer assistant. VibeGame is a declarative 3D game engine using XML-like syntax, similar to Roblox but with "vibe coding" - focusing on quick, creative game development.
|
| 6 |
-
|
| 7 |
-
ENGINE CONTEXT:
|
| 8 |
-
VibeGame is a custom engine not in your training data. It uses declarative XML syntax for scene definition with Entity-Component-System (ECS) architecture.
|
| 9 |
-
|
| 10 |
-
${documentation}
|
| 11 |
-
|
| 12 |
-
AVAILABLE TOOLS:
|
| 13 |
-
${tools}
|
| 14 |
-
|
| 15 |
-
TOOL USAGE:
|
| 16 |
-
- Execute tools with: [TOOL: tool_name] or [TOOL: tool_name {"param": "value"}]
|
| 17 |
-
- read_game_code: Get current editor content
|
| 18 |
-
- write_game_code: Update the editor with new code
|
| 19 |
-
- read_console_output: Check console for errors, logs, and game output
|
| 20 |
-
|
| 21 |
-
WORKFLOW:
|
| 22 |
-
1. Read the current code to understand the game
|
| 23 |
-
2. Make changes using write_game_code
|
| 24 |
-
3. Check console output for errors or success
|
| 25 |
-
4. Iterate based on feedback
|
| 26 |
-
|
| 27 |
-
Be helpful, accurate, and adapt to what the user needs. Focus on working code and practical solutions.`,
|
| 28 |
-
|
| 29 |
-
toolFollowUp: (
|
| 30 |
-
documentation: string,
|
| 31 |
-
) => `Based on the tool execution result, provide relevant analysis of what you found.
|
| 32 |
-
|
| 33 |
-
${documentation}`,
|
| 34 |
-
};
|
| 35 |
-
|
| 36 |
export const formatDocumentation = (fullDocs: string): string => {
|
| 37 |
return `VIBEGAME REFERENCE:
|
| 38 |
${fullDocs}`;
|
| 39 |
};
|
| 40 |
-
|
| 41 |
-
export const buildSystemPrompt = (
|
| 42 |
-
tools: string,
|
| 43 |
-
documentation: string,
|
| 44 |
-
): string => {
|
| 45 |
-
return SYSTEM_PROMPTS.main(tools, documentation);
|
| 46 |
-
};
|
| 47 |
-
|
| 48 |
-
export const buildToolFollowUpPrompt = (documentation: string): string => {
|
| 49 |
-
return SYSTEM_PROMPTS.toolFollowUp(documentation);
|
| 50 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
export const formatDocumentation = (fullDocs: string): string => {
|
| 2 |
return `VIBEGAME REFERENCE:
|
| 3 |
${fullDocs}`;
|
| 4 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/reasoning-extractor.test.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
| 1 |
-
import { describe, test, expect } from "bun:test";
|
| 2 |
-
import { UniversalReasoningExtractor } from "./reasoning-extractor";
|
| 3 |
-
|
| 4 |
-
describe("UniversalReasoningExtractor", () => {
|
| 5 |
-
test("extracts Qwen thinking tags", () => {
|
| 6 |
-
const extractor = new UniversalReasoningExtractor();
|
| 7 |
-
const input = `<thinking>
|
| 8 |
-
Let me analyze this step by step:
|
| 9 |
-
1. First, I need to understand the problem
|
| 10 |
-
2. Then I'll formulate a solution
|
| 11 |
-
3. Finally, I'll provide the answer
|
| 12 |
-
</thinking>
|
| 13 |
-
|
| 14 |
-
The answer to your question is 42.`;
|
| 15 |
-
|
| 16 |
-
const result = extractor.extract(input);
|
| 17 |
-
expect(result.content).toBe("The answer to your question is 42.");
|
| 18 |
-
expect(result.reasoning).toContain("Let me analyze this step by step");
|
| 19 |
-
});
|
| 20 |
-
|
| 21 |
-
test("extracts DeepSeek R1 think tags", () => {
|
| 22 |
-
const extractor = new UniversalReasoningExtractor();
|
| 23 |
-
const input = `<think>
|
| 24 |
-
Hmm, this is an interesting problem.
|
| 25 |
-
I should consider multiple approaches.
|
| 26 |
-
</think>
|
| 27 |
-
|
| 28 |
-
Here's the solution to your problem.`;
|
| 29 |
-
|
| 30 |
-
const result = extractor.extract(input);
|
| 31 |
-
expect(result.content).toBe("Here's the solution to your problem.");
|
| 32 |
-
expect(result.reasoning).toContain("interesting problem");
|
| 33 |
-
});
|
| 34 |
-
|
| 35 |
-
test("handles interleaved reasoning tags", () => {
|
| 36 |
-
const extractor = new UniversalReasoningExtractor();
|
| 37 |
-
const input = `<thinking>
|
| 38 |
-
First reasoning block
|
| 39 |
-
</thinking>
|
| 40 |
-
Some content here.
|
| 41 |
-
<reasoning>
|
| 42 |
-
Second reasoning block
|
| 43 |
-
</reasoning>
|
| 44 |
-
Final answer.`;
|
| 45 |
-
|
| 46 |
-
const result = extractor.extract(input);
|
| 47 |
-
expect(result.content).toBe("Some content here.\n\nFinal answer.");
|
| 48 |
-
expect(result.reasoning).toContain("First reasoning block");
|
| 49 |
-
expect(result.reasoning).toContain("Second reasoning block");
|
| 50 |
-
});
|
| 51 |
-
|
| 52 |
-
test("streams content correctly", () => {
|
| 53 |
-
const extractor = new UniversalReasoningExtractor();
|
| 54 |
-
|
| 55 |
-
const chunks = [
|
| 56 |
-
"Hello, ",
|
| 57 |
-
"<think",
|
| 58 |
-
"ing>",
|
| 59 |
-
"This is my ",
|
| 60 |
-
"reasoning process",
|
| 61 |
-
"</thinking>",
|
| 62 |
-
" Here is the answer.",
|
| 63 |
-
];
|
| 64 |
-
|
| 65 |
-
let finalContent = "";
|
| 66 |
-
let finalReasoning = "";
|
| 67 |
-
|
| 68 |
-
for (const chunk of chunks) {
|
| 69 |
-
const result = extractor.stream(chunk);
|
| 70 |
-
if (result.partial) {
|
| 71 |
-
finalContent += result.partial;
|
| 72 |
-
}
|
| 73 |
-
if (result.reasoning) {
|
| 74 |
-
finalReasoning = result.reasoning;
|
| 75 |
-
}
|
| 76 |
-
}
|
| 77 |
-
|
| 78 |
-
expect(finalContent).toBe("Hello, Here is the answer.");
|
| 79 |
-
expect(finalReasoning).toBe("This is my reasoning process");
|
| 80 |
-
});
|
| 81 |
-
|
| 82 |
-
test("handles multiple reasoning blocks", () => {
|
| 83 |
-
const extractor = new UniversalReasoningExtractor();
|
| 84 |
-
const input = `<thinking>First thought</thinking>
|
| 85 |
-
Some content here.
|
| 86 |
-
<reasoning>Second thought</reasoning>
|
| 87 |
-
More content.`;
|
| 88 |
-
|
| 89 |
-
const result = extractor.extract(input);
|
| 90 |
-
expect(result.content).toBe("Some content here.\n\nMore content.");
|
| 91 |
-
expect(result.reasoning).toContain("First thought");
|
| 92 |
-
expect(result.reasoning).toContain("Second thought");
|
| 93 |
-
});
|
| 94 |
-
|
| 95 |
-
test("returns original text when no reasoning tags", () => {
|
| 96 |
-
const extractor = new UniversalReasoningExtractor();
|
| 97 |
-
const input = "This is just regular text without any reasoning tags.";
|
| 98 |
-
|
| 99 |
-
const result = extractor.extract(input);
|
| 100 |
-
expect(result.content).toBe(input);
|
| 101 |
-
expect(result.reasoning).toBe("");
|
| 102 |
-
});
|
| 103 |
-
|
| 104 |
-
test("reset clears internal state", () => {
|
| 105 |
-
const extractor = new UniversalReasoningExtractor();
|
| 106 |
-
|
| 107 |
-
extractor.stream("<thinking>Test");
|
| 108 |
-
extractor.reset();
|
| 109 |
-
|
| 110 |
-
const result = extractor.stream("Normal text");
|
| 111 |
-
expect(result.partial).toBe("Normal text");
|
| 112 |
-
expect(result.reasoning).toBeUndefined();
|
| 113 |
-
});
|
| 114 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/reasoning-extractor.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
| 1 |
-
export interface ExtractedContent {
|
| 2 |
-
reasoning: string;
|
| 3 |
-
content: string;
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
export interface StreamResult {
|
| 7 |
-
partial: string;
|
| 8 |
-
reasoning?: string;
|
| 9 |
-
complete: boolean;
|
| 10 |
-
}
|
| 11 |
-
|
| 12 |
-
export interface ReasoningExtractor {
|
| 13 |
-
patterns: RegExp[];
|
| 14 |
-
extract(text: string): ExtractedContent;
|
| 15 |
-
stream(chunk: string): StreamResult;
|
| 16 |
-
reset(): void;
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
export class UniversalReasoningExtractor implements ReasoningExtractor {
|
| 20 |
-
patterns = [
|
| 21 |
-
/<thinking>(.*?)<\/thinking>/s,
|
| 22 |
-
/<think>(.*?)<\/think>/s,
|
| 23 |
-
/<reasoning>(.*?)<\/reasoning>/s,
|
| 24 |
-
];
|
| 25 |
-
|
| 26 |
-
private buffer = "";
|
| 27 |
-
private inReasoning = false;
|
| 28 |
-
private reasoningBuffer = "";
|
| 29 |
-
private currentTag: string | null = null;
|
| 30 |
-
|
| 31 |
-
extract(text: string): ExtractedContent {
|
| 32 |
-
let reasoning = "";
|
| 33 |
-
let cleanContent = text;
|
| 34 |
-
|
| 35 |
-
for (const pattern of this.patterns) {
|
| 36 |
-
const matches = Array.from(
|
| 37 |
-
text.matchAll(new RegExp(pattern.source, "gs")),
|
| 38 |
-
);
|
| 39 |
-
|
| 40 |
-
for (const match of matches) {
|
| 41 |
-
if (match[1]) {
|
| 42 |
-
reasoning += match[1].trim() + "\n";
|
| 43 |
-
cleanContent = cleanContent.replace(match[0], "");
|
| 44 |
-
}
|
| 45 |
-
}
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
return {
|
| 49 |
-
reasoning: reasoning.trim(),
|
| 50 |
-
content: cleanContent.trim(),
|
| 51 |
-
};
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
stream(chunk: string): StreamResult {
|
| 55 |
-
this.buffer += chunk;
|
| 56 |
-
|
| 57 |
-
const result: StreamResult = {
|
| 58 |
-
partial: "",
|
| 59 |
-
complete: false,
|
| 60 |
-
};
|
| 61 |
-
|
| 62 |
-
// Check for opening tags
|
| 63 |
-
if (!this.inReasoning) {
|
| 64 |
-
const openingMatch = this.buffer.match(/<(thinking|think|reasoning)>/);
|
| 65 |
-
if (openingMatch) {
|
| 66 |
-
const beforeTag = this.buffer.substring(0, openingMatch.index);
|
| 67 |
-
result.partial = beforeTag;
|
| 68 |
-
this.buffer = this.buffer.substring(
|
| 69 |
-
openingMatch.index! + openingMatch[0].length,
|
| 70 |
-
);
|
| 71 |
-
this.inReasoning = true;
|
| 72 |
-
this.currentTag = openingMatch[1];
|
| 73 |
-
return result;
|
| 74 |
-
}
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
// Check for closing tags
|
| 78 |
-
if (this.inReasoning && this.currentTag) {
|
| 79 |
-
const closingTag = `</${this.currentTag}>`;
|
| 80 |
-
const closingIndex = this.buffer.indexOf(closingTag);
|
| 81 |
-
|
| 82 |
-
if (closingIndex !== -1) {
|
| 83 |
-
this.reasoningBuffer += this.buffer.substring(0, closingIndex);
|
| 84 |
-
result.reasoning = this.reasoningBuffer.trim();
|
| 85 |
-
this.reasoningBuffer = "";
|
| 86 |
-
this.buffer = this.buffer.substring(closingIndex + closingTag.length);
|
| 87 |
-
this.inReasoning = false;
|
| 88 |
-
this.currentTag = null;
|
| 89 |
-
|
| 90 |
-
// Process any remaining content
|
| 91 |
-
const nextResult = this.stream("");
|
| 92 |
-
result.partial = nextResult.partial;
|
| 93 |
-
if (nextResult.reasoning) {
|
| 94 |
-
result.reasoning =
|
| 95 |
-
(result.reasoning || "") + "\n" + nextResult.reasoning;
|
| 96 |
-
}
|
| 97 |
-
return result;
|
| 98 |
-
} else {
|
| 99 |
-
// Still in reasoning, buffer it
|
| 100 |
-
this.reasoningBuffer += this.buffer;
|
| 101 |
-
this.buffer = "";
|
| 102 |
-
return result;
|
| 103 |
-
}
|
| 104 |
-
}
|
| 105 |
-
|
| 106 |
-
// No reasoning tags, output as content
|
| 107 |
-
if (!this.inReasoning && this.buffer.length > 0) {
|
| 108 |
-
// Check if we might be in the middle of a tag
|
| 109 |
-
const lastAngle = this.buffer.lastIndexOf("<");
|
| 110 |
-
if (lastAngle !== -1 && !this.buffer.substring(lastAngle).includes(">")) {
|
| 111 |
-
// Might be incomplete tag, output everything before it
|
| 112 |
-
result.partial = this.buffer.substring(0, lastAngle);
|
| 113 |
-
this.buffer = this.buffer.substring(lastAngle);
|
| 114 |
-
} else {
|
| 115 |
-
// Safe to output everything
|
| 116 |
-
result.partial = this.buffer;
|
| 117 |
-
this.buffer = "";
|
| 118 |
-
}
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
result.complete = !this.inReasoning && this.buffer.length === 0;
|
| 122 |
-
return result;
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
reset(): void {
|
| 126 |
-
this.buffer = "";
|
| 127 |
-
this.inReasoning = false;
|
| 128 |
-
this.reasoningBuffer = "";
|
| 129 |
-
this.currentTag = null;
|
| 130 |
-
}
|
| 131 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/server/tools.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { DynamicStructuredTool, DynamicTool } from "@langchain/core/tools";
|
| 2 |
+
import { z } from "zod";
|
| 3 |
+
|
| 4 |
+
let currentEditorContent = `<canvas id="game-canvas"></canvas>
|
| 5 |
+
|
| 6 |
+
<world canvas="#game-canvas" sky="#87ceeb">
|
| 7 |
+
<!-- Ground -->
|
| 8 |
+
<static-part pos="0 -0.5 0" shape="box" size="20 1 20" color="#90ee90"></static-part>
|
| 9 |
+
|
| 10 |
+
<!-- Ball -->
|
| 11 |
+
<dynamic-part pos="-2 4 -3" shape="sphere" size="1" color="#ff4500"></dynamic-part>
|
| 12 |
+
</world>
|
| 13 |
+
|
| 14 |
+
<script type="module">
|
| 15 |
+
import * as GAME from 'vibegame';
|
| 16 |
+
|
| 17 |
+
GAME.run();
|
| 18 |
+
</script>`;
|
| 19 |
+
|
| 20 |
+
interface WebSocketConnection {
|
| 21 |
+
send: (message: { type: string; payload: Record<string, unknown> }) => void;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
let wsConnection: WebSocketConnection | null = null;
|
| 25 |
+
|
| 26 |
+
export function setWebSocketConnection(ws: WebSocketConnection) {
|
| 27 |
+
wsConnection = ws;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
export function updateEditorContent(content: string) {
|
| 31 |
+
currentEditorContent = content;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export const readEditorTool = new DynamicTool({
|
| 35 |
+
name: "read_editor",
|
| 36 |
+
description: "Read the current code in the editor",
|
| 37 |
+
func: async () => {
|
| 38 |
+
return `Current editor content (html):\n${currentEditorContent}`;
|
| 39 |
+
},
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
export const writeEditorTool = new DynamicStructuredTool({
|
| 43 |
+
name: "write_editor",
|
| 44 |
+
description: "Write new code to the editor",
|
| 45 |
+
schema: z.object({
|
| 46 |
+
content: z.string().describe("The code content to write to the editor"),
|
| 47 |
+
}),
|
| 48 |
+
func: async (input: { content: string }) => {
|
| 49 |
+
currentEditorContent = input.content;
|
| 50 |
+
|
| 51 |
+
if (wsConnection) {
|
| 52 |
+
wsConnection.send({
|
| 53 |
+
type: "editor_update",
|
| 54 |
+
payload: { content: input.content },
|
| 55 |
+
});
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
return "Code updated successfully in the editor";
|
| 59 |
+
},
|
| 60 |
+
});
|
| 61 |
+
|
| 62 |
+
export const tools = [readEditorTool, writeEditorTool];
|
src/lib/stores/agent.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
-
import { writable, derived } from "svelte/store";
|
| 2 |
import { authStore } from "../services/auth";
|
|
|
|
| 3 |
|
| 4 |
export interface ChatMessage {
|
| 5 |
id: string;
|
|
@@ -53,6 +54,21 @@ function createAgentStore() {
|
|
| 53 |
timestamp: Date.now(),
|
| 54 |
}),
|
| 55 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
} else {
|
| 57 |
update((state) => ({
|
| 58 |
...state,
|
|
@@ -101,6 +117,10 @@ function createAgentStore() {
|
|
| 101 |
role?: string;
|
| 102 |
content?: string;
|
| 103 |
error?: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
};
|
| 105 |
}) {
|
| 106 |
switch (message.type) {
|
|
@@ -199,6 +219,32 @@ function createAgentStore() {
|
|
| 199 |
processing: false,
|
| 200 |
}));
|
| 201 |
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
}
|
| 203 |
}
|
| 204 |
|
|
|
|
| 1 |
+
import { writable, derived, get } from "svelte/store";
|
| 2 |
import { authStore } from "../services/auth";
|
| 3 |
+
import { editorStore } from "./editor";
|
| 4 |
|
| 5 |
export interface ChatMessage {
|
| 6 |
id: string;
|
|
|
|
| 54 |
timestamp: Date.now(),
|
| 55 |
}),
|
| 56 |
);
|
| 57 |
+
|
| 58 |
+
const editorState = get(editorStore);
|
| 59 |
+
if (editorState && editorState.content) {
|
| 60 |
+
setTimeout(() => {
|
| 61 |
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
| 62 |
+
ws.send(
|
| 63 |
+
JSON.stringify({
|
| 64 |
+
type: "editor_sync",
|
| 65 |
+
payload: { content: editorState.content },
|
| 66 |
+
timestamp: Date.now(),
|
| 67 |
+
}),
|
| 68 |
+
);
|
| 69 |
+
}
|
| 70 |
+
}, 500);
|
| 71 |
+
}
|
| 72 |
} else {
|
| 73 |
update((state) => ({
|
| 74 |
...state,
|
|
|
|
| 117 |
role?: string;
|
| 118 |
content?: string;
|
| 119 |
error?: string;
|
| 120 |
+
toolName?: string;
|
| 121 |
+
toolArgs?: Record<string, unknown>;
|
| 122 |
+
toolResult?: string;
|
| 123 |
+
message?: string;
|
| 124 |
};
|
| 125 |
}) {
|
| 126 |
switch (message.type) {
|
|
|
|
| 219 |
processing: false,
|
| 220 |
}));
|
| 221 |
break;
|
| 222 |
+
|
| 223 |
+
case "editor_update":
|
| 224 |
+
if (message.payload.content) {
|
| 225 |
+
editorStore.setContent(message.payload.content);
|
| 226 |
+
}
|
| 227 |
+
break;
|
| 228 |
+
|
| 229 |
+
case "tool_execution":
|
| 230 |
+
update((state) => {
|
| 231 |
+
const toolMessage: ChatMessage = {
|
| 232 |
+
id: `tool_${Date.now()}`,
|
| 233 |
+
role: "system",
|
| 234 |
+
content: `🔧 ${message.payload.toolName}: ${message.payload.message || "Executing..."}`,
|
| 235 |
+
timestamp: Date.now(),
|
| 236 |
+
};
|
| 237 |
+
|
| 238 |
+
if (message.payload.toolResult) {
|
| 239 |
+
toolMessage.content = `🔧 ${message.payload.toolName} completed`;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
return {
|
| 243 |
+
...state,
|
| 244 |
+
messages: [...state.messages, toolMessage],
|
| 245 |
+
};
|
| 246 |
+
});
|
| 247 |
+
break;
|
| 248 |
}
|
| 249 |
}
|
| 250 |
|
src/lib/stores/loading.ts
CHANGED
|
@@ -24,7 +24,6 @@ function createLoadingStore() {
|
|
| 24 |
startLoading: () => {
|
| 25 |
set({ isLoading: true, progress: 0 });
|
| 26 |
|
| 27 |
-
// Simulate initial progress
|
| 28 |
let currentProgress = 0;
|
| 29 |
progressInterval = window.setInterval(() => {
|
| 30 |
currentProgress += Math.random() * 15;
|
|
@@ -44,10 +43,8 @@ function createLoadingStore() {
|
|
| 44 |
progressInterval = null;
|
| 45 |
}
|
| 46 |
|
| 47 |
-
// Complete the progress animation
|
| 48 |
update((state) => ({ ...state, progress: 100 }));
|
| 49 |
|
| 50 |
-
// Hide loading screen after progress completes
|
| 51 |
setTimeout(() => {
|
| 52 |
set({ isLoading: false, progress: 100 });
|
| 53 |
}, 300);
|
|
|
|
| 24 |
startLoading: () => {
|
| 25 |
set({ isLoading: true, progress: 0 });
|
| 26 |
|
|
|
|
| 27 |
let currentProgress = 0;
|
| 28 |
progressInterval = window.setInterval(() => {
|
| 29 |
currentProgress += Math.random() * 15;
|
|
|
|
| 43 |
progressInterval = null;
|
| 44 |
}
|
| 45 |
|
|
|
|
| 46 |
update((state) => ({ ...state, progress: 100 }));
|
| 47 |
|
|
|
|
| 48 |
setTimeout(() => {
|
| 49 |
set({ isLoading: false, progress: 100 });
|
| 50 |
}, 300);
|
src/lib/tools/context.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
| 1 |
-
# Tools Context
|
| 2 |
-
|
| 3 |
-
AI agent tools for game manipulation and feedback.
|
| 4 |
-
|
| 5 |
-
## Structure
|
| 6 |
-
|
| 7 |
-
- `registry.ts` - Tool registration
|
| 8 |
-
- `parser.ts` - Tool call parsing
|
| 9 |
-
- `executor.ts` - Execution flow
|
| 10 |
-
- `read-game-code.ts` - Editor content reader
|
| 11 |
-
- `write-game-code.ts` - Editor content writer
|
| 12 |
-
- `read-console-output.ts` - Console message reader
|
| 13 |
-
- `index.ts` - Auto-registration
|
| 14 |
-
|
| 15 |
-
## Architecture
|
| 16 |
-
|
| 17 |
-
Clean separation:
|
| 18 |
-
|
| 19 |
-
- Registry handles registration only
|
| 20 |
-
- Parser extracts tool calls from responses
|
| 21 |
-
- Executor manages execution flow
|
| 22 |
-
- Individual tools implement business logic
|
| 23 |
-
|
| 24 |
-
## Pattern
|
| 25 |
-
|
| 26 |
-
Agent includes: `[TOOL: tool_name {"param": "value"}]`
|
| 27 |
-
Results formatted: `[TOOL_RESULT: name]` or `[TOOL_ERROR: name]`
|
| 28 |
-
|
| 29 |
-
## Available Tools
|
| 30 |
-
|
| 31 |
-
1. **read_game_code** - Returns editor content
|
| 32 |
-
2. **write_game_code** - Updates editor with new code
|
| 33 |
-
3. **read_console_output** - Reads console messages with filtering
|
| 34 |
-
|
| 35 |
-
## Workflow
|
| 36 |
-
|
| 37 |
-
Read code → Write changes → Check console → Iterate on feedback
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/executor.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
import { toolRegistry } from "./registry";
|
| 2 |
-
import { parseToolCalls, formatToolResult, type ToolCall } from "./parser";
|
| 3 |
-
|
| 4 |
-
export interface ToolExecutionResult {
|
| 5 |
-
toolName: string;
|
| 6 |
-
formatted: string;
|
| 7 |
-
raw: {
|
| 8 |
-
success: boolean;
|
| 9 |
-
data?: unknown;
|
| 10 |
-
error?: string;
|
| 11 |
-
};
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
export class ToolExecutor {
|
| 15 |
-
async executeFromResponse(response: string): Promise<ToolExecutionResult[]> {
|
| 16 |
-
const toolCalls = parseToolCalls(response);
|
| 17 |
-
|
| 18 |
-
if (toolCalls.length === 0) {
|
| 19 |
-
return [];
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
return Promise.all(toolCalls.map((call) => this.executeSingle(call)));
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
private async executeSingle(call: ToolCall): Promise<ToolExecutionResult> {
|
| 26 |
-
const result = await toolRegistry.execute(call.tool, call.parameters);
|
| 27 |
-
|
| 28 |
-
return {
|
| 29 |
-
toolName: call.tool,
|
| 30 |
-
formatted: formatToolResult(call.tool, result),
|
| 31 |
-
raw: result,
|
| 32 |
-
};
|
| 33 |
-
}
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
export const toolExecutor = new ToolExecutor();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/index.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
export { toolRegistry } from "./registry";
|
| 2 |
-
export type { Tool, ToolResult } from "./registry";
|
| 3 |
-
|
| 4 |
-
export { parseToolCalls, formatToolResult } from "./parser";
|
| 5 |
-
export type { ToolCall } from "./parser";
|
| 6 |
-
|
| 7 |
-
export { toolExecutor } from "./executor";
|
| 8 |
-
export type { ToolExecutionResult } from "./executor";
|
| 9 |
-
|
| 10 |
-
import { toolRegistry } from "./registry";
|
| 11 |
-
import { readGameCodeTool } from "./read-game-code";
|
| 12 |
-
import { writeGameCodeTool } from "./write-game-code";
|
| 13 |
-
import { readConsoleOutputTool } from "./read-console-output";
|
| 14 |
-
|
| 15 |
-
toolRegistry.register(readGameCodeTool);
|
| 16 |
-
toolRegistry.register(writeGameCodeTool);
|
| 17 |
-
toolRegistry.register(readConsoleOutputTool);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/parser.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
| 1 |
-
export interface ToolCall {
|
| 2 |
-
tool: string;
|
| 3 |
-
parameters?: unknown;
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
const TOOL_PATTERN = /\[TOOL:\s*(\w+)(?:\s+({[^}]*}))?\]/g;
|
| 7 |
-
|
| 8 |
-
export const parseToolCalls = (response: string): ToolCall[] => {
|
| 9 |
-
const toolCalls: ToolCall[] = [];
|
| 10 |
-
let match;
|
| 11 |
-
|
| 12 |
-
while ((match = TOOL_PATTERN.exec(response)) !== null) {
|
| 13 |
-
const toolName = match[1];
|
| 14 |
-
const paramsStr = match[2];
|
| 15 |
-
|
| 16 |
-
toolCalls.push({
|
| 17 |
-
tool: toolName,
|
| 18 |
-
parameters: paramsStr ? tryParseJson(paramsStr) : undefined,
|
| 19 |
-
});
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
return toolCalls;
|
| 23 |
-
};
|
| 24 |
-
|
| 25 |
-
const tryParseJson = (str: string): unknown => {
|
| 26 |
-
try {
|
| 27 |
-
return JSON.parse(str);
|
| 28 |
-
} catch {
|
| 29 |
-
return undefined;
|
| 30 |
-
}
|
| 31 |
-
};
|
| 32 |
-
|
| 33 |
-
export const formatToolResult = (
|
| 34 |
-
toolName: string,
|
| 35 |
-
result: { success: boolean; data?: unknown; error?: string },
|
| 36 |
-
): string => {
|
| 37 |
-
if (result.success) {
|
| 38 |
-
return `[TOOL_RESULT: ${toolName}]\n${JSON.stringify(result.data, null, 2)}\n[/TOOL_RESULT]`;
|
| 39 |
-
}
|
| 40 |
-
return `[TOOL_ERROR: ${toolName}]\n${result.error}\n[/TOOL_ERROR]`;
|
| 41 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/read-console-output.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
| 1 |
-
import { get } from "svelte/store";
|
| 2 |
-
import { consoleStore } from "../stores/console";
|
| 3 |
-
import type { Tool } from "./registry";
|
| 4 |
-
|
| 5 |
-
interface ReadConsoleParams {
|
| 6 |
-
limit?: number;
|
| 7 |
-
type?: "log" | "warn" | "error" | "info";
|
| 8 |
-
}
|
| 9 |
-
|
| 10 |
-
export const readConsoleOutputTool: Tool = {
|
| 11 |
-
name: "read_console_output",
|
| 12 |
-
description: "Read messages from the game console",
|
| 13 |
-
execute: async (params: unknown) => {
|
| 14 |
-
const { limit = 10, type } = (params || {}) as ReadConsoleParams;
|
| 15 |
-
|
| 16 |
-
try {
|
| 17 |
-
const state = get(consoleStore);
|
| 18 |
-
let messages = state.messages;
|
| 19 |
-
|
| 20 |
-
if (type) {
|
| 21 |
-
messages = messages.filter((msg) => msg.type === type);
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
if (limit > 0) {
|
| 25 |
-
messages = messages.slice(-limit);
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
return {
|
| 29 |
-
success: true,
|
| 30 |
-
data: {
|
| 31 |
-
messages: messages.map((msg) => ({
|
| 32 |
-
type: msg.type,
|
| 33 |
-
message: msg.message,
|
| 34 |
-
timestamp: msg.timestamp,
|
| 35 |
-
})),
|
| 36 |
-
total: messages.length,
|
| 37 |
-
},
|
| 38 |
-
};
|
| 39 |
-
} catch (error) {
|
| 40 |
-
return {
|
| 41 |
-
success: false,
|
| 42 |
-
error:
|
| 43 |
-
error instanceof Error ? error.message : "Failed to read console",
|
| 44 |
-
};
|
| 45 |
-
}
|
| 46 |
-
},
|
| 47 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/read-game-code.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
import { get } from "svelte/store";
|
| 2 |
-
import { editorStore } from "../stores/editor";
|
| 3 |
-
import type { Tool } from "./registry";
|
| 4 |
-
|
| 5 |
-
export const readGameCodeTool: Tool = {
|
| 6 |
-
name: "read_game_code",
|
| 7 |
-
description: "Get the current game code from the editor",
|
| 8 |
-
execute: async () => {
|
| 9 |
-
const state = get(editorStore);
|
| 10 |
-
return {
|
| 11 |
-
success: true,
|
| 12 |
-
data: {
|
| 13 |
-
content: state.content,
|
| 14 |
-
language: state.language,
|
| 15 |
-
},
|
| 16 |
-
};
|
| 17 |
-
},
|
| 18 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/registry.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
export interface Tool {
|
| 2 |
-
name: string;
|
| 3 |
-
description: string;
|
| 4 |
-
execute: (params: unknown) => Promise<ToolResult>;
|
| 5 |
-
}
|
| 6 |
-
|
| 7 |
-
export interface ToolResult {
|
| 8 |
-
success: boolean;
|
| 9 |
-
data?: unknown;
|
| 10 |
-
error?: string;
|
| 11 |
-
}
|
| 12 |
-
|
| 13 |
-
class ToolRegistry {
|
| 14 |
-
private tools: Map<string, Tool> = new Map();
|
| 15 |
-
|
| 16 |
-
register(tool: Tool): void {
|
| 17 |
-
this.tools.set(tool.name, tool);
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
async execute(toolName: string, params?: unknown): Promise<ToolResult> {
|
| 21 |
-
const tool = this.tools.get(toolName);
|
| 22 |
-
|
| 23 |
-
if (!tool) {
|
| 24 |
-
return {
|
| 25 |
-
success: false,
|
| 26 |
-
error: `Tool '${toolName}' not found. Available: ${this.getToolNames().join(", ")}`,
|
| 27 |
-
};
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
try {
|
| 31 |
-
return await tool.execute(params);
|
| 32 |
-
} catch (error) {
|
| 33 |
-
return {
|
| 34 |
-
success: false,
|
| 35 |
-
error: error instanceof Error ? error.message : "Unknown error",
|
| 36 |
-
};
|
| 37 |
-
}
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
getToolNames(): string[] {
|
| 41 |
-
return Array.from(this.tools.keys());
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
getToolDescriptions(): string {
|
| 45 |
-
return Array.from(this.tools.values())
|
| 46 |
-
.map((tool) => `- ${tool.name}: ${tool.description}`)
|
| 47 |
-
.join("\n");
|
| 48 |
-
}
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
export const toolRegistry = new ToolRegistry();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/tools-integration.test.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
| 1 |
-
import { describe, it, expect, beforeEach } from "bun:test";
|
| 2 |
-
import { get } from "svelte/store";
|
| 3 |
-
import { toolRegistry } from "./registry";
|
| 4 |
-
import { editorStore } from "../stores/editor";
|
| 5 |
-
import { consoleStore } from "../stores/console";
|
| 6 |
-
import "./index"; // Import to register tools
|
| 7 |
-
|
| 8 |
-
describe("Tool Integration", () => {
|
| 9 |
-
beforeEach(() => {
|
| 10 |
-
// Reset stores
|
| 11 |
-
editorStore.setContent("");
|
| 12 |
-
consoleStore.reset();
|
| 13 |
-
});
|
| 14 |
-
|
| 15 |
-
describe("write_game_code", () => {
|
| 16 |
-
it("should update editor content", async () => {
|
| 17 |
-
const testCode = `<world canvas="#game-canvas">
|
| 18 |
-
<static-part pos="0 0 0" shape="box" size="10 1 10" color="#90ee90"></static-part>
|
| 19 |
-
</world>`;
|
| 20 |
-
|
| 21 |
-
const result = await toolRegistry.execute("write_game_code", {
|
| 22 |
-
content: testCode,
|
| 23 |
-
});
|
| 24 |
-
|
| 25 |
-
expect(result.success).toBe(true);
|
| 26 |
-
expect(get(editorStore).content).toBe(testCode);
|
| 27 |
-
});
|
| 28 |
-
|
| 29 |
-
it("should handle invalid input", async () => {
|
| 30 |
-
const result = await toolRegistry.execute("write_game_code", {
|
| 31 |
-
content: 123, // Invalid type
|
| 32 |
-
});
|
| 33 |
-
|
| 34 |
-
expect(result.success).toBe(false);
|
| 35 |
-
expect(result.error).toContain("Content must be a string");
|
| 36 |
-
});
|
| 37 |
-
});
|
| 38 |
-
|
| 39 |
-
describe("read_game_code", () => {
|
| 40 |
-
it("should read current editor content", async () => {
|
| 41 |
-
const testCode = "<world>Test</world>";
|
| 42 |
-
editorStore.setContent(testCode);
|
| 43 |
-
|
| 44 |
-
const result = await toolRegistry.execute("read_game_code");
|
| 45 |
-
|
| 46 |
-
expect(result.success).toBe(true);
|
| 47 |
-
expect(result.data).toEqual({
|
| 48 |
-
content: testCode,
|
| 49 |
-
language: "html",
|
| 50 |
-
});
|
| 51 |
-
});
|
| 52 |
-
});
|
| 53 |
-
|
| 54 |
-
describe("read_console_output", () => {
|
| 55 |
-
it("should read console messages", async () => {
|
| 56 |
-
consoleStore.addMessage("log", "Game started");
|
| 57 |
-
consoleStore.addMessage("error", "Entity not found");
|
| 58 |
-
consoleStore.addMessage("warn", "Low performance");
|
| 59 |
-
|
| 60 |
-
const result = await toolRegistry.execute("read_console_output", {
|
| 61 |
-
limit: 2,
|
| 62 |
-
});
|
| 63 |
-
|
| 64 |
-
expect(result.success).toBe(true);
|
| 65 |
-
const data = result.data as { messages: Array<{ message: string }> };
|
| 66 |
-
expect(data.messages).toHaveLength(2);
|
| 67 |
-
expect(data.messages[1].message).toBe("Low performance");
|
| 68 |
-
});
|
| 69 |
-
|
| 70 |
-
it("should filter by type", async () => {
|
| 71 |
-
consoleStore.addMessage("log", "Info 1");
|
| 72 |
-
consoleStore.addMessage("error", "Error 1");
|
| 73 |
-
consoleStore.addMessage("log", "Info 2");
|
| 74 |
-
|
| 75 |
-
const result = await toolRegistry.execute("read_console_output", {
|
| 76 |
-
type: "error",
|
| 77 |
-
});
|
| 78 |
-
|
| 79 |
-
expect(result.success).toBe(true);
|
| 80 |
-
const data = result.data as { messages: Array<{ message: string }> };
|
| 81 |
-
expect(data.messages).toHaveLength(1);
|
| 82 |
-
expect(data.messages[0].message).toBe("Error 1");
|
| 83 |
-
});
|
| 84 |
-
});
|
| 85 |
-
|
| 86 |
-
describe("Tool workflow", () => {
|
| 87 |
-
it("should support a complete edit cycle", async () => {
|
| 88 |
-
// 1. Write some code
|
| 89 |
-
const code = `<world canvas="#game-canvas">
|
| 90 |
-
<dynamic-part pos="0 5 0" shape="sphere" size="1" color="#ff0000"></dynamic-part>
|
| 91 |
-
</world>`;
|
| 92 |
-
|
| 93 |
-
await toolRegistry.execute("write_game_code", { content: code });
|
| 94 |
-
|
| 95 |
-
// 2. Read it back
|
| 96 |
-
const readResult = await toolRegistry.execute("read_game_code");
|
| 97 |
-
expect((readResult.data as { content: string }).content).toBe(code);
|
| 98 |
-
|
| 99 |
-
// 3. Simulate console output
|
| 100 |
-
consoleStore.addMessage("log", "Entity created: dynamic-part");
|
| 101 |
-
consoleStore.addMessage("info", "Physics initialized");
|
| 102 |
-
|
| 103 |
-
// 4. Read console
|
| 104 |
-
const consoleResult = await toolRegistry.execute("read_console_output");
|
| 105 |
-
expect(consoleResult.success).toBe(true);
|
| 106 |
-
const messages = (consoleResult.data as { messages: unknown[] }).messages;
|
| 107 |
-
expect(messages.length).toBeGreaterThan(0);
|
| 108 |
-
});
|
| 109 |
-
});
|
| 110 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/lib/tools/write-game-code.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
import { editorStore } from "../stores/editor";
|
| 2 |
-
import type { Tool } from "./registry";
|
| 3 |
-
|
| 4 |
-
interface WriteGameCodeParams {
|
| 5 |
-
content: string;
|
| 6 |
-
}
|
| 7 |
-
|
| 8 |
-
export const writeGameCodeTool: Tool = {
|
| 9 |
-
name: "write_game_code",
|
| 10 |
-
description: "Update the game code in the editor",
|
| 11 |
-
execute: async (params: unknown) => {
|
| 12 |
-
const { content } = params as WriteGameCodeParams;
|
| 13 |
-
|
| 14 |
-
if (typeof content !== "string") {
|
| 15 |
-
return {
|
| 16 |
-
success: false,
|
| 17 |
-
error: "Content must be a string",
|
| 18 |
-
};
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
try {
|
| 22 |
-
editorStore.setContent(content);
|
| 23 |
-
|
| 24 |
-
return {
|
| 25 |
-
success: true,
|
| 26 |
-
data: {
|
| 27 |
-
message: "Code updated successfully",
|
| 28 |
-
length: content.length,
|
| 29 |
-
},
|
| 30 |
-
};
|
| 31 |
-
} catch (error) {
|
| 32 |
-
return {
|
| 33 |
-
success: false,
|
| 34 |
-
error: error instanceof Error ? error.message : "Failed to update code",
|
| 35 |
-
};
|
| 36 |
-
}
|
| 37 |
-
},
|
| 38 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vite.config.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { defineConfig } from "vite";
|
|
| 2 |
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
| 3 |
import { vibegame, consoleForwarding } from "vibegame/vite";
|
| 4 |
import { WebSocketServer } from "ws";
|
| 5 |
-
import { wsManager
|
| 6 |
|
| 7 |
export default defineConfig({
|
| 8 |
plugins: [
|
|
@@ -14,8 +14,6 @@ export default defineConfig({
|
|
| 14 |
configureServer(server) {
|
| 15 |
const wss = new WebSocketServer({ noServer: true });
|
| 16 |
|
| 17 |
-
initializeAgent().catch(console.error);
|
| 18 |
-
|
| 19 |
server.httpServer?.on("upgrade", (request, socket, head) => {
|
| 20 |
if (request.url === "/ws") {
|
| 21 |
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
@@ -24,7 +22,6 @@ export default defineConfig({
|
|
| 24 |
}
|
| 25 |
});
|
| 26 |
|
| 27 |
-
// Serve OAuth callback page
|
| 28 |
server.middlewares.use((req, res, next) => {
|
| 29 |
if (req.url === "/auth/callback") {
|
| 30 |
res.statusCode = 200;
|
|
|
|
| 2 |
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
| 3 |
import { vibegame, consoleForwarding } from "vibegame/vite";
|
| 4 |
import { WebSocketServer } from "ws";
|
| 5 |
+
import { wsManager } from "./src/lib/server/api";
|
| 6 |
|
| 7 |
export default defineConfig({
|
| 8 |
plugins: [
|
|
|
|
| 14 |
configureServer(server) {
|
| 15 |
const wss = new WebSocketServer({ noServer: true });
|
| 16 |
|
|
|
|
|
|
|
| 17 |
server.httpServer?.on("upgrade", (request, socket, head) => {
|
| 18 |
if (request.url === "/ws") {
|
| 19 |
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
|
|
| 22 |
}
|
| 23 |
});
|
| 24 |
|
|
|
|
| 25 |
server.middlewares.use((req, res, next) => {
|
| 26 |
if (req.url === "/auth/callback") {
|
| 27 |
res.statusCode = 200;
|