blob: a3301a9b0776230638399d923149fe3d470441cb [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use super::{invalid, selection::Selection};
use crate::StoreError;
use maka_agent_facts::event::Fact;
use maka_agent_facts::event::RuntimeEvent;
use sqlx::SqliteConnection;
pub(crate) async fn current_opening(
connection: &mut SqliteConnection,
session: &str,
current: Option<&str>,
) -> Result<Option<(i64, RuntimeEvent)>, StoreError> {
let Some(id) = current else {
return Ok(None);
};
let row: Option<(i64, String)> = sqlx::query_as(
"SELECT sequence, event_json FROM runtime_events e WHERE invocation_id = ?
AND kind = 'invocation_opened' AND json_extract(event_json, '$.invocation.session_id') = ?
AND NOT EXISTS(SELECT 1 FROM runtime_events t WHERE t.invocation_id = e.invocation_id AND t.kind = 'invocation_ended')",
).bind(id).bind(session).fetch_optional(connection).await?;
let (sequence, json) =
row.ok_or_else(|| invalid("current invocation is not live in this Session"))?;
Ok(Some((sequence, serde_json::from_str(&json)?)))
}
/// Checks all old effects, including hidden children and failed-but-unknown runs.
pub(crate) async fn require_safe(
connection: &mut SqliteConnection,
session: &str,
current: Option<&str>,
) -> Result<(), StoreError> {
require_safe_through(connection, session, current, i64::MAX as u64).await
}
/// Historical observations cannot borrow a later settlement or inherit later work.
/// NOT MATERIALIZED keeps the cut in the indexed queries rather than copying the ledger.
pub(crate) async fn require_safe_through(
connection: &mut SqliteConnection,
session: &str,
current: Option<&str>,
through: u64,
) -> Result<(), StoreError> {
require_safe_through_policy(connection, session, current, through, false).await
}
/// An explicit new message may observe a sealed prior tool dispatch with no
/// result. This never authorizes replay, continuation, or a terminal outcome.
pub(crate) async fn require_manual_message_safe(
connection: &mut SqliteConnection,
session: &str,
current: Option<&str>,
) -> Result<bool, StoreError> {
require_safe_through_policy(connection, session, current, i64::MAX as u64, true).await?;
let pending: bool = sqlx::query_scalar(
"SELECT EXISTS(SELECT 1 FROM runtime_events d JOIN runtime_events end
ON end.invocation_id=d.invocation_id AND end.kind='invocation_ended'
WHERE d.kind='tool_dispatched'
AND json_extract(d.event_json, '$.invocation.session_id')=?
AND json_extract(end.event_json, '$.fact.outcome.class')='outcome_unknown'
AND NOT EXISTS(SELECT 1 FROM runtime_events t WHERE t.invocation_id=d.invocation_id
AND t.operation_id=d.operation_id AND t.kind='tool_settled'))",
)
.bind(session)
.fetch_one(connection)
.await?;
Ok(pending)
}
async fn require_safe_through_policy(
connection: &mut SqliteConnection,
session: &str,
current: Option<&str>,
through: u64,
allow_sealed_unknown: bool,
) -> Result<(), StoreError> {
let unsafe_history: bool = sqlx::query_scalar(concat!(
"WITH runtime_events AS NOT MATERIALIZED (SELECT * FROM main.runtime_events WHERE sequence <= ?3), ",
crate::model_items::accepted_calls!(), "
SELECT EXISTS(SELECT 1 FROM runtime_events e
WHERE json_extract(e.event_json, '$.invocation.session_id') = ?1
AND (e.kind = 'invocation_opened' OR e.kind = 'tool_dispatched'
OR e.kind = 'model_requested' OR e.kind = 'model_completed')
AND (?2 IS NULL OR e.invocation_id != ?2) AND (
(e.kind = 'invocation_opened' AND NOT EXISTS(SELECT 1 FROM runtime_events t
WHERE t.invocation_id = e.invocation_id AND t.kind = 'invocation_ended'))
OR (e.kind = 'tool_dispatched' AND NOT EXISTS(SELECT 1 FROM runtime_events t
WHERE t.invocation_id = e.invocation_id AND t.operation_id = e.operation_id AND t.kind = 'tool_settled')
AND (?4 = 0 OR NOT EXISTS(SELECT 1 FROM runtime_events terminal
WHERE terminal.invocation_id = e.invocation_id AND terminal.kind = 'invocation_ended'
AND json_extract(terminal.event_json, '$.fact.outcome.class') = 'outcome_unknown')))
OR (e.kind = 'model_requested' AND NOT EXISTS(SELECT 1 FROM runtime_events t
WHERE t.invocation_id = e.invocation_id AND t.operation_id = e.operation_id AND t.kind IN ('model_completed','model_interrupted')))
OR (e.kind = 'model_requested' AND EXISTS(SELECT 1 FROM accepted_calls p
WHERE p.invocation_id=e.invocation_id AND p.step_id=e.operation_id AND json_extract(p.call, '$.provider_executed') = 0
AND NOT EXISTS(SELECT 1 FROM runtime_events d WHERE d.invocation_id = e.invocation_id
AND d.operation_id = e.operation_id || ':' || json_extract(p.call, '$.id')
AND d.kind IN ('tool_dispatched','tool_rejected'))))
OR (e.kind = 'model_completed' AND EXISTS(SELECT 1 FROM json_each(e.event_json, '$.fact.output.parts') p
WHERE json_extract(p.value, '$.kind') = 'tool_call' AND json_extract(p.value, '$.call.provider_executed') = 1
AND NOT EXISTS(SELECT 1 FROM json_each(e.event_json, '$.fact.output.parts') result
WHERE json_extract(result.value, '$.kind') = 'tool_result'
AND json_extract(result.value, '$.id') = json_extract(p.value, '$.call.id')
AND json_extract(result.value, '$.name') = json_extract(p.value, '$.call.name'))))))",
)).bind(session).bind(current).bind(through as i64).bind(allow_sealed_unknown).fetch_one(&mut *connection).await?;
if unsafe_history {
return Err(StoreError::HistoryUnsettled);
}
// An interrupted model step closes the request, not an observed remote
// effect. Only that step's durably observed matching result settles it.
let unknown_provider_effect: bool = sqlx::query_scalar(
"WITH runtime_events AS NOT MATERIALIZED (SELECT * FROM main.runtime_events WHERE sequence <= ?3)
SELECT EXISTS(SELECT 1 FROM runtime_events interrupted JOIN runtime_events call
ON call.invocation_id = interrupted.invocation_id AND call.kind = 'model_observed'
AND json_extract(call.event_json, '$.fact.step_id') = json_extract(interrupted.event_json, '$.fact.step_id')
AND call.sequence < interrupted.sequence
WHERE interrupted.kind = 'model_interrupted'
AND json_extract(interrupted.event_json, '$.invocation.session_id') = ?1
AND (?2 IS NULL OR interrupted.invocation_id != ?2)
AND json_extract(call.event_json, '$.fact.event.kind') = 'tool_call'
AND json_extract(call.event_json, '$.fact.event.data.provider_executed') = 1
AND NOT EXISTS(SELECT 1 FROM runtime_events result WHERE result.invocation_id = call.invocation_id
AND result.kind = 'model_observed' AND result.sequence > call.sequence AND result.sequence < interrupted.sequence
AND json_extract(result.event_json, '$.fact.step_id') = json_extract(call.event_json, '$.fact.step_id')
AND json_extract(result.event_json, '$.fact.event.kind') = 'provider_tool_result'
AND json_extract(result.event_json, '$.fact.event.data.id') = json_extract(call.event_json, '$.fact.event.data.id')
AND json_extract(result.event_json, '$.fact.event.data.name') = json_extract(call.event_json, '$.fact.event.data.name')))",
).bind(session).bind(current).bind(through as i64).fetch_one(&mut *connection).await?;
if unknown_provider_effect {
return Err(StoreError::HistoryProviderEffectUnresolved);
}
let broken_payload: bool = sqlx::query_scalar(
"WITH runtime_events AS NOT MATERIALIZED (SELECT * FROM main.runtime_events WHERE sequence <= ?2)
SELECT EXISTS(SELECT 1 FROM runtime_events e LEFT JOIN tool_result_payloads p ON p.event_id = e.event_id
WHERE json_extract(e.event_json, '$.invocation.session_id') = ?1
AND e.kind = 'tool_settled' AND json_extract(e.event_json, '$.fact.outcome.kind') = 'succeeded'
AND (p.event_id IS NULL OR length(p.payload) != COALESCE(json_extract(e.event_json, '$.fact.outcome.raw.bytes'), 0)))",
).bind(session).bind(through as i64).fetch_one(connection).await?;
if broken_payload {
return Err(invalid("source tool payload binding is broken"));
}
Ok(())
}
pub(super) async fn closed_boundary(
connection: &mut SqliteConnection,
session: &str,
through: u64,
) -> Result<(), StoreError> {
let closed: bool = sqlx::query_scalar(
"SELECT EXISTS(SELECT 1 FROM session_history_events WHERE sequence = ? AND kind IN ('invocation_ended','message_imported')
AND owner_session_id = ?)",
).bind(i64::try_from(through).map_err(|_| invalid("coverage overflow"))?).bind(session).fetch_one(connection).await?;
if !closed {
return Err(invalid("coverage must end at a closed Session boundary"));
}
Ok(())
}
/// A live opening is allowed; covered effects must be settled.
pub(crate) async fn settled_boundary(
connection: &mut SqliteConnection,
invocation: &str,
through: u64,
) -> Result<(), StoreError> {
execution_boundary(connection, invocation, through, Boundary::Settled).await
}
/// Main-model control may observe independent cells without summarizing away
/// their unresolved effects. Direct calls and unfinished model steps still block.
pub(crate) async fn model_boundary(
connection: &mut SqliteConnection,
invocation: &str,
through: u64,
) -> Result<(), StoreError> {
execution_boundary(connection, invocation, through, Boundary::Model).await
}
enum Boundary {
Settled,
Model,
}
// Parameters 1/2/4 are invocation, upper cut, and whether independent work is allowed.
const INDEPENDENT_CELLS: &str = "WITH RECURSIVE independent(operation_id) AS (
SELECT operation_id FROM runtime_events WHERE ?4 AND invocation_id=?1 AND sequence<=?2
AND kind='tool_dispatched' AND json_extract(event_json,'$.fact.call.origin.kind')='code_cell'
UNION ALL
SELECT child.operation_id FROM runtime_events child JOIN independent parent
ON json_extract(child.event_json,'$.fact.call.origin.parent_operation_id')=parent.operation_id
WHERE child.invocation_id=?1 AND child.sequence<=?2
AND child.kind IN ('tool_dispatched','tool_rejected')
)";
// Only admitted Summary requests without tool observations are private work.
// Recovery still sees them; foreground readiness may ignore them. Parameters
// 1 and 2 are invocation identity and the historical upper cut.
pub(crate) const PRIVATE_SUMMARIES: &str = "private_summaries(operation_id) AS (
SELECT r.operation_id FROM runtime_events r WHERE r.invocation_id=?1 AND r.sequence<=?2
AND r.kind='model_requested' AND json_extract(r.event_json,'$.fact.purpose')='summary'
AND NOT EXISTS(SELECT 1 FROM runtime_events o WHERE o.invocation_id=r.invocation_id
AND json_extract(o.event_json,'$.fact.step_id')=r.operation_id AND o.kind='model_observed' AND o.sequence<=?2
AND json_extract(o.event_json,'$.fact.event.kind') IN ('tool_call','provider_tool_result'))
)";
/// Ordinary Message requests retain their existing trace-admission contract,
/// but may overlap only private Summary work, never observed summary tools.
pub(super) async fn summary_boundary(
connection: &mut SqliteConnection,
invocation: &str,
) -> Result<(), StoreError> {
let unsafe_summary: bool = sqlx::query_scalar(sqlx::AssertSqlSafe(format!(
"WITH {PRIVATE_SUMMARIES} SELECT EXISTS(SELECT 1 FROM runtime_events r
WHERE r.invocation_id=?1 AND r.kind='model_requested'
AND json_extract(r.event_json,'$.fact.purpose')='summary'
AND r.operation_id NOT IN (SELECT operation_id FROM private_summaries))"
)))
.bind(invocation)
.bind(i64::MAX)
.fetch_one(connection)
.await?;
if unsafe_summary {
return Err(invalid("summary contains tool observations"));
}
Ok(())
}
/// The prompt's immutable cut may lag only asynchronous cell events, not a new
/// model step, direct tool result, checkpoint, or other selected history.
pub(crate) async fn model_source_unchanged(
connection: &mut SqliteConnection,
selection: &Selection,
request: &RuntimeEvent,
) -> Result<(), StoreError> {
let Fact::ModelRequested {
source_high_water: through,
..
} = &request.fact
else {
return Err(invalid("expected model request"));
};
let invocation = &request.invocation.invocation_id;
let filter = Selection::predicate("e", "?6");
let changed: bool = sqlx::query_scalar(sqlx::AssertSqlSafe(format!(
"{INDEPENDENT_CELLS}, {PRIVATE_SUMMARIES}, retryable AS (
SELECT r.operation_id FROM runtime_events r WHERE r.invocation_id=?1 AND r.kind='model_requested' AND r.sequence>?3
AND json_remove(json_extract(r.event_json,'$.fact'),'$.step_id')=json_remove(json(?7),'$.step_id')
AND EXISTS(SELECT 1 FROM runtime_events t WHERE t.invocation_id=r.invocation_id AND t.operation_id=r.operation_id
AND t.kind='model_interrupted' AND json_extract(t.event_json,'$.fact.status')='retryable_failure')
AND NOT (COALESCE(json_extract(r.event_json,'$.fact.item_acceptance'),0)=1 AND EXISTS(
SELECT 1 FROM runtime_events accepted WHERE accepted.invocation_id=r.invocation_id
AND accepted.kind='model_observed' AND json_extract(accepted.event_json,'$.fact.step_id')=r.operation_id
AND json_extract(accepted.event_json,'$.fact.event.kind') IN ('source','part_finished','tool_call','provider_tool_result')))
AND NOT EXISTS(SELECT 1 FROM runtime_events barrier WHERE barrier.invocation_id=r.invocation_id
AND barrier.kind='model_observed' AND json_extract(barrier.event_json,'$.fact.step_id')=r.operation_id
AND (json_extract(barrier.event_json,'$.fact.event.kind') IN ('provider_tool_result','finished')
OR (json_extract(barrier.event_json,'$.fact.event.kind')='tool_call'
AND json_extract(barrier.event_json,'$.fact.event.data.provider_executed')=1)
OR (json_extract(barrier.event_json,'$.fact.event.data.provider_options') IS NOT NULL
AND json_extract(barrier.event_json,'$.fact.event.data.provider_options') != '{{}}'))))
SELECT EXISTS(SELECT 1 FROM runtime_events e WHERE e.sequence > ?3
AND (e.event_json IS NOT NULL AND e.event_session=?5) AND {filter}
AND NOT (e.invocation_id=?1 AND e.kind IN ('tool_dispatched','tool_settled','tool_rejected')
AND e.operation_id IN (SELECT operation_id FROM independent))
AND NOT (e.invocation_id=?1 AND e.kind='tool_notified'
AND json_extract(e.event_json,'$.fact.operation_id') IN (SELECT operation_id FROM independent))
AND NOT (e.invocation_id=?1 AND e.kind IN ('model_requested','model_observed','model_interrupted')
AND json_extract(e.event_json,'$.fact.step_id') IN (SELECT operation_id FROM retryable))
AND NOT (e.invocation_id=?1 AND e.kind IN ('model_requested','model_observed','model_completed','model_interrupted')
AND json_extract(e.event_json,'$.fact.step_id') IN (SELECT operation_id FROM private_summaries)))"
)))
.bind(invocation)
.bind(i64::MAX)
.bind(*through as i64)
.bind(true)
.bind(&selection.session)
.bind(&selection.lineage)
.bind(serde_json::to_string(&request.fact)?)
.fetch_one(connection)
.await?;
if changed {
return Err(invalid("model request source changed"));
}
Ok(())
}
async fn execution_boundary(
connection: &mut SqliteConnection,
invocation: &str,
through: u64,
boundary: Boundary,
) -> Result<(), StoreError> {
let accepted_calls = crate::model_items::accepted_calls!();
let pending: bool = sqlx::query_scalar(sqlx::AssertSqlSafe(format!(
"{INDEPENDENT_CELLS}, {PRIVATE_SUMMARIES}, {accepted_calls}
SELECT EXISTS(SELECT 1 FROM runtime_events e WHERE e.invocation_id = ?1 AND e.sequence <= ?2 AND (
(e.kind = 'tool_dispatched' AND NOT EXISTS(SELECT 1 FROM runtime_events t
WHERE t.invocation_id = e.invocation_id AND t.operation_id = e.operation_id AND t.kind = 'tool_settled' AND t.sequence <= ?2)
AND e.operation_id NOT IN (SELECT operation_id FROM independent))
OR (e.kind = 'model_requested'
AND NOT (?4 AND e.operation_id IN (SELECT operation_id FROM private_summaries))
AND NOT EXISTS(SELECT 1 FROM runtime_events t
WHERE t.invocation_id = e.invocation_id AND t.operation_id = e.operation_id AND t.kind IN ('model_completed','model_interrupted') AND t.sequence <= ?2))
OR (e.kind = 'model_requested' AND EXISTS(SELECT 1 FROM accepted_calls p
WHERE p.invocation_id=e.invocation_id AND p.step_id=e.operation_id AND p.sequence <= ?2
AND json_extract(p.call, '$.provider_executed') = 0
AND NOT EXISTS(SELECT 1 FROM runtime_events d WHERE d.invocation_id = e.invocation_id
AND d.operation_id = e.operation_id || ':' || json_extract(p.call, '$.id')
AND d.kind IN ('tool_dispatched','tool_rejected') AND d.sequence <= ?2)))
OR (e.kind = 'model_interrupted'
AND (json_extract(e.event_json, '$.fact.status') != 'retryable_failure'
OR (NOT EXISTS(SELECT 1 FROM runtime_events request WHERE request.invocation_id=e.invocation_id
AND request.operation_id=e.operation_id AND request.kind='model_requested'
AND json_extract(request.event_json,'$.fact.item_acceptance')=1)
AND EXISTS(SELECT 1 FROM runtime_events barrier WHERE barrier.invocation_id=e.invocation_id
AND json_extract(barrier.event_json, '$.fact.step_id')=e.operation_id
AND barrier.kind='model_observed' AND barrier.sequence <= ?2
AND (json_extract(barrier.event_json, '$.fact.event.kind') IN ('provider_tool_result','finished')
OR (json_extract(barrier.event_json, '$.fact.event.kind')='tool_call'
AND json_extract(barrier.event_json, '$.fact.event.data.provider_executed')=1)
OR (json_extract(barrier.event_json, '$.fact.event.data.provider_options') IS NOT NULL
AND json_extract(barrier.event_json, '$.fact.event.data.provider_options') != '{{}}')))))
AND NOT ((?3 OR ?4) AND e.operation_id IN (SELECT operation_id FROM private_summaries))
AND EXISTS(SELECT 1 FROM runtime_events o
WHERE o.invocation_id = e.invocation_id AND json_extract(o.event_json, '$.fact.step_id') = e.operation_id
AND o.kind = 'model_observed' AND o.sequence <= ?2))
OR (e.kind='model_requested' AND json_extract(e.event_json,'$.fact.item_acceptance')=1
AND EXISTS(SELECT 1 FROM accepted_calls p WHERE p.invocation_id=e.invocation_id AND p.step_id=e.operation_id
AND p.sequence <= ?2 AND json_extract(p.call,'$.provider_executed')=1
AND NOT EXISTS(SELECT 1 FROM runtime_events result WHERE result.invocation_id=e.invocation_id
AND result.kind='model_observed' AND result.sequence <= ?2
AND json_extract(result.event_json,'$.fact.step_id')=e.operation_id
AND json_extract(result.event_json,'$.fact.event.kind')='provider_tool_result'
AND json_extract(result.event_json,'$.fact.event.data.id')=json_extract(p.call,'$.id')
AND json_extract(result.event_json,'$.fact.event.data.name')=json_extract(p.call,'$.name'))))
OR (e.kind = 'model_completed' AND EXISTS(SELECT 1 FROM json_each(e.event_json, '$.fact.output.parts') p
WHERE json_extract(p.value, '$.kind') = 'tool_call' AND json_extract(p.value, '$.call.provider_executed') = 1
AND NOT EXISTS(SELECT 1 FROM json_each(e.event_json, '$.fact.output.parts') result
WHERE json_extract(result.value, '$.kind') = 'tool_result'
AND json_extract(result.value, '$.id') = json_extract(p.value, '$.call.id')
AND json_extract(result.value, '$.name') = json_extract(p.value, '$.call.name'))))))",
))).bind(invocation).bind(through as i64).bind(matches!(boundary,Boundary::Settled))
.bind(matches!(boundary,Boundary::Model)).fetch_one(connection).await?;
if pending {
return Err(invalid(
"active source contains unresolved model or tool work",
));
}
Ok(())
}