summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/executor/executor.go6
-rw-r--r--internal/storage/db.go12
-rw-r--r--web/app.js11
3 files changed, 26 insertions, 3 deletions
diff --git a/internal/executor/executor.go b/internal/executor/executor.go
index bf209b7..475d150 100644
--- a/internal/executor/executor.go
+++ b/internal/executor/executor.go
@@ -28,6 +28,7 @@ type Store interface {
UpdateTaskQuestion(taskID, questionJSON string) error
UpdateTaskSummary(taskID, summary string) error
AppendTaskInteraction(taskID string, interaction task.Interaction) error
+ UpdateTaskAgent(id string, agent task.AgentConfig) error
}
// LogPather is an optional interface runners can implement to provide the log
@@ -435,6 +436,11 @@ func (p *Pool) execute(ctx context.Context, t *task.Task) {
}
}
+ // Persist the assigned agent (and model) to the database before running.
+ if err := p.store.UpdateTaskAgent(t.ID, t.Agent); err != nil {
+ p.logger.Error("failed to persist agent config", "error", err, "taskID", t.ID)
+ }
+
agentType := t.Agent.Type
if agentType == "" {
agentType = "claude"
diff --git a/internal/storage/db.go b/internal/storage/db.go
index b8a7085..043009c 100644
--- a/internal/storage/db.go
+++ b/internal/storage/db.go
@@ -251,6 +251,18 @@ func (s *DB) ResetTaskForRetry(id string) (*task.Task, error) {
return t, nil
}
+// UpdateTaskAgent updates only the agent configuration of a task.
+func (s *DB) UpdateTaskAgent(id string, agent task.AgentConfig) error {
+ configJSON, err := json.Marshal(agent)
+ if err != nil {
+ return fmt.Errorf("marshaling agent config: %w", err)
+ }
+ now := time.Now().UTC()
+ _, err = s.db.Exec(`UPDATE tasks SET config_json = ?, updated_at = ? WHERE id = ?`,
+ string(configJSON), now, id)
+ return err
+}
+
// RejectTask sets a task's state to PENDING and stores the rejection comment.
func (s *DB) RejectTask(id, comment string) error {
now := time.Now().UTC()
diff --git a/web/app.js b/web/app.js
index 6bcdf57..bca41fa 100644
--- a/web/app.js
+++ b/web/app.js
@@ -456,8 +456,11 @@ function renderAllPanel(tasks) {
// ── Run action ────────────────────────────────────────────────────────────────
-async function runTask(taskId) {
- const res = await fetch(`${API_BASE}/api/tasks/${taskId}/run`, { method: 'POST' });
+async function runTask(taskId, agent) {
+ const url = agent && agent !== 'auto'
+ ? `${API_BASE}/api/tasks/${taskId}/run?agent=${agent}`
+ : `${API_BASE}/api/tasks/${taskId}/run`;
+ const res = await fetch(url, { method: 'POST' });
if (!res.ok) {
let msg = `HTTP ${res.status}`;
try { const body = await res.json(); msg = body.error || body.message || msg; } catch {}
@@ -467,6 +470,8 @@ async function runTask(taskId) {
}
async function handleRun(taskId, btn, footer) {
+ const agentSelector = document.getElementById('select-agent');
+ const agent = agentSelector ? agentSelector.value : 'auto';
btn.disabled = true;
btn.textContent = 'Queuing…';
@@ -475,7 +480,7 @@ async function handleRun(taskId, btn, footer) {
if (prev) prev.remove();
try {
- await runTask(taskId);
+ await runTask(taskId, agent);
// Refresh active panel so state flips to QUEUED
await poll();
} catch (err) {