Bold questions

This commit is contained in:
Ian Gulliver
2026-10-14 19:35:34 -07:00
parent ab4c3d2c6f
commit 1ea055682b
54 changed files with 3696 additions and 762 deletions
+65 -10
View File
@@ -125,6 +125,12 @@ CREATE TABLE turn_queue (
`, `
UPDATE inbox_responses SET type = 'other' WHERE type IN ('free_form', 'redirect', 'question');
UPDATE inbox_items SET state = 'answered' WHERE state = 'redirected';
`, `
UPDATE inbox_items SET payload = json_object('text', coalesce((SELECT group_concat('- ' || value, char(10)) FROM json_each(inbox_items.payload, '$.done')), '')) WHERE kind = 'report';
`, `
ALTER TABLE tasks RENAME COLUMN completed_at TO closed_at;
ALTER TABLE tasks ADD COLUMN merge_commit TEXT NOT NULL DEFAULT '';
UPDATE tasks SET status = 'discarded' WHERE status = 'completed';
`}
type Store struct {
@@ -311,10 +317,18 @@ func (s *Store) GetTask(ctx context.Context, id string) (api.Task, error) {
return t, err
}
func (s *Store) CompleteTask(ctx context.Context, id, actor string) (api.Task, error) {
func (s *Store) MergeTask(ctx context.Context, id, commit, actor string) (api.Task, error) {
return s.closeTask(ctx, id, api.TaskStatusMerged, commit, actor, "task.merged", map[string]any{"id": id, "commit": commit})
}
func (s *Store) DiscardTask(ctx context.Context, id, actor string) (api.Task, error) {
return s.closeTask(ctx, id, api.TaskStatusDiscarded, "", actor, "task.discarded", map[string]any{"id": id})
}
func (s *Store) closeTask(ctx context.Context, id string, status api.TaskStatus, commit, actor, kind string, payload map[string]any) (api.Task, error) {
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
res, err := tx.ExecContext(ctx, `UPDATE tasks SET status = ?, completed_at = ? WHERE id = ? AND status = ?`,
string(api.TaskStatusCompleted), now(), id, string(api.TaskStatusActive))
res, err := tx.ExecContext(ctx, `UPDATE tasks SET status = ?, closed_at = ?, merge_commit = ? WHERE id = ? AND status = ?`,
string(status), now(), commit, id, string(api.TaskStatusActive))
if err != nil {
return err
}
@@ -331,7 +345,7 @@ func (s *Store) CompleteTask(ctx context.Context, id, actor string) (api.Task, e
if err != nil {
return err
}
return emit(api.Event{Kind: "task.completed", ProjectId: &projectID, TaskId: &id, Actor: actor, Payload: map[string]any{"id": id}})
return emit(api.Event{Kind: kind, ProjectId: &projectID, TaskId: &id, Actor: actor, Payload: payload})
})
if err != nil {
return api.Task{}, err
@@ -392,6 +406,13 @@ func (s *Store) AppendEvent(ctx context.Context, ev api.Event) error {
})
}
func (s *Store) Broadcast(ev api.Event) {
s.writeMu.Lock()
defer s.writeMu.Unlock()
ev.CreatedAt = time.Now().UTC()
s.onEvent(ev)
}
func (s *Store) CreateSession(ctx context.Context, sess api.AgentSession, projectID string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
_, err := tx.ExecContext(ctx, `
@@ -495,6 +516,36 @@ func (s *Store) transitionSession(ctx context.Context, id string, from *api.Agen
})
}
func (s *Store) SetSessionModel(ctx context.Context, id, model, actor string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
taskID := ""
projectID := ""
err := tx.QueryRowContext(ctx, `
SELECT t.id, t.project_id FROM agent_sessions s JOIN tasks t ON t.id = s.task_id WHERE s.id = ?
`, id).Scan(&taskID, &projectID)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, `UPDATE agent_sessions SET model = ? WHERE id = ?`, model, id)
if err != nil {
return err
}
return emit(api.Event{
Kind: "session.model",
ProjectId: &projectID,
TaskId: &taskID,
AgentSessionId: &id,
Actor: actor,
Payload: map[string]any{"model": model},
})
})
}
type Counts struct {
LastEventSeq int64
Projects int
@@ -560,7 +611,7 @@ func (s *Store) write(ctx context.Context, fn func(tx *sql.Tx, emit emitFunc) er
const projectColumns = `id, name, repo_path, default_branch, remote, created_by, created_at`
const taskColumns = `id, project_id, title, branch, worktree_path, base_ref, status, created_by, created_at, completed_at`
const taskColumns = `id, project_id, title, branch, worktree_path, base_ref, status, created_by, created_at, closed_at, merge_commit`
const sessionColumns = `id, task_id, vendor, model, vendor_session_id, state, role, created_by, created_at`
@@ -583,8 +634,9 @@ func scanTask(row scanner) (api.Task, error) {
t := api.Task{}
status := ""
createdAt := ""
completedAt := sql.NullString{}
err := row.Scan(&t.Id, &t.ProjectId, &t.Title, &t.Branch, &t.WorktreePath, &t.BaseRef, &status, &t.CreatedBy, &createdAt, &completedAt)
closedAt := sql.NullString{}
mergeCommit := ""
err := row.Scan(&t.Id, &t.ProjectId, &t.Title, &t.Branch, &t.WorktreePath, &t.BaseRef, &status, &t.CreatedBy, &createdAt, &closedAt, &mergeCommit)
if err != nil {
return t, err
}
@@ -593,12 +645,15 @@ func scanTask(row scanner) (api.Task, error) {
if err != nil {
return t, err
}
if completedAt.Valid {
ct, err := parseTime(completedAt.String)
if closedAt.Valid {
ct, err := parseTime(closedAt.String)
if err != nil {
return t, err
}
t.CompletedAt = &ct
t.ClosedAt = &ct
}
if mergeCommit != "" {
t.MergeCommit = &mergeCommit
}
return t, nil
}