Agent loop state machine refactor, unified LLM interface, and UI improvements

- Rewrite agent loop as Planning→Executing(N)→Completed state machine with
  per-step context isolation to prevent token explosion
- Split tools and prompts by phase (planning vs execution)
- Add advance_step/save_memo tools for step transitions and cross-step memory
- Unify LLM interface: remove duplicate types, single chat_with_tools path
- Add UTF-8 safe truncation (truncate_str) to prevent panics on Chinese text
- Extract CreateForm component, add auto-scroll to execution log
- Add report generation with app access URL, non-blocking title generation
- Add timer system, file serving, app proxy, exec module
- Update Dockerfile with uv, deployment config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 22:35:33 +00:00
parent e2d5a6a7eb
commit 2df4e12d30
31 changed files with 3924 additions and 571 deletions

View File

@@ -0,0 +1,142 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const emit = defineEmits<{
submit: [requirement: string]
cancel: []
}>()
const requirement = ref('')
const inputEl = ref<HTMLTextAreaElement>()
const examples = [
{ label: 'Todo 应用', text: '做一个 Todo List 应用:前端展示任务列表(支持添加、完成、删除),后端 FastAPI 提供增删改查 REST API数据存 SQLite。完成后用 curl 跑一遍 E2E 测试验证所有接口正常。' },
{ label: '贪吃蛇+排行榜', text: '做一个贪吃蛇游戏网站,前端用 HTML/JS后端用 FastAPI 存储排行榜,支持提交分数和查看 Top10' },
{ label: '抓取豆瓣 Top250', text: '用 Python 抓取豆瓣电影 Top250 并生成分析报告' },
]
onMounted(() => inputEl.value?.focus())
function onSubmit() {
const text = requirement.value.trim()
if (text) emit('submit', text)
}
</script>
<template>
<div class="create-form">
<h2>输入你的需求</h2>
<div class="create-examples">
<span
v-for="ex in examples"
:key="ex.label"
class="example-tag"
@click="requirement = ex.text"
>{{ ex.label }}</span>
</div>
<textarea
ref="inputEl"
v-model="requirement"
class="create-textarea"
placeholder="描述你想让 AI 做什么..."
rows="4"
@keydown.ctrl.enter="onSubmit"
@keydown.meta.enter="onSubmit"
/>
<div class="create-hint">Ctrl+Enter 提交</div>
<div class="create-actions">
<button class="btn-cancel" @click="emit('cancel')">取消</button>
<button class="btn-confirm" @click="onSubmit" :disabled="!requirement.trim()">开始</button>
</div>
</div>
</template>
<style scoped>
.create-form {
display: flex;
flex-direction: column;
gap: 12px;
width: 480px;
}
.create-form h2 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}
.create-examples {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.example-tag {
padding: 4px 10px;
font-size: 12px;
background: var(--bg-tertiary);
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 12px;
cursor: pointer;
transition: all 0.15s;
}
.example-tag:hover {
color: var(--accent);
border-color: var(--accent);
background: rgba(79, 195, 247, 0.08);
}
.create-textarea {
padding: 10px 12px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-size: 14px;
font-family: inherit;
outline: none;
resize: vertical;
min-height: 80px;
}
.create-textarea:focus {
border-color: var(--accent);
}
.create-hint {
font-size: 12px;
color: var(--text-secondary);
text-align: right;
}
.create-actions {
display: flex;
gap: 8px;
justify-content: flex-end;
}
.btn-cancel {
padding: 8px 16px;
background: var(--bg-tertiary);
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 13px;
}
.btn-confirm {
padding: 8px 16px;
background: var(--accent);
color: var(--bg-primary);
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
}
.btn-confirm:disabled {
opacity: 0.4;
}
</style>