Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
95458ea
Fix sandbox image permissions for arbitrary users
Mar 13, 2026
f30f0c2
chore: raise circuit breaker defaults and optimize coding image
Mar 14, 2026
6ec415b
fix(sandbox): force system gradle with wrapper fallback and timeout
Mar 14, 2026
7a829d9
fix(sandbox): keep gradlew and stabilize wrapper cache path
Mar 14, 2026
053b939
chore(sandbox): use workspace gradle cache path
Mar 14, 2026
a72aa15
chore(sandbox): move gradle cache out of worktrees
Mar 14, 2026
1f80ab2
docs: add java gradle sandbox design and plan
Mar 19, 2026
7fdbfc6
chore: ignore local worktrees
Mar 19, 2026
a10fcec
feat: expand sandbox java version detection
Mar 19, 2026
b2bcbfa
feat: add sandbox java override and defaulting
Mar 19, 2026
a968add
feat: detect java mismatch retry conditions
Mar 19, 2026
989454c
feat: prewarm offline gradle cache in sandbox image
Mar 19, 2026
9cc499d
test: add sandbox java fixture coverage
Mar 19, 2026
4d9ef72
merge: integrate java gradle sandbox offline support
Mar 19, 2026
688480c
fix(project): default sandbox image to coding image
Mar 19, 2026
f2f285a
docs: plan coding and testing convergence
Mar 19, 2026
8f42207
fix(worker): tighten coding and test stage guardrails
Mar 19, 2026
0fa8ef9
fix(worker): add convergent continuation prompts
Mar 19, 2026
d180f82
feat(tasks): add task clone endpoint
Mar 19, 2026
028f24d
fix(worker): tighten coding and test turn budgets
Mar 19, 2026
befdedd
docs: plan exploration budget convergence
Mar 19, 2026
c45e48c
fix(worker): force convergence after exploration drift
Mar 19, 2026
5611d6d
feat(web): surface continuation markers in react timeline
Mar 19, 2026
499145f
docs: plan stage reset and preflight optimization
Mar 19, 2026
5f04a5f
feat(worker): reset stage context after exploration drift
Mar 19, 2026
f8a343a
docs: plan static context token optimization
Mar 19, 2026
9098814
config(worker): tune role model routing defaults
Mar 19, 2026
c9ef94b
fix(worker): make signoff text only by default
Mar 19, 2026
851b6c3
refactor(worker): prune stage tool and skill exposure
Mar 19, 2026
5812113
feat(worker): slim execution stage context payloads
Mar 19, 2026
43a3962
fix(worker): prefer compact restart continuations
Mar 19, 2026
7763091
refactor(worker): aggressively slim execution context
Mar 19, 2026
63719c3
feat(worker): tighten execution context and preflight summaries
Mar 19, 2026
30fd109
chore: drop plan docs from branch
Mar 19, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ platform/htmlcov/
platform/coverage.xml
platform/**/*.cover
platform/**/*.py,cover

# Local git worktrees
.worktrees/
1 change: 1 addition & 0 deletions platform/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ LLM_API_KEY=your-api-key-here
LLM_BASE_URL=https://api.openai.com
LLM_MODEL=gpt-4o-mini
LLM_TIMEOUT=120.0
LLM_ROLE_MODEL_MAP={"orchestrator":"gpt-4o-mini","test":"gpt-4o-mini"}
EXTRA_SKILL_DIR_WHITELIST=

# SkillKit compatibility env vars.
Expand Down
8 changes: 8 additions & 0 deletions platform/app/api/v1/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,17 @@
task = await service.cancel_task(task_id)
if task is None:
raise HTTPException(status_code=404, detail="Task not found")
return task

Check warning on line 95 in platform/app/api/v1/tasks.py

View workflow job for this annotation

GitHub Actions / backend-test

Missing coverage

Missing coverage on line 95


@router.post("/{task_id}/clone", response_model=TaskDetailResponse, status_code=201)
async def clone_task(task_id: str, service: TaskService = Depends(get_task_service)):
task = await service.clone_task(task_id)
if task is None:
raise HTTPException(status_code=404, detail="Task not found")
return task

Check warning on line 103 in platform/app/api/v1/tasks.py

View workflow job for this annotation

GitHub Actions / backend-test

Missing coverage

Missing coverage on lines 101-103


@router.post("/{task_id}/retry", response_model=TaskDetailResponse)
async def retry_task(task_id: str, service: TaskService = Depends(get_task_service)):
task = await service.retry_task(task_id)
Expand Down
18 changes: 14 additions & 4 deletions platform/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ class Settings(BaseSettings):
LLM_TIMEOUT: float = 120.0

# Per-role model routing (JSON string: {"coding": "gpt-4o", "review": "claude-sonnet-4-20250514"})
# Unspecified roles fall back to LLM_MODEL
LLM_ROLE_MODEL_MAP: str = "{}"
# Unspecified roles fall back to LLM_MODEL. Keep lightweight defaults on
# orchestrator/test so parse + signoff stay cheaper unless env overrides them.
LLM_ROLE_MODEL_MAP: str = '{"orchestrator":"gpt-4o-mini","test":"gpt-4o-mini"}'
# Comma-separated absolute path prefixes allowed in agent config `extra_skill_dirs`.
# Empty means only built-in platform/skills directory is allowed.
EXTRA_SKILL_DIR_WHITELIST: str = ""
Expand All @@ -38,9 +39,12 @@ class Settings(BaseSettings):
DB_POOL_TIMEOUT: int = 30

# Circuit breaker configuration
CB_MAX_TOKENS_PER_TASK: int = 200000
CB_MAX_COST_PER_TASK_RMB: float = 50.0
CB_MAX_TOKENS_PER_TASK: int = 400000
CB_MAX_COST_PER_TASK_RMB: float = 100.0
CB_TOKEN_PRICE_PER_1K: float = 0.01
# Per-stage token budgets (JSON string). Empty means disabled.
# Example: {"parse": 60000, "code": 250000}
CB_STAGE_TOKEN_BUDGETS: str = '{"parse": 60000, "code": 250000}'

# Webhook secrets (empty = skip verification)
JIRA_WEBHOOK_SECRET: str = ""
Expand Down Expand Up @@ -96,6 +100,12 @@ class Settings(BaseSettings):
SANDBOX_ROLES: str = '["coding", "test"]'
SANDBOX_DUMP_MODEL_API_RESPONSE: bool = True
SANDBOX_MODEL_API_RAW_LOG_HOST_DIR: str = "/tmp/silicon_agent/model_api_logs"
SANDBOX_GRADLE_CMD_TIMEOUT_SECONDS: int = 480
SANDBOX_GRADLE_CACHE_HOST_DIR: str = "/var/lib/silicon_agent/gradle-cache"
SANDBOX_GRADLE_USER_HOME: str = "/var/lib/silicon_agent/gradle-cache"
SANDBOX_DEFAULT_JAVA_VERSION: int = 8
SANDBOX_GRADLE_WRAPPER_PREWARM: bool = True
SANDBOX_GRADLE_WRAPPER_PREWARM_TIMEOUT_SECONDS: int = 180

# Memory & compression configuration
MEMORY_ENABLED: bool = True
Expand Down
2 changes: 1 addition & 1 deletion platform/app/services/agent_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,11 @@
for role, _ in AGENT_ROLES:
model = (
role_model_map.get(role)
or FALLBACK_ROLE_DEFAULT_MODELS.get(role)
or settings.LLM_MODEL
or FALLBACK_ROLE_DEFAULT_MODELS.get(role)
)
if model not in available_models and available_models:
if settings.LLM_MODEL in available_models:

Check warning on line 161 in platform/app/services/agent_service.py

View workflow job for this annotation

GitHub Actions / backend-test

Missing coverage

Missing coverage on line 161
model = settings.LLM_MODEL
else:
model = available_models[0]
Expand Down
3 changes: 2 additions & 1 deletion platform/app/services/project_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.models.project import ProjectModel
from app.schemas.project import (
ProjectCreateRequest,
Expand Down Expand Up @@ -75,7 +76,7 @@ async def create_project(self, request: ProjectCreateRequest) -> ProjectResponse
repo_local_path=request.repo_local_path,
branch=request.branch,
description=request.description,
sandbox_image=request.sandbox_image,
sandbox_image=request.sandbox_image or settings.SANDBOX_IMAGE,
)
self.session.add(project)
await self.session.commit()
Expand Down
18 changes: 18 additions & 0 deletions platform/app/services/task_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ async def create_task(self, request: TaskCreateRequest) -> TaskDetailResponse:
task = result.scalar_one()
return self._task_to_response(task)

async def clone_task(self, task_id: str) -> Optional[TaskDetailResponse]:
"""Create a new task by copying only safe creation fields from a source task."""
source_task = await self._load_task_with_relations_optional(task_id)
if source_task is None:
return None

return await self.create_task(
TaskCreateRequest(
jira_id=source_task.jira_id,
title=source_task.title,
description=source_task.description,
template_id=source_task.template_id,
project_id=source_task.project_id,
yunxiao_task_id=source_task.yunxiao_task_id,
github_issue_number=getattr(source_task, "github_issue_number", None),
)
)

async def get_task(self, task_id: str) -> Optional[TaskDetailResponse]:
result = await self.session.execute(
select(TaskModel)
Expand Down
8 changes: 4 additions & 4 deletions platform/app/worker/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
ROLE_TOOLS: dict[str, set[str]] = {
"orchestrator": {"read", "execute", "skill"},
"spec": {"read", "write", "edit", "skill"},
"coding": {"read", "write", "edit", "execute", "execute_script", "skill"},
"test": {"read", "write", "edit", "execute", "execute_script", "skill"},
"coding": {"read", "write", "edit", "execute", "execute_script"},
"test": {"read", "write", "edit", "execute", "execute_script"},
"review": {"read", "execute", "skill"},
"smoke": {"read", "execute", "skill"},
"doc": {"read", "write", "edit", "skill"},
Expand All @@ -57,8 +57,8 @@
_ROLE_SKILL_DIRS: dict[str, list[str]] = {
"orchestrator": ["shared", "orchestrator"],
"spec": ["shared", "spec"],
"coding": ["shared", "coding"],
"test": ["shared", "test"],
"coding": [],
"test": [],
"review": ["shared", "review"],
"smoke": ["shared", "smoke"],
"doc": ["shared", "doc"],
Expand Down
2 changes: 1 addition & 1 deletion platform/app/worker/compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Fallback truncation limits when LLM is unavailable
_L0_FALLBACK_CHARS = 200
_L1_FALLBACK_CHARS = 1500
_L2_MAX_CHARS = 20_000 # Hard cap on full-text prior output to prevent token explosion
_L2_MAX_CHARS = 4_000 # Hard cap on full-text prior output to prevent token explosion


@dataclass
Expand Down
Loading
Loading