Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ jobs:
--add-data ".env.example${{ matrix.data_sep }}." \
--add-data "requirements.txt${{ matrix.data_sep }}." \
--add-data "environment.yml${{ matrix.data_sep }}." \
--add-data "app/config${{ matrix.data_sep }}app/config" \
--add-data "app/config/mcp_config.json${{ matrix.data_sep }}app/config" \
--add-data "app/config/connection_test_models.json${{ matrix.data_sep }}app/config" \
--add-data "app/config/scheduler_config.json${{ matrix.data_sep }}app/config" \
--add-data "app/config/skills_config.json${{ matrix.data_sep }}app/config" \
--add-data "app/config/external_comms_config.json${{ matrix.data_sep }}app/config" \
--add-data "app/data${{ matrix.data_sep }}app/data" \
--add-data "app/ui_layer/browser/frontend/dist${{ matrix.data_sep }}app/ui_layer/browser/frontend/dist" \
--add-data "app/gui/docker-compose.yaml${{ matrix.data_sep }}app/gui" \
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@ app/config/settings.json
**/USER.md
**/onboarding_config.json
**/config.json
!build_template.py
!build_template.py
docs/LIVING_UI_DEVELOPER_GUIDE.md
agent_file_system/ACTIONS.md
1 change: 0 additions & 1 deletion agent_core/core/impl/llm/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,6 @@ def _generate_anthropic(
for block in response.content:
if block.type == "text":
content += block.text

content = content.strip()

# Token usage from Anthropic response
Expand Down
6 changes: 6 additions & 0 deletions agent_core/core/impl/trigger/queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ async def fire(
*,
message: str | None = None,
platform: str | None = None,
living_ui_id: str | None = None,
) -> bool:
"""
Mark a trigger for a given session as ready to fire immediately.
Expand All @@ -469,6 +470,7 @@ async def fire(
description so the reasoning step sees it.
platform: Optional platform identifier (e.g., "Telegram", "WhatsApp")
to preserve message source information.
living_ui_id: Optional Living UI project ID if user is on a Living UI page.

Returns:
``True`` if a trigger was found (queued or active), otherwise ``False``.
Expand All @@ -485,6 +487,8 @@ async def fire(
t.payload["pending_user_message"] = message
if platform:
t.payload["pending_platform"] = platform
if living_ui_id:
t.payload["living_ui_id"] = living_ui_id
found = True

if found:
Expand All @@ -500,6 +504,8 @@ async def fire(
t.payload["pending_user_message"] = message
if platform:
t.payload["pending_platform"] = platform
if living_ui_id:
t.payload["living_ui_id"] = living_ui_id
logger.debug(f"[FIRE] Attached message to active trigger for session {session_id}")
return True

Expand Down
89 changes: 89 additions & 0 deletions agent_core/core/prompts/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
"""
Application-specific prompt templates.

Contains prompt templates for Living UI and other application features.
"""

LIVING_UI_TASK_INSTRUCTION = """Create a Living UI application.

Project ID: {project_id}
Project Name: {project_name}
Description: {description}
Features: {features}
Theme: {theme}
Project Path: {project_path}

Follow the living-ui-creator skill instructions. Here's the workflow:

1. Read agent_file_system/GLOBAL_LIVING_UI.md — apply its colors, fonts, and rules
2. Phase 0: Ask the user 2+ batches of questions about data, features, design, and layout
3. Document requirements in LIVING_UI.md
4. Break the app into features, then for each feature:
- Re-read LIVING_UI.md (check what's left) and GLOBAL_LIVING_UI.md (refresh design rules)
- Write backend tests first (backend/tests/)
- Create model + routes to pass tests
- Run pytest to verify
- Create frontend types + components
- Update LIVING_UI.md — mark this feature as done, add models/routes/components you created
Do NOT skip features listed in LIVING_UI.md. A working app with all planned features is the goal.
5. Update LIVING_UI.md with implementation details
6. Call living_ui_notify_ready(project_id="{project_id}")

What a GOOD Living UI looks like:
- Professional web app layout — proper spacing, visual hierarchy, sections, headers
- Uses preset components (Button, Card, Input, Modal, Table from './components/ui') — never raw HTML
- Thoughtful layout: sidebar or top nav, content area with grid/list views, detail panels or modals
- Colors from GLOBAL_LIVING_UI.md applied consistently
- Empty state when no data — the app launches with an empty database, users create their own content
- "Add" actions open forms/modals with proper input fields — never auto-create with placeholder text
- Every item is viewable, editable, and deletable through the UI
- Error handling with toast notifications on API failures
- Responsive design that works on different screen sizes

When pytest fails:
- Read ALL errors carefully before fixing — fix ALL issues in one go, not one at a time
- If you see an import error, check ALL files for the same pattern and fix them all
- Maximum 3 pytest attempts per feature. If still failing after 3, review your approach
- Common fix: relative imports (from . import X) → absolute imports (from X import Y)

External integrations (Gmail, YouTube, Discord, Slack, etc.):
- CraftBot has connected external services — use the integration bridge, NOT custom OAuth
- Import: from services.integration_client import integration
- Call: result = await integration.request("google_workspace", "GET", url)
- NEVER build OAuth flows, ask for API keys, or store credentials
- See the "External Integrations" section in SKILL.md for details and examples

What to AVOID:
- Flat list of items with no visual structure
- Custom CSS when preset components exist
- Hardcoded test data left in the database
- Buttons that create items without user input
- Everything crammed into one component file
- Relative imports in backend code
- Running uvicorn/npm manually — the launch pipeline handles this
- Editing main.py, main.tsx, manifest.json, or tests/conftest.py — system managed
- Rewriting conftest.py — it has the correct imports and test DB setup already

Your todo list should follow this EXACT pattern — do NOT add extra sub-steps:
Phase 0: Read global config
Phase 0: Ask user batch 1 (data/features)
Phase 0: Ask user batch 2 (design/layout)
Phase 0: Document requirements in LIVING_UI.md
Phase 1: Plan features
Feature 1 - [name]: Backend (tests + model + routes + pytest)
Feature 1 - [name]: Frontend (types + components + controller)
Feature 2 - [name]: Backend (tests + model + routes + pytest)
Feature 2 - [name]: Frontend (types + components + controller)
Feature 3 - [name]: Backend (tests + model + routes + pytest)
Feature 3 - [name]: Frontend (types + components + controller)
... repeat for each feature ...
Update LIVING_UI.md with implementation details
Call living_ui_notify_ready

IMPORTANT about features:
- Each feature is a USER-FACING capability (e.g., "Board Items", "Media Attachments", "Search/Filter")
- "Backend Setup" or "Frontend Setup" are NOT features — they are layers
- Each feature MUST have BOTH backend AND frontend todos — never just one
- Keep exactly 2 todos per feature (backend + frontend) — do NOT split into 10+ sub-steps
- Write ALL tests for a feature at once, not one endpoint at a time"""
7 changes: 7 additions & 0 deletions agent_core/core/prompts/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
- The message doesn't match any existing task's context AND there are multiple active sessions
- The message appears to be a follow-up to a COMPLETED task visible in recent conversation history but NOT in existing sessions

4. LIVING UI CONTEXT:
- Messages may include a Living UI context like [Living UI: AppName (id) | Path: ...] — this means the user is viewing that specific Living UI
- If the message has a Living UI ID, PRIORITIZE routing to a task with the SAME Living UI ID
- Do NOT route a Living UI A message to a Living UI B task UNLESS the message content clearly references Living UI B (e.g., mentions it by name)
- If no task exists for the incoming Living UI, create a NEW session — do not reuse a different Living UI's task
- Generic messages like "fix this", "it's broken", "add a feature" should go to the task matching the sender's Living UI, not a different one

IMPORTANT NOTES:
- If the message has no context, it is very LIKELY it is meant for another task, DO NOT CREATE a new session
- If there is on-going task waiting for user reply, it is very LIKELY the incoming item is meant for the session
Expand Down
55 changes: 55 additions & 0 deletions agent_file_system/GLOBAL_LIVING_UI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Global Living UI Configuration

Global design preferences and rules applied to ALL Living UI projects.
Per-project settings from Phase 0 Q&A override these when they conflict.

## Design Preferences

- **Primary Color:** #6366f1
- **Secondary Color:** #8b5cf6
- **Accent Color:** #06b6d4
- **Background Style:** Default (use CraftBot design tokens)
- **Theme Mode:** Follow system (dark/light)
- **Font Family:** System default (Segoe UI, sans-serif)
- **Border Radius:** Rounded (var(--radius-md))
- **Spacing:** Comfortable

## Always Enforced

- Must use preset UI components (Button, Card, Input, Modal, Table, etc.)
- Must use design tokens from global.css (no arbitrary colors)
- All API calls must handle errors with user-visible feedback
- No inline styles for standard UI elements
- Use react-toastify for notifications (already installed)
- Backend routes must use absolute imports (not relative)
- Images must always render with visible thumbnails
- Videos must show preview thumbnails
- Links should show preview cards when possible
- Empty states must have helpful messages with action buttons
- Loading spinners required for all async operations
- Use toast notifications for all CRUD feedback (success, error)
- Show confirmation dialogs for destructive actions (delete, reset)
- Forms must have inline validation with error messages
- Mobile responsive design required
- Hover states on all clickable elements
- Text must have sufficient contrast against background (dark text on light backgrounds, light text on dark backgrounds)
- Never use light text on light backgrounds or dark text on dark backgrounds

## Optional Rules

- [x] Enable drag-and-drop for reordering items
- [x] Add keyboard shortcuts for common actions
- [x] Show item count badges on categories/sections
- [x] Add search/filter bar to all list views
- [x] Support bulk selection and batch operations
- [ ] Enable dark mode only (ignore system preference)
- [ ] Add animations and transitions to UI interactions
- [ ] Show timestamps on all items (created/updated)
- [ ] Enable infinite scroll instead of pagination
- [ ] Add undo/redo support for user actions
- [ ] Show breadcrumb navigation for nested views

## Custom Rules

<!-- Add your own rules below as checkbox lines -->
<!-- Example: - [x] All lists must support search/filter -->
3 changes: 3 additions & 0 deletions agent_file_system/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ DO NOT copy and paste events here: This memory file only stores distilled memory

## Memory

[2026-03-26 13:20:40] [system_warning] Token usage reached 80% of maximum limit (6000000 tokens)
[2026-03-26 13:22:25] [system_limit] Task cancelled due to reaching maximum token limit (6000000 tokens)

13 changes: 9 additions & 4 deletions agent_file_system/PROACTIVE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
version: "1.0"
last_updated: null # Auto-updated by system (format: YYYY-MM-DDTHH:MM:SSZ)
last_updated: 2026-03-27T22:00:00Z # Auto-updated by system (format: YYYY-MM-DDTHH:MM:SSZ)
---

# Proactive Tasks
Expand Down Expand Up @@ -178,15 +178,20 @@ No long-term goals defined yet.

### Current Focus
<!-- Updated by week/day planner -->
No current focus defined.
Supporting Living UI development projects and maintaining development workflow efficiency.

### Recent Accomplishments
<!-- Updated by planners after task completion -->
None yet.
- ✅ Successfully completed Living UI Todo Manager project with full kanban board functionality
- ✅ Implemented persistent data storage with SQLite backend
- ✅ Created responsive UI with drag-and-drop capabilities and modern design

### Upcoming Priorities
<!-- Updated by day planner -->
None defined.
- Monitor Living UI project health and performance metrics
- Proactively identify opportunities for new Living UI applications
- Maintain development environment and dependencies
- Track project completion rates and user satisfaction

---

62 changes: 60 additions & 2 deletions app/agent_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class TriggerData:
contact_id: str | None = None # Sender/chat ID from external platform
channel_id: str | None = None # Channel/group ID from external platform
payload: dict | None = None # Full trigger payload for passing extra data
living_ui_id: str | None = None # Living UI project ID if user is on a Living UI page

class AgentBase:
"""
Expand Down Expand Up @@ -681,6 +682,7 @@ def _extract_trigger_data(self, trigger: Trigger) -> TriggerData:
contact_id=payload.get("contact_id", ""),
channel_id=payload.get("channel_id", ""),
payload=payload,
living_ui_id=payload.get("living_ui_id"),
)

def _extract_user_message_from_trigger(self, trigger: Trigger) -> Optional[str]:
Expand Down Expand Up @@ -1795,6 +1797,25 @@ def _format_sessions_for_routing(

lines.append(f"Platform: {platform}")

# Add Living UI context if the user is on a Living UI page
living_ui_id = trigger.payload.get("living_ui_id") if trigger else None
if living_ui_id:
lines.append(f"Living UI ID: {living_ui_id}")
try:
from app.living_ui import get_living_ui_manager
mgr = get_living_ui_manager()
if mgr:
proj = mgr.get_project(living_ui_id)
if proj:
lines.append(f"Living UI Name: {proj.name}")
lines.append(f"Living UI Path: {proj.path}")
lines.append(f" Read {proj.path}/LIVING_UI.md for app context")
lines.append(f" If debugging issues, FIRST read these logs:")
lines.append(f" - {proj.path}/backend/logs/subprocess_output.log (crashes, stack traces)")
lines.append(f" - {proj.path}/backend/logs/frontend_console.log (frontend errors, network failures)")
except Exception:
pass

sections.append("\n".join(lines))

return "\n\n".join(sections)
Expand Down Expand Up @@ -1945,8 +1966,10 @@ async def _handle_chat_message(self, payload: Dict):
logger.info(f"[CHAT] Direct reply to session {target_session_id}")

# Fire the target trigger directly, bypassing routing LLM
living_ui_id = payload.get("living_ui_id")
fired = await self.triggers.fire(
target_session_id, message=chat_content, platform=platform
target_session_id, message=chat_content, platform=platform,
living_ui_id=living_ui_id
)

if fired:
Expand Down Expand Up @@ -2026,7 +2049,8 @@ async def _handle_chat_message(self, payload: Dict):
# and attach the new user message so react() sees it.
# This also works for active triggers (being processed).
fired = await self.triggers.fire(
matched_session_id, message=chat_content, platform=platform
matched_session_id, message=chat_content, platform=platform,
living_ui_id=payload.get("living_ui_id")
)
logger.info(
f"[CHAT] Routed message to existing session {matched_session_id} "
Expand Down Expand Up @@ -2095,6 +2119,29 @@ async def _handle_chat_message(self, payload: Dict):

# No existing triggers matched or action == "new" — create a fresh session
await self.state_manager.start_session(gui_mode)

# If user is on a Living UI page, prepend context to the message
living_ui_id = payload.get("living_ui_id")
if living_ui_id:
living_ui_context = f"[Living UI: {living_ui_id}]"
try:
from app.living_ui import get_living_ui_manager
mgr = get_living_ui_manager()
if mgr:
proj = mgr.get_project(living_ui_id)
if proj:
living_ui_context = (
f"[Living UI: {proj.name} ({living_ui_id}) | "
f"Path: {proj.path} | "
f"Read {proj.path}/LIVING_UI.md for app context]"
f" If debugging issues, FIRST read these logs:"
f" - {proj.path}/backend/logs/subprocess_output.log (crashes, stack traces)"
f" - {proj.path}/backend/logs/frontend_console.log (frontend errors, network failures)"
)
except Exception:
pass
chat_content = f"{living_ui_context}\n{chat_content}"

self.state_manager.record_user_message(chat_content, platform=platform)

# skip_merge=True because we already did routing above
Expand All @@ -2103,6 +2150,9 @@ async def _handle_chat_message(self, payload: Dict):
"platform": platform,
"user_message": chat_content, # Original user message for task event stream
}
# Carry Living UI context if user is on a Living UI page
if payload.get("living_ui_id"):
trigger_payload["living_ui_id"] = payload["living_ui_id"]
# Carry external message context for platform-aware routing
if payload.get("external_event"):
trigger_payload["is_self_message"] = payload.get("is_self_message", False)
Expand Down Expand Up @@ -3139,6 +3189,14 @@ def print_startup_step(step: int, total: int, message: str):
# Shutdown scheduler (handles all periodic tasks including memory processing)
self.is_running = False
await self.scheduler.shutdown()
# Stop all Living UI projects (kill backend/frontend processes)
try:
from app.living_ui import get_living_ui_manager
lui_mgr = get_living_ui_manager()
if lui_mgr:
await lui_mgr.stop_all_projects()
except Exception as e:
logger.warning(f"[SHUTDOWN] Living UI cleanup error: {e}")
# Gracefully shutdown MCP connections
await self._shutdown_mcp()
# Stop external communications
Expand Down
5 changes: 2 additions & 3 deletions app/config/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.2.2",
"version": "1.2.3",
"general": {
"agent_name": "CraftBot",
"os_language": "en"
Expand All @@ -25,8 +25,7 @@
"openai": "",
"anthropic": "",
"google": "",
"byteplus": "",
"deepseek": ""
"byteplus": ""
},
"endpoints": {
"remote_model_url": "",
Expand Down
Loading