2026-10-15 18:33:18 -07:00
package store
import (
"context"
"database/sql"
"encoding/json"
2026-10-18 08:48:57 -07:00
"errors"
2026-10-15 18:33:18 -07:00
"time"
)
type TaskInput struct {
ID int64
TaskID string
2026-10-18 08:48:57 -07:00
Member string
2026-10-15 18:33:18 -07:00
Actor string
Text string
Images [] string
2026-10-20 15:22:50 -07:00
SendID string
2026-10-15 18:33:18 -07:00
CreatedAt time . Time
}
2026-10-17 15:33:59 -07:00
func ( s * Store ) AddTaskInput ( ctx context . Context , in TaskInput , projectID , sessionID string ) error {
2026-10-15 18:33:18 -07:00
images , err := json . Marshal ( append ([] string {}, in . Images ... ))
if err != nil {
return err
}
2026-10-20 15:22:50 -07:00
if in . SendID == "" {
in . SendID = NewID ()
}
payload := map [ string ] any { "text" : in . Text , "send_id" : in . SendID }
2026-10-17 15:33:59 -07:00
if len ( in . Images ) > 0 {
payload [ "images" ] = in . Images
}
2026-10-15 18:33:18 -07:00
return s . write ( ctx , func ( tx * sql . Tx , emit emitFunc ) error {
2026-10-20 15:22:50 -07:00
res , err := tx . ExecContext ( ctx , `INSERT INTO task_inputs (task_id, member, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)` ,
in . TaskID , in . Member , in . Actor , in . Text , string ( images ), in . SendID , now ())
2026-10-17 15:33:59 -07:00
if err != nil {
return err
}
2026-10-17 15:45:27 -07:00
payload [ "id" ], err = res . LastInsertId ()
if err != nil {
return err
}
2026-10-17 15:33:59 -07:00
return emit ( sessionEvent ( "user.queued" , projectID , in . TaskID , "" , sessionID , in . Actor , payload ))
2026-10-15 18:33:18 -07:00
})
}
2026-10-20 15:47:29 -07:00
func ( s * Store ) SendSession ( ctx context . Context , sendID string ) ( string , bool , error ) {
sessionID := sql . NullString {}
err := s . db . QueryRowContext ( ctx , `SELECT agent_session_id FROM events WHERE kind IN ('user.queued', 'user.message') AND json_extract(payload, '$.send_id') = ? LIMIT 1` , sendID ). Scan ( & sessionID )
if err == nil {
return sessionID . String , true , nil
}
if ! errors . Is ( err , sql . ErrNoRows ) {
return "" , false , err
}
err = s . db . QueryRowContext ( ctx , `SELECT 1 FROM lead_inputs WHERE send_id = ? LIMIT 1` , sendID ). Scan ( new ( int ))
if errors . Is ( err , sql . ErrNoRows ) {
return "" , false , nil
}
return "" , err == nil , err
}
2026-10-18 08:48:57 -07:00
func ( s * Store ) WithdrawTaskInput ( ctx context . Context , taskID string , id int64 , projectID , actor string ) ( string , error ) {
sessionID := ""
err := s . write ( ctx , func ( tx * sql . Tx , emit emitFunc ) error {
member := ""
err := tx . QueryRowContext ( ctx , `DELETE FROM task_inputs WHERE id = ? AND task_id = ? RETURNING member` , id , taskID ). Scan ( & member )
if errors . Is ( err , sql . ErrNoRows ) {
return ErrNotFound
}
2026-10-17 15:45:27 -07:00
if err != nil {
return err
}
2026-10-18 08:48:57 -07:00
err = tx . QueryRowContext ( ctx , `SELECT id FROM agent_sessions WHERE task_id = ? AND member = ? AND role = 'implementer' ORDER BY created_at DESC LIMIT 1` , taskID , member ). Scan ( & sessionID )
2026-10-17 15:45:27 -07:00
if err != nil {
return err
}
return emit ( sessionEvent ( "user.unqueued" , projectID , taskID , "" , sessionID , actor , map [ string ] any { "id" : id }))
})
2026-10-18 08:48:57 -07:00
return sessionID , err
2026-10-17 15:45:27 -07:00
}
2026-10-18 08:48:57 -07:00
func ( s * Store ) TaskInputs ( ctx context . Context , taskID , member string ) ([] TaskInput , error ) {
2026-10-20 15:22:50 -07:00
rows , err := s . db . QueryContext ( ctx , `SELECT id, task_id, member, actor, text, images, send_id, created_at FROM task_inputs WHERE task_id = ? AND member = ? ORDER BY id` , taskID , member )
2026-10-15 18:33:18 -07:00
if err != nil {
return nil , err
}
defer rows . Close ()
inputs := [] TaskInput {}
for rows . Next () {
in := TaskInput {}
images := ""
createdAt := ""
2026-10-20 15:22:50 -07:00
err = rows . Scan ( & in . ID , & in . TaskID , & in . Member , & in . Actor , & in . Text , & images , & in . SendID , & createdAt )
2026-10-15 18:33:18 -07:00
if err != nil {
return nil , err
}
err = json . Unmarshal ([] byte ( images ), & in . Images )
if err != nil {
return nil , err
}
in . CreatedAt , err = parseTime ( createdAt )
if err != nil {
return nil , err
}
inputs = append ( inputs , in )
}
return inputs , rows . Err ()
}
2026-10-16 14:18:40 -07:00
func ( s * Store ) RestoreTaskInputs ( ctx context . Context , inputs [] TaskInput ) error {
return s . write ( ctx , func ( tx * sql . Tx , emit emitFunc ) error {
for _ , in := range inputs {
images , err := json . Marshal ( append ([] string {}, in . Images ... ))
if err != nil {
return err
}
2026-10-20 15:22:50 -07:00
_ , err = tx . ExecContext ( ctx , `INSERT INTO task_inputs (id, task_id, member, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` ,
in . ID , in . TaskID , in . Member , in . Actor , in . Text , string ( images ), in . SendID , formatTime ( in . CreatedAt ))
2026-10-16 14:18:40 -07:00
if err != nil {
return err
}
}
return nil
})
}
2026-10-15 18:33:18 -07:00
func ( s * Store ) DeleteTaskInputs ( ctx context . Context , ids [] int64 ) error {
return s . write ( ctx , func ( tx * sql . Tx , emit emitFunc ) error {
for _ , id := range ids {
_ , err := tx . ExecContext ( ctx , `DELETE FROM task_inputs WHERE id = ?` , id )
if err != nil {
return err
}
}
return nil
})
}