release: v0.6.0 — security audit fixes + 7 new features
All checks were successful
PR Check / check (pull_request) Successful in 57s

Audit corrections (security, concurrency, stability):
- chat_engine: bound resp.Choices[0] access, release tool slot per-iteration
- conversation_multi: synchronous save under existing lock (was racy fire-and-forget)
- workflow/engine: short-circuit on failed deps (no more infinite busy-wait); track failed/skipped status
- handlers_workflow: rune-aware truncate for plan goal (UTF-8 safe)
- server: CORS limited to localhost origins (was wildcard)
- handlers_info / terminal: mask API keys and SSH passwords as "***" in GET responses; preserve stored secret if "***" sent on update
- terminal: sshpass uses -e + SSHPASS env var (was both -p and -e)
- handlers_chat: MaxBytesReader 50 MB on /api/chat
- image_cache: 10 MB cap per image
- handlers_config: font size <= 72; profile-save unmarshal errors propagated
- handlers_info: /lsp/auto-install ProjectDir restricted to user home
- Shell.jsx: parenthesized resize-condition (operator precedence)
- orchestrator_test: CleanAIResponse capitalization (fixes failing vet)

New features:
- platform: detect OS name (Debian, Ubuntu, Windows 11, macOS X.Y) and inject in Studio system prompt next to the date
- agents: default timeout 30 min for crush_run/claude_run (cap also 30 min)
- agents: new cwd, wsl_distro, wsl_user params; on Windows hosts launch via "wsl -d <distro> -u <user> --cd <cwd> --"
- agents: new claude_run tool (mirror of crush_run for Claude Code CLI)
- terminal: list installed WSL distros individually in new-tab menu (Windows only)
- studio: system prompt rewritten around BMAD-METHOD personas + mandatory delegation template
- studio: "Réflexion avancée" toggle — inactive provider produces a preliminary report injected as [RAPPORT PRÉALABLE] context for the active provider
- studio: "Historique compressé" toggle — collapses past tool calls to last action only, with "Tout afficher" expansion
This commit is contained in:
Muyue
2026-04-27 10:12:11 +02:00
parent 0753167fb9
commit 6a7b4d8001
22 changed files with 804 additions and 145 deletions

View File

@@ -225,17 +225,21 @@ func (e *Engine) Execute(ctx context.Context, workflowID string, onStep func(ste
stepStatuses[step.ID] = StatusPending
}
resolveDeps := func(stepID string) bool {
resolveDeps := func(stepID string) (ready bool, blocked bool) {
step := wf.findStep(stepID)
if step == nil {
return false
return false, true
}
for _, dep := range step.DependsOn {
if stepStatuses[dep] != StatusDone {
return false
depStatus := stepStatuses[dep]
if depStatus == StatusFailed || depStatus == StatusSkipped {
return false, true
}
if depStatus != StatusDone {
return false, false
}
}
return true
return true, false
}
executeStep := func(step *Step) error {
@@ -296,6 +300,7 @@ func (e *Engine) Execute(ctx context.Context, workflowID string, onStep func(ste
s.Error = stepErr.Error()
s.EndedAt = &endTime
})
stepStatuses[step.ID] = StatusFailed
if onStep != nil {
onStep(step, "failed")
}
@@ -321,8 +326,27 @@ func (e *Engine) Execute(ctx context.Context, workflowID string, onStep func(ste
continue
}
for !resolveDeps(step.ID) {
time.Sleep(100 * time.Millisecond)
ready, blocked := resolveDeps(step.ID)
if blocked {
e.UpdateStep(workflowID, step.ID, func(s *Step) {
s.Status = StatusSkipped
})
stepStatuses[step.ID] = StatusSkipped
if onStep != nil {
onStep(&step, "skipped")
}
continue
}
if !ready {
e.UpdateStep(workflowID, step.ID, func(s *Step) {
s.Status = StatusSkipped
s.Error = "dependency not satisfied at execution time"
})
stepStatuses[step.ID] = StatusSkipped
if onStep != nil {
onStep(&step, "skipped")
}
continue
}
if err := executeStep(&step); err != nil {