diff --git a/internal/app/app.go b/internal/app/app.go index 0b5c72c..27c3c04 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -83,14 +83,14 @@ func New(configPath, version, updateToken string) (*App, error) { // OpenCode runner — один на аналитика и воркер ocRunner := &opencode.Runner{ - Bin: cfg.OpenCode.Bin, - DBPath: cfg.OpenCode.DBPath, - Config: cfg.OpenCode.Config, - ConfigDir: cfg.OpenCode.ConfigDir, - IdleTimeout: cfg.OpenCode.IdleTimeout.Duration(), - HardTimeout: cfg.OpenCode.HardTimeout.Duration(), + Bin: cfg.OpenCode.Bin, + DBPath: cfg.OpenCode.DBPath, + Config: cfg.OpenCode.Config, + ConfigDir: cfg.OpenCode.ConfigDir, + IdleTimeout: cfg.OpenCode.IdleTimeout.Duration(), + HardTimeout: cfg.OpenCode.HardTimeout.Duration(), PollInterval: cfg.OpenCode.PollMs.Duration(), - Stdout: os.Stderr, + Stdout: os.Stderr, } // Analyst (Decider) @@ -266,6 +266,21 @@ func (a *App) handleIncoming(inc chat.Incoming) { return } + // Активная задача есть, но воркер её выполняет (running). Обычный ввод и команды + // редактирования НЕ должны переводить задачу обратно в collecting — это невалидный + // переход S3 (running → collecting). Разрешено лишь терминальное /cancel и /status N. + if task.Status == storage.StatusRunning { + switch { + case cmdName(text) == "/cancel": + // переходим на терминальный статус + case cmdName(text) == "/status" && hasArg(text): + // запрос статуса конкретной задачи — безопасно, не трогает машину состояний + default: + a.send(ctx, uid, "⏳ Задача ещё выполняется воркером. Дождитесь результата, или /cancel чтобы остановить.") + return + } + } + // Активная задача есть — обработка result, err := a.CoreCtx.ProcessTurn(ctx, task.ID, text) if err != nil { @@ -475,4 +490,4 @@ func ensureAgentsDir(cfg *config.Config) error { } log.Printf("app: agents ensured in %s", cfg.OpenCode.ConfigDir) return nil -} \ No newline at end of file +}