You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit d47d39d
Browse filesBrowse the repository at this point in the historyBrowse files
.execute("UPDATE contexts SET human_version=version",[])?;
79
+
}
80
+
if table == "outbox"{
81
+
store.db.execute("UPDATE outbox SET attempted=created WHERE status IN ('sending','sent','uncertain')",[])?;
82
+
}
83
+
}
84
+
}
60
85
crate::permissions::initialize(&store)?;
61
86
Ok(store)
62
87
}
@@ -207,14 +232,20 @@ impl Store {
207
232
let ctx = m.context();
208
233
tx.execute("INSERT INTO messages VALUES(?1,?2,?3,?4,?5) ON CONFLICT(channel,ts) DO UPDATE SET context=excluded.context,revision=excluded.revision,body=excluded.body",params![m.channel,m.ts,ctx,m.revision,serde_json::to_string(m)?])?;
209
234
tx.execute("INSERT INTO contexts(id,version,bot_replies,last_human) VALUES(?1,1,0,CASE WHEN ?2 THEN '' ELSE ?3 END) ON CONFLICT(id) DO UPDATE SET version=version+1,bot_replies=CASE WHEN NOT ?2 AND ?3>last_human THEN 0 ELSE bot_replies END,last_human=CASE WHEN NOT ?2 AND ?3>last_human THEN ?3 ELSE last_human END",params![ctx,m.bot||m.deleted,m.ts])?;
235
+
if !m.bot || old.is_some(){
236
+
tx.execute(
237
+
"UPDATE contexts SET human_version=version WHERE id=?1",
238
+
[&ctx],
239
+
)?;
240
+
}
210
241
if old.is_some(){
211
242
tx.execute("UPDATE outbox SET status='cancelled',note='Source edited or deleted before delivery' WHERE status='ready' AND job_id IN (SELECT id FROM jobs WHERE json_extract(trigger,'$.channel')=?1 AND json_extract(trigger,'$.ts')=?2)",params![m.channel,m.ts])?;
212
243
}
213
244
// A root edit also changes the source snapshot of its child thread.
214
245
if old.is_some() && m.thread.is_empty(){
215
246
let thread_ctx = crate::model::context_id(&m.team,&m.channel,&m.ts);
216
247
tx.execute(
217
-
"UPDATE contexts SET version=version+1 WHERE id=?1",
248
+
"UPDATE contexts SET version=version+1,human_version=version+1 WHERE id=?1",
218
249
[thread_ctx],
219
250
)?;
220
251
}
@@ -268,7 +299,7 @@ impl Store {
268
299
/// neither a private draft nor an attempted send establishes publication.
let candidate:Option<(String,String,String,String)>=tx.query_row("SELECT id,context,trigger,note FROM jobs j WHERE status='pending' AND updated<=?1 AND NOT EXISTS(SELECT 1 FROM jobs x WHERE x.context=j.context AND x.status='running') ORDER BY created LIMIT 1",[now-a.settle_msasi64],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?))).optional()?;
381
+
let candidate:Option<(String,String,String,String)>=tx.query_row("SELECT id,context,trigger,note FROM jobs j WHERE status='pending' AND available<=?2 AND updated<=?1 AND NOT EXISTS(SELECT 1 FROM jobs x WHERE x.context=j.context AND x.status='running') ORDER BY created LIMIT 1",params![now-a.settle_ms asi64,now],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?))).optional()?;
let decisions:i64=tx.query_row("SELECT COUNT(*) FROM jobs WHERE status NOT IN ('pending','superseded','budget') AND updated>?1",[now-3600000],|r|r.get(0))?;
355
386
if decisions >= a.max_decisions_per_agent_per_hourasi64{
356
-
tx.execute("UPDATE jobs SET status='budget',note='Hourly model decision budget reached' WHERE id=?1",[id])?;
387
+
let oldest:i64=tx.query_row("SELECT MIN(updated) FROM jobs WHERE status NOT IN ('pending','superseded','budget') AND updated>?1",[now-3600000],|r|r.get(0))?;
388
+
tx.execute("UPDATE jobs SET available=?2,note='Waiting for hourly model capacity; resumes automatically' WHERE id=?1",params![id,oldest+3600001])?;
self.db.execute("UPDATE jobs SET status='superseded',updated=?1,note='Conversation changed during this turn; contribution withheld' WHERE id=?2",params![now_ms(),job.id])?;
tx.execute("INSERT INTO outbox(id,job_id,context,channel,thread,text,status,created,available,media) VALUES(?1,?2,?3,?4,?5,?6,'ready',?7,?7,?8)",params![id,job.id,job.context,job.trigger.channel,job.trigger.thread,d.text,now_ms(),serde_json::to_string(&d.media)?])?;
430
-
tx.execute(
431
-
"UPDATE contexts SET bot_replies=bot_replies+1 WHERE id=?1",
432
-
[&job.context],
433
-
)?;
434
456
}
435
457
tx.execute(
436
458
"UPDATE jobs SET status=?1,updated=?2,note=?3 WHERE id=?4",
let(total,room,last):(i64,i64,i64)=self.db.query_row("SELECT COUNT(*),COALESCE(SUM(CASE WHEN context=?1 THEN 1 ELSE 0 END),0),COALESCE(MAX(CASE WHEN context=?1 THEN created ELSE 0 END),0) FROM outbox WHERE created>?2 AND status!='failed'",params![ctx,now_ms()-3600000],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?)))?;
452
-
let burst:i64 =
453
-
self.db
454
-
.query_row("SELECT bot_replies FROM contexts WHERE id=?1",[ctx], |r| {
455
-
r.get(0)
456
-
})?;
457
-
let why = if total >= a.max_replies_per_agent_per_hourasi64{
let candidate:Option<(Outgoing,String,i64,i64,i64)>=tx.query_row("SELECT o.id,o.channel,o.thread,o.text,j.trigger,j.version,c.version,o.media,(SELECT COUNT(*) FROM outbox_parts p WHERE p.outbox_id=o.id AND p.status='sent') FROM outbox o JOIN jobs j ON o.job_id=j.id JOIN contexts c ON c.id=o.context WHERE o.status='ready' AND o.available<=?1 ORDER BY o.created LIMIT 1",[now_ms()],|r| {
474
+
let candidate:Option<(Outgoing,String,i64,i64,i64)>=tx.query_row("SELECT o.id,o.channel,o.thread,o.text,j.trigger,j.version,c.human_version,o.media,(SELECT COUNT(*) FROM outbox_parts p WHERE p.outbox_id=o.id AND p.status='sent') FROM outbox o JOIN jobs j ON o.job_id=j.id JOIN contexts c ON c.id=o.context WHERE o.status='ready' AND o.available<=?1 ORDER BY o.created LIMIT 1",[now_ms()],|r| {
473
475
let raw:String=r.get(7)?;
474
476
let media=serde_json::from_str(&raw).map_err(|e|rusqlite::Error::FromSqlConversionFailure(7,rusqlite::types::Type::Text,Box::new(e)))?;
let last:i64=tx.query_row("SELECT COALESCE(MAX(attempted),0) FROM outbox WHERE context=?1 AND status IN ('sent','sending','uncertain')",[trigger.context()],|r|r.get(0))?;
541
+
let reset = c.attention.bot_exchange_cooldown_seconds > 0
542
+
&& now - last >= c.attention.bot_exchange_cooldown_secondsasi64*1000;
543
+
tx.execute("UPDATE contexts SET bot_replies=CASE WHEN ?2 THEN 1 ELSE bot_replies+1 END WHERE id=?1",params![trigger.context(),reset])?;
544
+
}
510
545
tx.execute(
511
-
"UPDATE outbox SET status='sending',note='' WHERE id=?1 AND status='ready'",
512
-
[&out.id],
546
+
"UPDATE outbox SET status='sending',note='',attempted=CASE WHEN attempted=0 THEN ?2 ELSE attempted END WHERE id=?1 AND status='ready'",
db.execute("INSERT INTO jobs(id,context,trigger,status,created,updated,note) SELECT ?1,?2,?3,'pending',?4,?4,'Reconsider the latest conversation; an earlier turn may already have performed work. Do not repeat actions without checking the existing result.' WHERE NOT EXISTS(SELECT 1 FROM jobs WHERE context=?2 AND status IN ('pending','running'))",params![Uuid::new_v4().to_string(),m.context(),serde_json::to_string(m)?,now_ms()])?;
593
628
Ok(())
594
629
}
630
+
631
+
// Budgets are checked when reserving delivery, rather than throwing away a
632
+
// completed model turn. Retries never replay its tools or count as a new reply.
633
+
fndelivery_wait(
634
+
db:&Connection,
635
+
m:&Message,
636
+
a:&Attention,
637
+
now:i64,
638
+
) -> Result<Option<(String,Option<i64>)>>{
639
+
let ctx = m.context();
640
+
let(total,room,last,oldest,room_oldest):(i64,i64,i64,i64,i64)=db.query_row("SELECT COUNT(*),COALESCE(SUM(CASE WHEN context=?1 THEN 1 ELSE 0 END),0),COALESCE(MAX(CASE WHEN context=?1 THEN attempted ELSE 0 END),0),COALESCE(MIN(attempted),0),COALESCE(MIN(CASE WHEN context=?1 THEN attempted END),0) FROM outbox WHERE attempted>?2 AND status IN ('sent','sending','uncertain')",params![ctx,now-3600000],|r|Ok((r.get(0)?,r.get(1)?,r.get(2)?,r.get(3)?,r.get(4)?)))?;
641
+
if total >= a.max_replies_per_agent_per_hourasi64{
642
+
returnOk(Some((
643
+
"Waiting for agent reply capacity".into(),
644
+
Some(oldest + 3600001),
645
+
)));
646
+
}
647
+
if room >= a.max_replies_per_context_per_hourasi64{
648
+
returnOk(Some((
649
+
"Waiting for conversation reply capacity".into(),
650
+
Some(room_oldest + 3600001),
651
+
)));
652
+
}
653
+
let burst:i64 = db.query_row("SELECT bot_replies FROM contexts WHERE id=?1",[ctx], |r| {
654
+
r.get(0)
655
+
})?;
656
+
if m.bot && burst >= a.max_bot_replies_without_humanasi64{
657
+
if a.bot_exchange_cooldown_seconds == 0{
658
+
returnOk(Some((
659
+
"Agent exchange paused until a person responds".into(),
660
+
None,
661
+
)));
662
+
}
663
+
let until = last + a.bot_exchange_cooldown_secondsasi64*1000;
0 commit comments