self modifying website

feat: add tool feedback

dunkirk.sh ffacc84f 99b77929

verified
+120 -8
+120 -8
index.html
··· 362 362 console.log('Executing tool call:', func, args); 363 363 364 364 if (window.toolCallbacks[func]) { 365 - // Handle parameter name variations 366 - if (func === 'updateStyle' && args.styleObj) { 367 - // Convert styleObj to the expected parameter name 368 - return window.toolCallbacks[func](args.selector, args.styleObj); 369 - } else { 370 - // Use all argument values in order 371 - return window.toolCallbacks[func](...Object.values(args)); 365 + try { 366 + // Handle different function signatures 367 + if (func === 'removeElement') { 368 + // removeElement expects just a selector 369 + const selector = args.selector || Object.keys(args)[0] || Object.values(args)[0]; 370 + return window.toolCallbacks[func](selector); 371 + } else if (func === 'updateStyle' && args.styleObj) { 372 + return window.toolCallbacks[func](args.selector, args.styleObj); 373 + } else if (func === 'executeJS') { 374 + return window.toolCallbacks[func](args.code); 375 + } else if (func === 'updateElement') { 376 + return window.toolCallbacks[func](args.selector, args.newContent); 377 + } else if (func === 'replaceElement') { 378 + return window.toolCallbacks[func](args.selector, args.newHTML); 379 + } else if (func === 'addElement') { 380 + return window.toolCallbacks[func](args.parentSelector, args.newHTML, args.position); 381 + } else { 382 + // Fallback: use all argument values in order 383 + return window.toolCallbacks[func](...Object.values(args)); 384 + } 385 + } catch (error) { 386 + return { success: false, message: `Error executing ${func}: ${error.message}` }; 372 387 } 373 388 } 374 389 return { success: false, message: `Unknown tool: ${func}` }; ··· 399 414 messages: [ 400 415 { 401 416 role: "user", 402 - content: `Here is the current HTML page:\n\n${currentPageHTML}\n\nUser request: "${userPrompt}"\n\nIMPORTANT: You must respond with ONLY one of these two formats:\n\nFORMAT 1 - Tool calls (for precise modifications):\n{"tool_calls": [{"function": "functionName", "arguments": {"param": "value"}}]}\n\nFORMAT 2 - Raw HTML (to append to page):\n<div>Your HTML content here</div>\n\nDO NOT include any explanatory text, markdown formatting, or additional commentary. Respond with ONLY the JSON or HTML.\n\nAvailable tools:\n1. replaceElement(selector, newHTML) - Replace an element\n2. updateElement(selector, newContent) - Update element content \n3. addElement(parentSelector, newHTML, position) - Add element\n4. removeElement(selector) - Remove an element\n5. updateStyle(selector, styleObj) - Update CSS styles\n6. executeJS(code) - Run JavaScript code\n\nUse DOS/retro aesthetic with flat colors: #000000 (black), #ffffff (white), #c0c0c0 (gray), #000080 (blue), #ff0000 (red). Use monospace fonts.`, 417 + content: `Here is the current HTML page:\n\n${currentPageHTML}\n\nUser request: "${userPrompt}"\n\nIMPORTANT: You must respond with ONLY one of these two formats:\n\nFORMAT 1 - Tool calls (for precise modifications):\n{"tool_calls": [{"function": "functionName", "arguments": {"param": "value"}}]}\n\nFORMAT 2 - Raw HTML (to append to page):\n<div>Your HTML content here</div>\n\nDO NOT include any explanatory text, markdown formatting, or additional commentary. Respond with ONLY the JSON or HTML.\n\nAvailable tools with correct argument formats:\n1. removeElement: {"function": "removeElement", "arguments": {"selector": ".class-name"}}\n2. updateElement: {"function": "updateElement", "arguments": {"selector": ".class-name", "newContent": "new content"}}\n3. replaceElement: {"function": "replaceElement", "arguments": {"selector": ".class-name", "newHTML": "<div>new html</div>"}}\n4. addElement: {"function": "addElement", "arguments": {"parentSelector": ".parent", "newHTML": "<div>content</div>", "position": "beforeend"}}\n5. updateStyle: {"function": "updateStyle", "arguments": {"selector": ".class-name", "styleObj": {"color": "#000000"}}}\n6. executeJS: {"function": "executeJS", "arguments": {"code": "console.log('hello');"}}\n\nUse DOS/retro aesthetic with flat colors: #000000 (black), #ffffff (white), #c0c0c0 (gray), #000080 (blue), #ff0000 (red). Use monospace fonts.`, 403 418 }, 404 419 ], 405 420 }), ··· 464 479 console.log('Tool call result:', result); 465 480 } 466 481 statusDiv.textContent = `EXECUTED ${results.length} COMMANDS`; 482 + 483 + // Send feedback to AI about tool results 484 + setTimeout(() => sendToolFeedback(userPrompt, results), 100); 467 485 } else { 468 486 throw new Error("Invalid tool call format"); 469 487 } ··· 497 515 function clearEditor() { 498 516 document.getElementById("codeEditor").value = ""; 499 517 document.getElementById("statusDisplay").textContent = ""; 518 + } 519 + 520 + async function sendToolFeedback(originalPrompt, toolResults) { 521 + const statusDiv = document.getElementById("statusDisplay"); 522 + 523 + try { 524 + statusDiv.textContent = "SENDING FEEDBACK TO AI..."; 525 + 526 + const currentPageHTML = document.documentElement.outerHTML; 527 + const resultsText = toolResults.map(r => 528 + `${r.success ? '✓' : '✗'} ${r.message}${r.result ? ` (result: ${r.result})` : ''}` 529 + ).join('\n'); 530 + 531 + const response = await fetch( 532 + "https://ai.hackclub.com/chat/completions", 533 + { 534 + method: "POST", 535 + headers: { 536 + "Content-Type": "application/json", 537 + }, 538 + body: JSON.stringify({ 539 + messages: [ 540 + { 541 + role: "user", 542 + content: `Previous request: "${originalPrompt}"\n\nTool execution results:\n${resultsText}\n\nCurrent page state:\n${currentPageHTML}\n\nBased on the tool results, do you need to make any follow-up modifications? If everything looks good, respond with "COMPLETE". If you need to make adjustments, respond with tool calls or HTML.\n\nIMPORTANT: Respond with ONLY one of these formats:\n- "COMPLETE" (if satisfied)\n- {"tool_calls": [...]} (for modifications)\n- Raw HTML (to append content)\n\nDO NOT include explanatory text.`, 543 + }, 544 + ], 545 + }), 546 + }, 547 + ); 548 + 549 + if (!response.ok) { 550 + throw new Error(`HTTP ${response.status}: ${response.statusText}`); 551 + } 552 + 553 + const data = await response.json(); 554 + let followUpContent; 555 + 556 + if (data.choices && data.choices[0] && data.choices[0].message) { 557 + followUpContent = data.choices[0].message.content; 558 + } else if (data.content) { 559 + followUpContent = data.content; 560 + } else if (data.response) { 561 + followUpContent = data.response; 562 + } else { 563 + throw new Error("Unexpected API response format"); 564 + } 565 + 566 + followUpContent = followUpContent.trim(); 567 + console.log('Follow-up response:', followUpContent); 568 + 569 + if (followUpContent === "COMPLETE") { 570 + statusDiv.textContent = "AI SATISFIED - TASK COMPLETE"; 571 + setTimeout(() => statusDiv.textContent = "", 3000); 572 + return; 573 + } 574 + 575 + // Process follow-up commands 576 + statusDiv.textContent = "AI MAKING ADJUSTMENTS..."; 577 + 578 + // Try to parse as tool calls 579 + try { 580 + const jsonMatch = followUpContent.match(/\{[\s\S]*"tool_calls"[\s\S]*\}/); 581 + if (jsonMatch) { 582 + const toolResponse = JSON.parse(jsonMatch[0]); 583 + if (toolResponse.tool_calls && Array.isArray(toolResponse.tool_calls)) { 584 + const followUpResults = []; 585 + for (const toolCall of toolResponse.tool_calls) { 586 + const result = executeToolCall(toolCall); 587 + followUpResults.push(result); 588 + console.log('Follow-up tool result:', result); 589 + } 590 + statusDiv.textContent = `AI EXECUTED ${followUpResults.length} ADJUSTMENTS`; 591 + } 592 + } else { 593 + // Treat as HTML 594 + let cleanCode = followUpContent 595 + .replace(/```html\n?/g, "") 596 + .replace(/```\n?/g, ""); 597 + document.querySelector(".content").innerHTML += cleanCode; 598 + statusDiv.textContent = "AI ADDED FOLLOW-UP CONTENT"; 599 + } 600 + } catch (error) { 601 + console.log('Follow-up parsing error:', error); 602 + statusDiv.textContent = "AI FEEDBACK ERROR"; 603 + } 604 + 605 + setTimeout(() => statusDiv.textContent = "", 4000); 606 + 607 + } catch (error) { 608 + statusDiv.textContent = `FEEDBACK ERROR: ${error.message}`; 609 + console.error('Feedback error:', error); 610 + setTimeout(() => statusDiv.textContent = "", 3000); 611 + } 500 612 } 501 613 502 614 // Handle Ctrl+Enter in textarea