Questions tab, question bubbles, answer snapshots; errors to transcript
This commit is contained in:
+24
-26
@@ -12,14 +12,6 @@ import (
|
||||
"g.fc.run/misc/byre/internal/api"
|
||||
)
|
||||
|
||||
func DeliverableResponse(t api.InboxResponseType) bool {
|
||||
switch t {
|
||||
case api.Option, api.Other:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Store) CreateInboxItem(ctx context.Context, item api.InboxItem) error {
|
||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
payload, err := json.Marshal(item.Payload)
|
||||
@@ -28,9 +20,9 @@ func (s *Store) CreateInboxItem(ctx context.Context, item api.InboxItem) error {
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO inbox_items (id, project_id, task_id, agent_session_id, thread_id, kind, payload, state, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, item.Id, item.ProjectId, item.TaskId, item.AgentSessionId, item.ThreadId, string(item.Kind),
|
||||
INSERT INTO inbox_items (id, project_id, task_id, agent_session_id, thread_id, payload, state, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, item.Id, item.ProjectId, item.TaskId, item.AgentSessionId, item.ThreadId,
|
||||
string(payload), string(item.State), formatTime(item.CreatedAt))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -220,10 +212,9 @@ func (s *Store) AwaitingHuman(ctx context.Context, sessionID string) (int, error
|
||||
n := 0
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*) FROM inbox_items i
|
||||
WHERE i.agent_session_id = ? AND i.kind = ? AND i.state = ?
|
||||
AND NOT EXISTS (SELECT 1 FROM inbox_responses r WHERE r.item_id = i.id AND r.type IN (?, ?))
|
||||
`, sessionID, string(api.Question), string(api.Open),
|
||||
string(api.Option), string(api.Other)).Scan(&n)
|
||||
WHERE i.agent_session_id = ? AND i.state = ?
|
||||
AND NOT EXISTS (SELECT 1 FROM inbox_responses r WHERE r.item_id = i.id)
|
||||
`, sessionID, string(api.Open)).Scan(&n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
@@ -235,9 +226,9 @@ type Pending struct {
|
||||
func (s *Store) Undelivered(ctx context.Context, sessionID string) ([]Pending, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT r.id, r.item_id FROM inbox_responses r JOIN inbox_items i ON i.id = r.item_id
|
||||
WHERE i.agent_session_id = ? AND r.delivered_at IS NULL AND r.type IN (?, ?)
|
||||
WHERE i.agent_session_id = ? AND r.delivered_at IS NULL
|
||||
ORDER BY r.created_at
|
||||
`, sessionID, string(api.Option), string(api.Other))
|
||||
`, sessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -274,11 +265,15 @@ func (s *Store) Undelivered(ctx context.Context, sessionID string) ([]Pending, e
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
func (s *Store) MarkDelivered(ctx context.Context, responseIDs []string) error {
|
||||
func (s *Store) MarkDelivered(ctx context.Context, responseIDs []string, snapshotN int) error {
|
||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
at := now()
|
||||
var snap any
|
||||
if snapshotN > 0 {
|
||||
snap = snapshotN
|
||||
}
|
||||
for _, id := range responseIDs {
|
||||
_, err := tx.ExecContext(ctx, `UPDATE inbox_responses SET delivered_at = ? WHERE id = ?`, at, id)
|
||||
_, err := tx.ExecContext(ctx, `UPDATE inbox_responses SET delivered_at = ?, snapshot_n = ? WHERE id = ?`, at, snap, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -290,7 +285,7 @@ func (s *Store) MarkDelivered(ctx context.Context, responseIDs []string) error {
|
||||
func (s *Store) MarkUndelivered(ctx context.Context, responseIDs []string) error {
|
||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
for _, id := range responseIDs {
|
||||
_, err := tx.ExecContext(ctx, `UPDATE inbox_responses SET delivered_at = NULL WHERE id = ?`, id)
|
||||
_, err := tx.ExecContext(ctx, `UPDATE inbox_responses SET delivered_at = NULL, snapshot_n = NULL WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -306,7 +301,6 @@ type querier interface {
|
||||
|
||||
func getItem(ctx context.Context, q querier, id string) (api.InboxItem, error) {
|
||||
item := api.InboxItem{}
|
||||
kind := ""
|
||||
payload := ""
|
||||
state := ""
|
||||
createdAt := ""
|
||||
@@ -314,16 +308,15 @@ func getItem(ctx context.Context, q querier, id string) (api.InboxItem, error) {
|
||||
resolvedAt := sql.NullString{}
|
||||
|
||||
err := q.QueryRowContext(ctx, `
|
||||
SELECT id, project_id, task_id, agent_session_id, thread_id, kind, payload, state,
|
||||
SELECT id, project_id, task_id, agent_session_id, thread_id, payload, state,
|
||||
created_at, resolved_by, resolved_at
|
||||
FROM inbox_items WHERE id = ?
|
||||
`, id).Scan(&item.Id, &item.ProjectId, &item.TaskId, &item.AgentSessionId, &item.ThreadId, &kind,
|
||||
`, id).Scan(&item.Id, &item.ProjectId, &item.TaskId, &item.AgentSessionId, &item.ThreadId,
|
||||
&payload, &state, &createdAt, &resolvedBy, &resolvedAt)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
item.Kind = api.InboxItemKind(kind)
|
||||
item.State = api.InboxState(state)
|
||||
err = json.Unmarshal([]byte(payload), &item.Payload)
|
||||
if err != nil {
|
||||
@@ -347,7 +340,7 @@ func getItem(ctx context.Context, q querier, id string) (api.InboxItem, error) {
|
||||
|
||||
func getResponses(ctx context.Context, q querier, itemID string) ([]api.InboxResponse, error) {
|
||||
rows, err := q.QueryContext(ctx, `
|
||||
SELECT id, item_id, user_login, type, options, text, images, attachments, created_at, delivered_at
|
||||
SELECT id, item_id, user_login, type, options, text, images, attachments, created_at, delivered_at, snapshot_n
|
||||
FROM inbox_responses WHERE item_id = ? ORDER BY created_at
|
||||
`, itemID)
|
||||
if err != nil {
|
||||
@@ -365,8 +358,9 @@ func getResponses(ctx context.Context, q querier, itemID string) ([]api.InboxRes
|
||||
attachments := ""
|
||||
createdAt := ""
|
||||
delivered := sql.NullString{}
|
||||
snap := sql.NullInt64{}
|
||||
|
||||
err = rows.Scan(&r.Id, &r.ItemId, &r.User, &typ, &options, &text, &images, &attachments, &createdAt, &delivered)
|
||||
err = rows.Scan(&r.Id, &r.ItemId, &r.User, &typ, &options, &text, &images, &attachments, &createdAt, &delivered, &snap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -383,6 +377,10 @@ func getResponses(ctx context.Context, q querier, itemID string) ([]api.InboxRes
|
||||
if text.Valid {
|
||||
r.Text = &text.String
|
||||
}
|
||||
if snap.Valid {
|
||||
n := int(snap.Int64)
|
||||
r.SnapshotN = &n
|
||||
}
|
||||
err = json.Unmarshal([]byte(images), &r.Images)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -62,6 +62,14 @@ func (s *Store) LastSnapshot(ctx context.Context, taskID string) (api.Snapshot,
|
||||
return snap, err
|
||||
}
|
||||
|
||||
func (s *Store) LastSnapshotNot(ctx context.Context, taskID string, trigger api.SnapshotTrigger) (api.Snapshot, error) {
|
||||
snap, err := scanSnapshot(s.db.QueryRowContext(ctx, `SELECT `+snapshotColumns+` FROM snapshots WHERE task_id = ? AND trigger != ? ORDER BY n DESC LIMIT 1`, taskID, string(trigger)))
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return snap, ErrNotFound
|
||||
}
|
||||
return snap, err
|
||||
}
|
||||
|
||||
func (s *Store) LastTaskSeq(ctx context.Context, taskID string) (int64, error) {
|
||||
seq := int64(0)
|
||||
err := s.db.QueryRowContext(ctx, `SELECT COALESCE(MAX(seq), 0) FROM events WHERE task_id = ?`, taskID).Scan(&seq)
|
||||
|
||||
@@ -479,6 +479,23 @@ ALTER TABLE users ADD COLUMN default_model TEXT NOT NULL DEFAULT 'claude-opus-5-
|
||||
`}, {"add-team-members", `
|
||||
ALTER TABLE agent_sessions ADD COLUMN member TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE task_inputs ADD COLUMN member TEXT NOT NULL DEFAULT '';
|
||||
`}, {"rename-inbox-tab", `
|
||||
UPDATE users SET focus = replace(focus, '"inbox"', '"questions"');
|
||||
`}, {"add-inbox-response-snapshots", `
|
||||
ALTER TABLE inbox_responses ADD COLUMN snapshot_n INTEGER;
|
||||
`}, {"errors-to-transcript", `
|
||||
UPDATE events SET kind = 'session.error', payload = json_object('error', json_extract(payload, '$.payload.error'))
|
||||
WHERE kind = 'inbox.created' AND json_extract(payload, '$.kind') = 'session_error';
|
||||
UPDATE events SET kind = 'session.rate_limited', payload = json_object(
|
||||
'vendor', json_extract(payload, '$.payload.vendor'),
|
||||
'rate_limit', json(json_extract(payload, '$.payload.rate_limit')),
|
||||
'error', json_extract(payload, '$.payload.error'))
|
||||
WHERE kind = 'inbox.created' AND json_extract(payload, '$.kind') = 'rate_limited';
|
||||
DELETE FROM events WHERE kind IN ('inbox.created', 'inbox.updated') AND json_extract(payload, '$.kind') != 'question';
|
||||
UPDATE events SET payload = json_remove(payload, '$.kind') WHERE kind IN ('inbox.created', 'inbox.updated');
|
||||
DELETE FROM inbox_responses WHERE item_id IN (SELECT id FROM inbox_items WHERE kind != 'question');
|
||||
DELETE FROM inbox_items WHERE kind != 'question';
|
||||
ALTER TABLE inbox_items DROP COLUMN kind;
|
||||
`}}
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user