dylanebert commited on
Commit
ec75a88
·
1 Parent(s): eb1a39a

langgraph.js migration

Browse files
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 Smolagents for AI-driven game modifications with console feedback loops.
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/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
39
 
 
 
40
  "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.18.2", "", {}, "sha512-DTXrOsn3ra9ZonB+VyqJc16xnRXWsHVa5FK230Z+R1PJ7q8oSRmWQ+AU6e+IJYBHxkM0a5QVDqd729DyeEhHPA=="],
41
 
42
  "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
@@ -127,6 +132,14 @@
127
 
128
  "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
129
 
 
 
 
 
 
 
 
 
130
  "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
131
 
132
  "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
@@ -199,6 +212,10 @@
199
 
200
  "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
201
 
 
 
 
 
202
  "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
203
 
204
  "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "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": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
231
 
232
- "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
233
 
234
  "anymatch": ["anymatch@3.1.3", "", { "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": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
259
 
 
 
260
  "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
261
 
262
  "bitecs": ["bitecs@0.3.40", "", {}, "sha512-wAylY4pNfX8IeIH5phtwt1lUNtHKrkoSNrArI7Ris2Y4nEQWFIVvXdgAuqprEg9bq8Wolmlj0gVfeG6MFmtI2Q=="],
@@ -277,6 +296,8 @@
277
 
278
  "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
279
 
 
 
280
  "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
281
 
282
  "chokidar": ["chokidar@3.6.0", "", { "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": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
293
 
 
 
294
  "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
295
 
296
  "css-tree": ["css-tree@2.3.1", "", { "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": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
307
 
 
 
308
  "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
309
 
310
  "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
@@ -373,6 +398,8 @@
373
 
374
  "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
375
 
 
 
376
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
377
 
378
  "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
@@ -515,6 +542,8 @@
515
 
516
  "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
517
 
 
 
518
  "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
519
 
520
  "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
@@ -529,6 +558,8 @@
529
 
530
  "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
531
 
 
 
532
  "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
533
 
534
  "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
@@ -563,6 +594,8 @@
563
 
564
  "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
565
 
 
 
566
  "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
567
 
568
  "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
@@ -587,10 +620,18 @@
587
 
588
  "own-keys": ["own-keys@1.0.1", "", { "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": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
591
 
592
  "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
593
 
 
 
 
 
 
 
594
  "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
595
 
596
  "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
@@ -631,6 +672,8 @@
631
 
632
  "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
633
 
 
 
634
  "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
635
 
636
  "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
@@ -669,6 +712,8 @@
669
 
670
  "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "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": ["sorcery@0.11.1", "", { "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": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
@@ -733,6 +778,8 @@
733
 
734
  "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
735
 
 
 
736
  "vibegame": ["vibegame@0.1.1", "", { "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": ["vite@5.4.20", "", { "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": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
761
 
 
 
762
  "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
763
 
764
  "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
765
 
 
 
 
 
 
 
766
  "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
767
 
768
  "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
769
 
 
 
770
  "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
771
 
772
  "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
@@ -779,6 +836,8 @@
779
 
780
  "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
781
 
 
 
782
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "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/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
42
 
43
+ "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
44
+
45
  "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.18.2", "", {}, "sha512-DTXrOsn3ra9ZonB+VyqJc16xnRXWsHVa5FK230Z+R1PJ7q8oSRmWQ+AU6e+IJYBHxkM0a5QVDqd729DyeEhHPA=="],
46
 
47
  "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
 
132
 
133
  "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
134
 
135
+ "@langchain/core": ["@langchain/core@0.3.75", "", { "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/langgraph@0.4.9", "", { "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/langgraph-checkpoint@0.1.1", "", { "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/langgraph-sdk@0.1.3", "", { "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/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
144
 
145
  "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
 
212
 
213
  "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
214
 
215
+ "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
216
+
217
+ "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="],
218
+
219
  "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
220
 
221
  "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "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": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
248
 
249
+ "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
250
 
251
  "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
252
 
 
274
 
275
  "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
276
 
277
+ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
278
+
279
  "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
280
 
281
  "bitecs": ["bitecs@0.3.40", "", {}, "sha512-wAylY4pNfX8IeIH5phtwt1lUNtHKrkoSNrArI7Ris2Y4nEQWFIVvXdgAuqprEg9bq8Wolmlj0gVfeG6MFmtI2Q=="],
 
296
 
297
  "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
298
 
299
+ "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
300
+
301
  "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
302
 
303
  "chokidar": ["chokidar@3.6.0", "", { "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": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
314
 
315
+ "console-table-printer": ["console-table-printer@2.14.6", "", { "dependencies": { "simple-wcswidth": "^1.0.1" } }, "sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw=="],
316
+
317
  "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
318
 
319
  "css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
 
328
 
329
  "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
330
 
331
+ "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
332
+
333
  "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
334
 
335
  "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
 
398
 
399
  "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
400
 
401
+ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
402
+
403
  "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
404
 
405
  "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
 
542
 
543
  "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
544
 
545
+ "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
546
+
547
  "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
548
 
549
  "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
 
558
 
559
  "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
560
 
561
+ "langsmith": ["langsmith@0.3.68", "", { "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": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
564
 
565
  "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
 
594
 
595
  "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
596
 
597
+ "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
598
+
599
  "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
600
 
601
  "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
 
620
 
621
  "own-keys": ["own-keys@1.0.1", "", { "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": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
624
+
625
  "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
626
 
627
  "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
628
 
629
+ "p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
630
+
631
+ "p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
632
+
633
+ "p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
634
+
635
  "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
636
 
637
  "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
 
672
 
673
  "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
674
 
675
+ "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
676
+
677
  "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
678
 
679
  "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
 
712
 
713
  "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "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": ["simple-wcswidth@1.1.2", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="],
716
+
717
  "sorcery": ["sorcery@0.11.1", "", { "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": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
 
778
 
779
  "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
780
 
781
+ "uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
782
+
783
  "vibegame": ["vibegame@0.1.1", "", { "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": ["vite@5.4.20", "", { "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": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
808
 
809
+ "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="],
810
+
811
  "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
812
 
813
  "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
814
 
815
+ "@langchain/core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
816
+
817
+ "@langchain/langgraph/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
818
+
819
+ "@langchain/langgraph-sdk/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
820
+
821
  "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
822
 
823
  "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
824
 
825
+ "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
826
+
827
  "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
828
 
829
  "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
 
836
 
837
  "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
838
 
839
+ "langsmith/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
840
+
841
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "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 API
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**: Hugging Face models via WebSocket for game assistance
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, // Large enough for content
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 type { AgentRunner } from "./agent-runner";
 
 
4
 
5
  export interface WebSocketMessage {
6
- type: "chat" | "error" | "status" | "stream" | "auth";
 
 
 
 
 
 
 
 
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<WebSocket, { token?: string; agent?: AgentRunner }> =
23
- new Map();
 
 
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
- // Create a new agent instance for this connection
64
- const { AgentRunner } = await import("./agent-runner");
65
- connectionData.agent = new AgentRunner();
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
- (chunk: string, reasoning?: string) => {
 
107
  this.sendMessage(ws, {
108
  type: "stream",
109
- payload: { chunk, reasoning },
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 and AI agent orchestration with reasoning extraction.
4
 
5
- ## Components
6
 
7
- - `api.ts` - WebSocket connection management
8
- - `agent-runner.ts` - Message flow orchestration with reasoning filtering
9
- - `agent-config.ts` - Configuration constants
10
- - `prompts.ts` - Prompt templates
11
- - `documentation.ts` - Docs loading service
12
- - `reasoning-extractor.ts` - Filters thinking tags from model output
13
 
14
  ## Architecture
15
 
16
- Clean separation of concerns:
17
-
18
- - Agent runner orchestrates message flow and filters reasoning
19
- - Reasoning extractor handles streaming tag extraction
20
- - Tools execute via registry pattern
21
- - Prompts and config externalized
22
-
23
- ## Integration
24
-
25
- Frontend via `src/lib/stores/agent.ts` store with reasoning support.
 
 
 
 
 
 
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, initializeAgent } from "./src/lib/server/api";
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;