Agent Template

Build a Boat Blueprint Co-Pilot

Plan Build A Boat for Treasure creations, budget gold, and keep a separate library of blueprints without changing existing Roblox saves.

Roblox Build A Boat for Treasure building blueprints gold budgeting
bash Oct 17, 2026 1 use

Preview Mode

This is a preview with sample data. The template uses placeholders like which will be replaced with actual agent data.

About This Template

Build a Boat Blueprint Co-Pilot is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.

Topics Roblox Build A Boat for Treasure building blueprints gold budgeting
Template Preview

Template Metadata

Slug
build-a-boat-blueprint-co-pilot
Created By
bash
Created
Oct 17, 2026
Usage Count
1

Tags

Roblox Build A Boat for Treasure building blueprints gold budgeting

Code Statistics

HTML Lines
46
CSS Lines
1
JS Lines
112
Python Lines
39

Source Code

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ agent_name }}</title>
  <style>{{ css_code }}</style>
</head>
<body>
  <div class="app-shell">
    <header class="masthead">
      <div class="brand-mark" aria-hidden="true">⛵</div>
      <div><p class="eyebrow">BUILD A BOAT FOR TREASURE · BLUEPRINT DESK</p><h1>{{ agent_name }}</h1><p class="subtitle">{{ description }}</p></div>
    </header>
    <div class="notice">🛡️ Your Roblox builds are untouched. Loading and saving here affects browser blueprints only; gold purchases are suggestions, never transactions.</div>
    <div class="workspace">
      <section class="panel controls" aria-labelledby="plan-heading">
        <div class="panel-head"><span class="step">01</span><div><h2 id="plan-heading">Shape your boat</h2><p>Set a goal, inventory, and spending limit.</p></div></div>
        <form id="build-form" novalidate>
          <label for="build-name">Blueprint name</label><input id="build-name" type="text" maxlength="70" placeholder="e.g. River Runner Mk 1" required>
          <label for="goal">Build goal</label><select id="goal"><option value="treasure run">Reach the treasure</option><option value="starter boat">Build a beginner boat</option><option value="multiplayer boat">Carry a crew</option><option value="themed ship">Make a themed ship</option><option value="custom">Custom boat idea</option></select>
          <p id="goal-hint" class="hint">Prioritize a survivable trip toward the treasure.</p>
          <label for="budget">Maximum gold to spend</label><div class="input-unit"><input id="budget" type="number" min="0" max="1000000" step="1" value="0" required><span>gold</span></div>
          <label for="inventory">Blocks and parts you already own <span class="optional">optional</span></label><textarea id="inventory" rows="4" maxlength="2500" placeholder="e.g. 40 wood blocks, 8 metal blocks, 2 seats, 1 motor"></textarea>
          <label for="constraints">Boat idea or constraints <span class="optional">optional</span></label><textarea id="constraints" rows="3" maxlength="1200" placeholder="e.g. two players, compact hull, no rare parts"></textarea>
          <p id="form-error" class="error hidden" role="alert"></p>
          <button id="generate-btn" class="primary" type="submit" disabled>🧭 Draft boat blueprint</button>
        </form>
      </section>
      <section class="panel output" aria-labelledby="blueprint-heading">
        <div class="panel-head"><span class="step">02</span><div><h2 id="blueprint-heading">Boat blueprint</h2><p id="status" role="status" aria-live="polite">Initializing blueprint desk…</p></div></div>
        <div id="results-container" aria-live="polite"><div class="empty-state"><div class="empty-icon" aria-hidden="true">🗺️</div><h3>A clear plan before you place a block</h3><p>Draft a blueprint to get:</p><ul><li>🧱 A parts list based on your inventory</li><li>📐 An ordered build sequence</li><li>🪙 Gold suggestions within your limit</li><li>🛡️ A checklist to protect existing saves</li></ul></div></div>
        <button id="save-btn" class="secondary" type="button" disabled>📌 Save blueprint in this browser</button>
      </section>
      <aside class="panel library" aria-labelledby="library-heading">
        <div class="panel-head"><span class="step">03</span><div><h2 id="library-heading">Blueprint library</h2><p>Load a saved plan without replacing another.</p></div></div>
        <label for="saved-plans">Saved browser blueprints</label><select id="saved-plans"><option value="">No blueprint selected</option></select>
        <button id="load-btn" class="secondary" type="button" disabled>📂 Load selected blueprint</button>
        <p class="hint">Blueprints live in this browser only. Loading one displays its plan; it does not load a Roblox save slot.</p>
      </aside>
    </div>
  </div>
  <div id="loading-overlay" class="loading-overlay hidden" role="status" aria-live="polite"><div class="loading-card"><span class="spinner" aria-hidden="true"></span><strong>Sketching your boat blueprint…</strong><span>Checking your goal and gold limit.</span></div></div>
  <script>{{ js_code }}</script>
</body>
</html>
:root{--sea:#102d40;--deep:#092233;--teal:#087e81;--teal-dark:#07676a;--sand:#f5f2e9;--paper:#fffefa;--ink:#18303d;--muted:#596f78;--line:#d9e3df;--gold:#f2b84b;--danger:#ae303c;--radius:18px;--shadow:0 12px 35px rgba(9,34,51,.10);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:var(--ink);background:var(--sand)}*{box-sizing:border-box}body{margin:0;min-height:100vh}.app-shell{max-width:1640px;margin:auto;padding:34px 32px 60px}.masthead{display:flex;align-items:center;gap:20px;margin-bottom:20px}.brand-mark{display:grid;place-items:center;width:64px;height:64px;border-radius:20px;background:var(--sea);font-size:32px;flex:none}.eyebrow{margin:0 0 5px;color:var(--teal-dark);font-size:.72rem;font-weight:800;letter-spacing:.16em}h1{margin:0;color:var(--deep);font-size:clamp(1.8rem,3vw,2.65rem);letter-spacing:-.04em}.subtitle{margin:6px 0 0;color:var(--muted);line-height:1.5}.notice{background:#fff7df;border:1px solid #ecd596;border-radius:13px;padding:12px 16px;margin:0 0 22px;line-height:1.45}.workspace{display:grid;grid-template-columns:minmax(300px,1.05fr) minmax(340px,1.4fr) minmax(235px,.75fr);gap:18px;align-items:start}.panel{min-width:0;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:22px}.panel-head{display:flex;align-items:flex-start;gap:12px;margin-bottom:22px}.step{display:grid;place-items:center;flex:none;width:37px;height:37px;background:#ddf1ee;color:var(--teal-dark);border-radius:11px;font-size:.8rem;font-weight:850}.panel-head h2{margin:0;font-size:1.2rem}.panel-head p{margin:4px 0 0;color:var(--muted);font-size:.88rem;line-height:1.4}label{display:block;margin:16px 0 7px;font-size:.88rem;font-weight:750}.optional{font-weight:400;color:var(--muted)}input,select,textarea{width:100%;border:1px solid #bfcfcd;border-radius:10px;background:white;color:var(--ink);padding:11px 12px;font:inherit;line-height:1.4}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#7a8d92}.input-unit{position:relative}.input-unit input{padding-right:55px}.input-unit span{position:absolute;right:12px;top:12px;color:var(--muted);font-size:.85rem}.hint{color:var(--muted);font-size:.8rem;line-height:1.5;margin:8px 0 14px}button{width:100%;border:0;border-radius:11px;padding:12px 14px;font:inherit;font-weight:750;cursor:pointer;transition:transform .15s,background .15s}button:hover:not(:disabled){transform:translateY(-1px)}button:disabled{opacity:.48;cursor:not-allowed}.primary{background:var(--teal);color:white;margin-top:15px}.primary:hover:not(:disabled){background:var(--teal-dark)}.secondary{background:#e5f2ef;color:var(--teal-dark);margin-top:15px}.secondary:hover:not(:disabled){background:#d3eae5}:is(button,input,textarea,select):focus-visible{outline:3px solid var(--gold);outline-offset:3px}.error{background:#fff1f1;color:var(--danger);border:1px solid #f0c4c8;border-radius:9px;padding:10px;font-size:.86rem;line-height:1.4}.hidden{display:none!important}#results-container{min-height:410px;max-height:65vh;overflow:auto;background:#f9fbf8;border:1px dashed #bdcfca;border-radius:14px;padding:20px}.empty-state{max-width:390px;margin:38px auto;color:var(--muted)}.empty-icon{font-size:36px}.empty-state h3{color:var(--ink);font-size:1.2rem;margin:12px 0}.empty-state ul{padding-left:20px;line-height:2}.blueprint-text{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.65;font-size:.93rem}.library select{margin-bottom:2px}.loading-overlay{position:fixed;inset:0;z-index:20;background:rgba(9,34,51,.42);display:grid;place-items:center;padding:16px}.loading-card{display:flex;flex-direction:column;align-items:center;gap:11px;text-align:center;background:var(--paper);border-radius:18px;padding:32px;box-shadow:var(--shadow);max-width:340px}.loading-card span:last-child{color:var(--muted);font-size:.85rem}.spinner{width:38px;height:38px;border:4px solid #d8e7e4;border-top-color:var(--teal);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1200px){.workspace{grid-template-columns:minmax(300px,1fr) minmax(360px,1.3fr)}.library{grid-column:1/-1}}@media(max-width:768px){.app-shell{padding:18px 14px 40px}.workspace{grid-template-columns:1fr}.library{grid-column:auto}.panel{padding:18px}#results-container{min-height:320px;max-height:none}.masthead{align-items:flex-start}.brand-mark{width:48px;height:48px;font-size:25px}}
(() => {
  const el = id => document.getElementById(id);
  const form = el('build-form');
  const nameInput = el('build-name');
  const goalInput = el('goal');
  const budgetInput = el('budget');
  const inventoryInput = el('inventory');
  const constraintsInput = el('constraints');
  const generateBtn = el('generate-btn');
  const saveBtn = el('save-btn');
  const loadBtn = el('load-btn');
  const savedSelect = el('saved-plans');
  const errorBox = el('form-error');
  const results = el('results-container');
  const overlay = el('loading-overlay');
  const status = el('status');
  const storageKey = 'agentop-build-a-boat-blueprints-v1';
  const hints = {'treasure run':'Prioritize a survivable trip toward the treasure.','starter boat':'Keep the design simple and easy to assemble.','multiplayer boat':'Make room for the whole crew.','themed ship':'Balance the theme with a usable hull.','custom':'Describe your boat idea in the constraints field.'};
  let ready = false;
  let busy = false;
  let draft = null;
  let library = {};
  const escapeHtml = value => String(value).replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char]));
  const isLocal = () => /local|wllama/.test(String(window.PROVIDER || '').toLowerCase()) || !!window.agentManager;
  const modelLoaded = () => !isLocal() || !!(window.agentManager && window.agentManager.isLoaded);
  function showError(message) { errorBox.textContent = message; errorBox.classList.remove('hidden'); }
  function clearError() { errorBox.textContent = ''; errorBox.classList.add('hidden'); }
  function updateControls() {
    generateBtn.disabled = busy || !ready || !modelLoaded();
    saveBtn.disabled = busy || !draft;
    loadBtn.disabled = busy || !savedSelect.value || !Object.prototype.hasOwnProperty.call(library, savedSelect.value);
    status.textContent = !ready ? 'Initializing blueprint desk…' : busy ? 'Drafting your boat blueprint…' : !modelLoaded() ? 'Load a model from the top bar to draft a blueprint.' : 'Ready to plan your next boat.';
  }
  function invalidateDraft() { draft = null; updateControls(); }
  function renderBlueprint(text) { results.innerHTML = '<div class="blueprint-text">' + escapeHtml(text) + '</div>'; }
  function refreshLibrary() {
    const selected = savedSelect.value;
    savedSelect.innerHTML = '<option value="">Select a browser blueprint</option>';
    Object.keys(library).sort((a,b) => a.localeCompare(b)).forEach(key => {
      const option = document.createElement('option');
      option.value = key;
      option.textContent = library[key].name;
      savedSelect.appendChild(option);
    });
    if (Object.prototype.hasOwnProperty.call(library, selected)) savedSelect.value = selected;
    updateControls();
  }
  function readLibrary() {
    try {
      const data = JSON.parse(localStorage.getItem(storageKey) || '{}');
      library = data && typeof data === 'object' && !Array.isArray(data) ? data : {};
    } catch (error) { library = {}; showError('Browser blueprints could not be read. Existing data has not been changed.'); }
    refreshLibrary();
  }
  function markReady() { ready = !!window.pyodide; updateControls(); }
  document.addEventListener('pyodide-ready', markReady);
  if (window.pyodideReady && window.pyodide) markReady();
  goalInput.addEventListener('change', () => { el('goal-hint').textContent = hints[goalInput.value] || hints.custom; invalidateDraft(); });
  form.addEventListener('input', () => { clearError(); invalidateDraft(); });
  form.addEventListener('change', () => { clearError(); invalidateDraft(); });
  savedSelect.addEventListener('change', updateControls);
  form.addEventListener('submit', async event => {
    event.preventDefault();
    clearError();
    const name = nameInput.value.trim();
    const goal = goalInput.value;
    const rawBudget = budgetInput.value.trim();
    const inventory = inventoryInput.value.trim();
    const constraints = constraintsInput.value.trim();
    if (!name) return showError('Give your new boat blueprint a name.');
    if (!rawBudget || !/^\d+$/.test(rawBudget) || Number(rawBudget) > 1000000) return showError('Enter a whole-number gold limit from 0 to 1,000,000.');
    if (!ready || !window.pyodide) return showError('The blueprint desk is still initializing. Try again in a moment.');
    if (!modelLoaded()) return showError('Load a model from the top bar before drafting a boat blueprint.');
    const request = {name, goal, budget:Number(rawBudget), inventory, constraints};
    busy = true; draft = null; updateControls(); overlay.classList.remove('hidden');
    try {
      window.pyodide.globals.set('user_query', JSON.stringify(request));
      const answer = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
      if (!String(answer || '').trim()) throw new Error('The blueprint came back empty. Please try again.');
      renderBlueprint(String(answer));
      draft = {name, request, result:String(answer)};
    } catch (error) {
      showError('Could not draft the boat blueprint. Check that your model or provider is ready, then try again. ' + (error && error.message ? error.message : ''));
    } finally { busy = false; overlay.classList.add('hidden'); updateControls(); }
  });
  saveBtn.addEventListener('click', () => {
    clearError();
    if (!draft) return showError('Draft a boat blueprint before saving it.');
    const key = draft.name.trim().toLocaleLowerCase();
    if (Object.keys(library).some(existing => existing.toLocaleLowerCase() === key)) return showError('A browser blueprint already has that name. Rename this blueprint and draft it again; nothing was overwritten.');
    const updated = {...library, [key]:draft};
    try { localStorage.setItem(storageKey, JSON.stringify(updated)); library = updated; refreshLibrary(); savedSelect.value = key; draft = null; updateControls(); status.textContent = 'Blueprint saved in this browser. Roblox saves are unchanged.'; }
    catch (error) { showError('This browser could not save the blueprint. No existing blueprint was changed.'); }
  });
  loadBtn.addEventListener('click', () => {
    clearError();
    const plan = library[savedSelect.value];
    if (!plan || !plan.request || typeof plan.result !== 'string') return showError('Choose a valid saved browser blueprint to load.');
    nameInput.value = plan.request.name || plan.name || '';
    goalInput.value = plan.request.goal || 'custom';
    budgetInput.value = Number.isFinite(plan.request.budget) ? plan.request.budget : 0;
    inventoryInput.value = plan.request.inventory || '';
    constraintsInput.value = plan.request.constraints || '';
    el('goal-hint').textContent = hints[goalInput.value] || hints.custom;
    renderBlueprint(plan.result);
    draft = null;
    updateControls();
    status.textContent = 'Browser blueprint loaded. No Roblox build or save slot was changed.';
  });
  readLibrary();
  updateControls();
})();
import json


async def process_user_query(query: str) -> str:
    """Draft a Build A Boat for Treasure blueprint without changing game saves.

    Args:
        query: JSON with blueprint name, goal, gold budget, inventory, and constraints.
    """
    try:
        data = json.loads(query)
        if not isinstance(data, dict):
            return 'Please enter a boat blueprint request.'
        name = str(data.get('name', '')).strip()[:70]
        goal = str(data.get('goal', '')).strip()[:60]
        inventory = str(data.get('inventory', '')).strip()[:2500]
        constraints = str(data.get('constraints', '')).strip()[:1200]
        budget = data.get('budget')
        if not name or not isinstance(budget, int) or isinstance(budget, bool) or not 0 <= budget <= 1000000:
            return 'Please provide a blueprint name and a whole-number gold budget from 0 to 1,000,000.'
        if goal not in ('treasure run', 'starter boat', 'multiplayer boat', 'themed ship', 'custom'):
            return 'Please choose a valid boat build goal.'
    except (ValueError, TypeError):
        return 'Please check the boat blueprint inputs and try again.'
    prompt = (
        'Draft a practical Build A Boat for Treasure planning blueprint from these inputs:\n'
        + json.dumps({'name': name, 'goal': goal, 'maximum_gold_to_spend': budget,
                      'owned_parts': inventory or 'Not provided',
                      'preferences': constraints or 'None'}, ensure_ascii=False)
        + '\nGive short sections: Build concept; Parts to use (mark unknown inventory as optional); '
        'Assembly steps; Gold plan; Test and save-safety checklist. '
        'Keep purchase suggestions within the stated budget. If current prices or owned parts are '
        'unknown, do not invent exact prices, totals, or availability; say what to check in-game. '
        'A zero budget means suggest no purchases. Advise using a free/unused in-game save slot '
        'and checking its contents before saving; never suggest overwriting an existing build. '
        'You cannot access Roblox, place blocks, load in-game saves, or spend gold yourself. '
        'Do not imply any of these actions happened. Keep the answer under 450 words.'
    )
    return str(await agentop_llm.generate(prompt, globals().get('TEMPLATE_SYSTEM_PROMPT', '')))