feat: resume時のscope claimを過去の有効scopeに揃える

This commit is contained in:
2026-05-03 17:12:36 +09:00
parent 81ff4c6073
commit 4e48f35e55
16 changed files with 465 additions and 52 deletions
+4 -3
View File
@@ -41,11 +41,12 @@ pub use logged_item::{LoggedContentPart, LoggedItem, LoggedRole, from_logged, to
pub use session::{
SessionStartState, create_compacted_session, create_session, create_session_with_id,
ensure_head_or_fork, fork, fork_at, restore, save_config_changed, save_delta, save_extension,
save_run_completed, save_run_errored, save_turn_end, save_usage, save_user_input,
save_pod_scope, save_run_completed, save_run_errored, save_turn_end, save_usage,
save_user_input,
};
pub use session_log::{
EntryHash, HashedEntry, LogEntry, RestoredState, SessionOrigin, build_chain, collect_state,
compute_hash,
EntryHash, HashedEntry, LogEntry, POD_SCOPE_EXTENSION_DOMAIN, PodScopeSnapshot, RestoredState,
SessionOrigin, build_chain, collect_state, compute_hash,
};
pub use store::{Store, StoreError};
+19 -1
View File
@@ -6,7 +6,7 @@
use crate::SessionId;
use crate::logged_item::{LoggedItem, to_logged};
use crate::session_log::{self, EntryHash, HashedEntry, LogEntry, SessionOrigin};
use crate::session_log::{self, EntryHash, HashedEntry, LogEntry, PodScopeSnapshot, SessionOrigin};
use crate::store::{Store, StoreError};
use llm_worker::WorkerResult;
use llm_worker::llm_client::RequestConfig;
@@ -360,6 +360,24 @@ pub async fn save_extension(
.await
}
/// Log the Pod's latest runtime scope snapshot.
pub async fn save_pod_scope(
store: &impl Store,
session_id: SessionId,
head_hash: &mut Option<EntryHash>,
snapshot: &PodScopeSnapshot,
) -> Result<(), StoreError> {
let payload = serde_json::to_value(snapshot)?;
save_extension(
store,
session_id,
head_hash,
session_log::POD_SCOPE_EXTENSION_DOMAIN,
payload,
)
.await
}
/// Log a `ConfigChanged` entry.
pub async fn save_config_changed(
store: &impl Store,
+18 -1
View File
@@ -10,7 +10,7 @@
use llm_worker::llm_client::types::{Item, RequestConfig};
use llm_worker::{UsageRecord, WorkerResult};
use protocol::Segment;
use protocol::{ScopeRule, Segment};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
@@ -197,6 +197,16 @@ pub struct SessionOrigin {
pub at_hash: EntryHash,
}
/// Domain used by Pod to persist its latest effective runtime scope.
pub const POD_SCOPE_EXTENSION_DOMAIN: &str = "pod.scope";
/// Payload stored in `LogEntry::Extension { domain: "pod.scope", .. }`.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PodScopeSnapshot {
pub allow: Vec<ScopeRule>,
pub deny: Vec<ScopeRule>,
}
/// State collected from log entries.
#[derive(Debug, Clone)]
pub struct RestoredState {
@@ -214,6 +224,9 @@ pub struct RestoredState {
/// `LogEntry::Extension` を replay 順に積んだもの。`(domain, payload)`。
/// session-store は domain を不透明扱いし、各ドメインが自前で fold する。
pub extensions: Vec<(String, serde_json::Value)>,
/// Latest runtime scope snapshot persisted by the Pod. `None` means
/// the session predates scope persistence or the payload was corrupt.
pub pod_scope: Option<PodScopeSnapshot>,
/// User submissions in original typed form, in submit order.
/// One entry per `LogEntry::UserInput`; the K-th entry corresponds to
/// the K-th `Item::user_message` derived during replay (modulo
@@ -234,6 +247,7 @@ pub fn collect_state(entries: &[HashedEntry]) -> RestoredState {
head_hash: None,
usage_history: Vec::new(),
extensions: Vec::new(),
pod_scope: None,
user_segments: Vec::new(),
};
@@ -296,6 +310,9 @@ pub fn collect_state(entries: &[HashedEntry]) -> RestoredState {
LogEntry::Extension {
domain, payload, ..
} => {
if domain == POD_SCOPE_EXTENSION_DOMAIN {
state.pod_scope = serde_json::from_value(payload.clone()).ok();
}
state.extensions.push((domain.clone(), payload.clone()));
}
}