Agent handler_mode: internal/external/paused — robust meldingsruting
Tre moduser i agent_identities.handler_mode: - internal: maskinrommet kjører synops-respond (eksternt API) - external: jobb settes til 'deferred', forblir urørt for Claude Code - paused: svar bruker med "AI utilgjengelig", marker done Jobbkøen overskriver ikke deferred-status (sjekker result.status). Ny job_status 'deferred' i PG enum. Scripts: vaktmester-poll.sh (finn deferred jobber), vaktmester-complete.sh (marker behandlet). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
db7a8f4a15
commit
956fbc124c
4 changed files with 93 additions and 10 deletions
|
|
@ -55,23 +55,57 @@ pub async fn handle_agent_respond(
|
|||
|
||||
let config = load_agent_config(db, agent_node_id).await?;
|
||||
|
||||
// --- Sikkerhetskontroller (forblir i maskinrommet) ---
|
||||
// --- Handler-modus og sikkerhetskontroller ---
|
||||
|
||||
// Kill switch
|
||||
let is_active: bool = sqlx::query_scalar(
|
||||
"SELECT is_active FROM agent_identities WHERE node_id = $1",
|
||||
// Sjekk handler_mode: internal, external, disabled
|
||||
let (is_active, handler_mode): (bool, String) = sqlx::query_as::<_, (bool, String)>(
|
||||
"SELECT is_active, handler_mode FROM agent_identities WHERE node_id = $1",
|
||||
).bind(agent_node_id).fetch_optional(db).await
|
||||
.map_err(|e| format!("DB-feil: {e}"))?.unwrap_or(false);
|
||||
if !is_active {
|
||||
// La jobben ligge for ekstern handler (Claude Code polling)
|
||||
sqlx::query("UPDATE job_queue SET status = 'pending', started_at = NULL WHERE id = $1")
|
||||
.map_err(|e| format!("DB-feil: {e}"))?
|
||||
.unwrap_or((false, "disabled".to_string()));
|
||||
|
||||
// Disabled: dropp jobben
|
||||
if !is_active || handler_mode == "disabled" {
|
||||
return Ok(serde_json::json!({"status": "skipped", "reason": "agent_disabled"}));
|
||||
}
|
||||
|
||||
// External: la jobben ligge for ekstern handler (Claude Code / synops-agent)
|
||||
if handler_mode == "external" {
|
||||
sqlx::query("UPDATE job_queue SET status = 'deferred'::job_status, started_at = NULL WHERE id = $1")
|
||||
.bind(job.id)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| format!("DB-feil ved tilbakestilling: {e}"))?;
|
||||
return Ok(serde_json::json!({"status": "deferred", "reason": "agent_inactive_external_handler"}));
|
||||
tracing::info!(
|
||||
job_id = %job.id,
|
||||
"agent_respond deferred til ekstern handler (handler_mode=external)"
|
||||
);
|
||||
return Ok(serde_json::json!({"status": "deferred", "reason": "external_handler"}));
|
||||
}
|
||||
|
||||
// Paused: svar brukeren at AI er utilgjengelig
|
||||
if handler_mode == "paused" {
|
||||
let pause_msg = "AI-assistenten er midlertidig utilgjengelig. Meldingen din er registrert.";
|
||||
// Skriv svar i chatten
|
||||
sqlx::query(
|
||||
"WITH new_node AS (
|
||||
INSERT INTO nodes (id, node_kind, content, visibility, created_by)
|
||||
VALUES (gen_random_uuid(), 'content', $1, 'hidden', $2)
|
||||
RETURNING id
|
||||
)
|
||||
INSERT INTO edges (id, source_id, target_id, edge_type)
|
||||
SELECT gen_random_uuid(), id, $3, 'belongs_to' FROM new_node"
|
||||
)
|
||||
.bind(pause_msg)
|
||||
.bind(agent_node_id)
|
||||
.bind(communication_id)
|
||||
.execute(db).await
|
||||
.map_err(|e| format!("DB-feil ved pause-svar: {e}"))?;
|
||||
return Ok(serde_json::json!({"status": "paused", "reason": "agent_paused"}));
|
||||
}
|
||||
|
||||
// Internal: fortsett med synops-respond (under)
|
||||
|
||||
// Rate limiting
|
||||
let count: i64 = sqlx::query_scalar::<_, Option<i64>>(
|
||||
"SELECT COUNT(*) FROM ai_usage_log WHERE agent_node_id = $1 AND created_at > now() - interval '1 hour'",
|
||||
|
|
|
|||
|
|
@ -882,7 +882,11 @@ pub fn start_worker(
|
|||
|
||||
match result {
|
||||
Ok(Ok(res)) => {
|
||||
if let Err(e) = complete_job(&db2, job.id, res).await {
|
||||
// Deferred-jobber eies av ekstern handler — ikke marker completed
|
||||
let is_deferred = res.get("status").and_then(|s| s.as_str()) == Some("deferred");
|
||||
if is_deferred {
|
||||
tracing::info!(job_id = %job.id, "Jobb deferred til ekstern handler");
|
||||
} else if let Err(e) = complete_job(&db2, job.id, res).await {
|
||||
tracing::error!(job_id = %job.id, error = %e, "Kunne ikke markere jobb som fullført");
|
||||
} else {
|
||||
tracing::info!(job_id = %job.id, "Jobb fullført");
|
||||
|
|
|
|||
13
scripts/vaktmester-complete.sh
Executable file
13
scripts/vaktmester-complete.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# Marker en deferred jobb som completed etter at Claude Code har svart.
|
||||
# Bruk: ./scripts/vaktmester-complete.sh <job-id>
|
||||
|
||||
set -euo pipefail
|
||||
JOB_ID="${1:?Mangler job-id}"
|
||||
|
||||
docker exec sidelinja-postgres-1 psql -U sidelinja -d synops -q -c "
|
||||
UPDATE job_queue SET status = 'completed', completed_at = now(),
|
||||
result = '{\"status\":\"completed\",\"handler\":\"claude-code\"}'::jsonb
|
||||
WHERE id = '$JOB_ID';
|
||||
"
|
||||
echo "Jobb $JOB_ID markert som completed"
|
||||
32
scripts/vaktmester-poll.sh
Executable file
32
scripts/vaktmester-poll.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env bash
|
||||
# Poll for deferred agent_respond-jobber og returner info for Claude Code.
|
||||
# Brukes av Claude Code sin cron-loop.
|
||||
#
|
||||
# Output: JSON med jobb-info hvis det finnes en jobb, ellers "none"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CHAT_ID="abe2edfd-986b-45ba-8c2e-4461a8a7e480"
|
||||
|
||||
# Hent eldste deferred jobb
|
||||
JOB=$(docker exec sidelinja-postgres-1 psql -U sidelinja -d synops -t -A -c "
|
||||
SELECT row_to_json(t) FROM (
|
||||
SELECT j.id as job_id, j.payload, j.created_at,
|
||||
n.content as message,
|
||||
n.created_by as sender_id,
|
||||
(SELECT title FROM nodes WHERE id = n.created_by) as sender_name
|
||||
FROM job_queue j
|
||||
JOIN nodes n ON n.id = (j.payload->>'message_id')::uuid
|
||||
WHERE j.job_type = 'agent_respond'
|
||||
AND j.status = 'deferred'
|
||||
ORDER BY j.created_at ASC
|
||||
LIMIT 1
|
||||
) t;
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ -z "$JOB" ] || [ "$JOB" = "" ]; then
|
||||
echo "none"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "$JOB"
|
||||
Loading…
Add table
Reference in a new issue